UNIX 环境高级编程读书笔记(1)
在Liunx系統編程中,經常會出現或多或少的困惑,雖然基本大部分會用,但是可能感覺還是因為對于一些概念性的東西了解的不夠深入,因此重新讀一遍APUE,從頭梳理一遍知識點。這篇總結博客是大概暑假的時候就準備看書總結了,但是這段時間因為個人的一些原因導致狀態很差,最近感覺調整的好一些了,因此決定用博客來記錄自己的讀書筆記,一是為了熟練的使用,自己能動手敲鞏固一遍的話效果可能更好,二是為了明年的實習(假如導師放實習的話- -。
那就直接從第三章文件I/O開始了。
如果存在問題的話,歡迎指出!
1.文件I/O
1.1 引言
對于大多數文件I/O來講,只需要用到5個函數:open、read、write、lseek、close。
與我們平時在C/C++中所用的標準I/O不同,本章所描述的函數被稱為不帶緩沖的I/O。同時本章還會說明dup、fcntl、sync、fsync、isctl函數。
1.2 文件描述符
首先介紹文件描述符,在UNIX系統中,所有打開的文件都通過文件描述符引用,文件描述符是一個非負整數,這與我們通常在Windows系統下編寫C/C++程序有所不同,平時我們對于一個描述進行操作時,通常是對一個FILE*類型的結構體進行操作,但是在UNIX系統中,我們通過文件描述符來對文件進行描述和操作。
UNIX系統會將文件描述符為0的文件關聯標準輸入(STDIN_FILENO),文件描述符為1的文件關聯標準輸出(STDOUT_FILENO),文件描述符為2的文件關聯標準錯誤輸出(STDERR_FILENO),對應的宏定義都在頭文件<unistd.h>中定義。
如果想要查看當前進程所能打開的最大數量文件個數,可以通過命令ulimit -a來查看。
1.3 函數open和openat
path:打開或創建文件的名字
flag:可以用來說明我們打開文件的方式。
| O_RDONLY | 只讀打開 |
| O_WRONLY | 只寫打開 |
| O_RDWR | 讀寫打開 |
| O_EXEC | 只執行打開 |
| O_SEARCH | 只搜索打開 |
| O_APPEND | 追加尾端寫入 |
| O_CLOEXEC | 把FD_CLOEXEC常量設置為文件描述符標志 |
| O_CREAT | 創建文件并通過mode參數設定文件權限 |
| O_DIRECTORY | 如果path引用不是目錄就報錯 |
| O_EXCL | 可以搭配O_CREAT,如果文件存在就報錯,這可成為一個原子操作 |
| O_SYNC | 每次write等待物理I/O操作完成 |
| O_TRUNC | 寫文件時將文件清空 |
mode_t:代表的是變參,這也是在C中為了實現類似于C++中的重載函數的功能,當我們需要創建文件的時候,就需要通過這個參數來指定文件權限。在這里對于常見的普通文件和目錄文件的權限進行說明:讀寫操作就不贅述了,對于普通文件而言,如果它具備執行權限,那就是可執行文件;對于目錄文件,如果存在執行權限,那就代表可以打開,否則不能打開目錄(chmod命令)。
通過open和openat函數返回的文件描述符一定是當前進程最小的未用描述符數值,比如在上文中我們介紹,文件描述符012已經被默認關聯,也就是說,此時如果我們在當前進程打開一個新文件,那么這個新文件對應的文件描述符的值為3.
相較于open函數,openat函數旨在解決兩個問題,首先是通過相對路徑名打開目錄中的文件(應該是因為openat是一個原子操作所以可以避免第二個問題中所描述的錯誤),第二可以避免time-of-check-to-time-of-use(TOCTTOU)錯誤。
1.4 函數creat
creat函數的原型在1.3中圖片中有給出。
此函數等效于open(path,O_WRONLY | O_CREAT | O_TRUNC, mode);
1.5 函數close
關閉一個打開文件,并且還會釋放進程加載該文件上的所有記錄所,同樣的當當前進程結束時也會自動用close關閉打開文件。
1.6 函數lseek
文件偏移量是一個非負整數,通常讀寫操作都是從當前文件偏移量處開始的,一般打開一個文件的時候除非指定flag為O_APPEND,否則一般都為0也就是文件開始處。
功能:
改變文件的偏移量
參數:
fd:文件描述符
offset:根據whence來移動的位移數(偏移量),可以是正數,也可以負數,如果正數,則相對于whence往右移動,如果是負數,則相對于whence往左移動。如果向前移動的字節數超過了文件開頭則出錯返回,如果向后移動的字節數超過了文件末尾,再次寫入時將增大文件尺寸。
whence:其取值如下:
SEEK_SET:從文件開頭移動offset個字節
SEEK_CUR:從當前位置移動offset個字節
SEEK_END:從文件末尾移動offset個字節
返回值:
若lseek成功執行, 則返回新的偏移量
如果失敗, 返回-1
文件偏移量可以大于文件的當前長度,在這種情況下,對該文件的下一次寫將加長該文件,并在文件中構成空洞,文件中的空洞并不要求在磁盤上占用存儲區。
因為lseek使用的偏移量是用off_t類型表示的,所以允許具體實現根據特點平臺自行選擇合適大小的數據類型。
3.7 函數read
功能:
把指定數目的數據讀到內存(緩沖區)
參數:
fd : 文件描述符
buf : 內存首地址
count : 讀取的字節個數
返回值:
成功:實際讀取到的字節個數
失敗: - 1
有以下多種情況可能導致實際獨到的字節數少于要求讀的字節數:
1.讀普通文件時,在讀到要求字節數之前到達了文件尾端,返回實際字節數。
2.讀終端設備,以此最多讀一行
3.讀網絡,網絡的緩沖機制可能造成返回值小于所要求的字節數
4.從管道讀,管道字節小于要求的字節數
5.信號造成中斷,此時已經讀取了部分數據的時候。
3.8 函數write
功能:
把指定數目的數據寫到文件(fd)
參數:
fd : 文件描述符
buf : 數據首地址
count : 寫入數據的長度(字節)
返回值:
成功:實際寫入數據的字節個數
失敗: - 1
3.9 I/O效率
大多數文件系統為改善性能都采用某種預讀技術。當檢測到正進行順序讀取時,系統就駛入讀入比應用所要求的更多數據,并假想應用很快就會讀這些數據。(類似的概念有很多例如cache感覺可以聯想理解一下就知道這樣做的原因了。)
3.10 文件共享
UNIX支持在不同進程之間共享打開文件。
內核使用3種數據結構表示打開文件:
1.每個進程在進程表中都有一個記錄項,記錄項中包含一張打開文件描述符,每個描述符占用一項。與每個文件描述符相關聯的是文件描述符標志和指向一個文件表項的指針。
2.內核為所有打開文件維持一張文件表,每個文件表項包含文件狀態標志,當前文件偏移量以及指向該文件v節點表項的指針。
3.每個打開文件都有一個v節點結構。v節點包含了文件類型和對此文件進行各種操作函數的指針,對于大多數文件,v節點還包含了該文件的inode。
下圖是一個進程打開兩個文件描述符:
兩個獨立進程打開了同一個文件如下所示:
也就是說,即使對于不同進程而言,他們對于相同的文件有著不同的認知(不同進程中偏移量和文件狀態等都不同),但是由于都是相同的v節點,因此指向的還是同一文件。
通過上圖對上述的操作進行總結:
1.在完成每個write后,在文件表項中的當前文件偏移量即增加所寫入的字節數,如果當前文件偏移量超出了當前文件長度則將i節點表項中的當前文件長度設置為當前文件偏移量。
2.使用O_APPEND標志打開文件,文件表項中的當前文件偏移量會首先設置為i節點表項中的文件長度。
3.若一個文件使用lseek定位到文件尾端,則文件偏移量會設置為i節點表項的當前文件長度。
4.lseek函數只修改文件表項中的當前文件偏移量不進行任何I/O操作。
3.11 原子操作
原子操作:由多步組成的一個操作,如果該原子操作執行,那么要么執行完全部,要不就不執行。
3.12 函數dup和dup2
int dup(int oldfd);
功能:
通過 oldfd 復制出一個新的文件描述符,新的文件描述符是調用進程文件描述符表中最小可用的文件描述符,最終 oldfd 和新的文件描述符都指向同一個文件。
參數:
oldfd : 需要復制的文件描述符 oldfd
返回值:
成功:新文件描述符
失敗: -1
int dup2(int oldfd, int newfd);
功能:
通過 oldfd 復制出一個新的文件描述符 newfd,如果成功,newfd 和函數返回值是同一個返回值,最終 oldfd 和新的文件描述符 newfd 都指向同一個文件。
參數:
oldfd : 需要復制的文件描述符
newfd : 新的文件描述符,這個描述符可以人為指定一個合法數字(0 - 1023),如果指定的數字已經被占用(和某個文件有關聯),此函數會自動關閉 close() 斷開這個數字和某個文件的關聯,再來使用這個合法數字。
返回值:
成功:返回 newfd
失敗:返回 -1
3.13 函數sync、fsync和fdatasync
內核通過重用緩沖區來存放其他磁盤塊數據時,它會把所有延遲寫數據塊寫入磁盤。通過sync、fsync和fdatasync函數來保證實際文件系統與緩沖區內容的一致性。
sync將所有修改過的塊緩沖區排入寫隊列然后返回,并不等待實際寫磁盤結束。
fsync函數只對由文件描述符指定的一個文件起作用并等待寫磁盤操作結束菜返回。
fdatasync函數類似于fsync,但它只影響文件的數據部分。
3.14 函數fcntl
改變已經打開文件的屬性
fd:指定要改變屬性的文件
cmd:
1.復制一個已有的描述符(cmd=F_DUPFD或F_DUPFD_CLOEXEC)
2.獲取/設置文件描述符標志(cmd=F_GETFD或F_SETFD)
3.獲取/設置文件狀態標志(cmd=F_GETEF或F_SETLFL)
4.獲取/設置異步I/O所有權(cmd=F_GETOWN或F_SETWON)
5.獲取/設置記錄鎖(cmd=F_GETLK\F_SETLK或F_SETLKW)
3.16 /dev/fd
打開文件dev/fd/n等效于復制描述符n。
總結
以上是生活随笔為你收集整理的UNIX 环境高级编程读书笔记(1)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python安装失败0x80070005
- 下一篇: windows 进程通信(使用DDE)