本章描述的函數被成爲不帶緩衝的IO,涉及5個函數:open、read、write、lseek、close。html
文件控制:dup、sync、fsync、fdatasync、fcntl、ioctl。node
文件描述符爲非負整數,取值範圍爲0 ~ OPEN_MAX - 1,調用open、create返回參數路徑的文件描述符,能夠做爲傳遞給read、write。linux
可以使用shell命令查詢系統對OPEN_MAX定義,grep -rn --col OPEN_MAX /usr/include
shell
linux三個特殊的文件描述符:數據庫
0:標準讀,STDIN_FILENO,該常亮在unistd.h中定義數據結構
1:標準寫,STDOUT_FILENOapp
2:標準錯誤,STDERR_FILENO異步
該方法能夠打開或者建立一個文件,頭文件 fctnl.h。成功返回文件描述符,失敗返回-1。socket
1 /* Open FILE and return a new file descriptor for it, or -1 on error. 2 OFLAG determines the type of access used. If O_CREAT or O_TMPFILE is set 3 in OFLAG, the third argument is taken as a `mode_t', the mode of the 4 created file. 5 6 This function is a cancellation point and therefore not marked with 7 __THROW. */ 8 #ifndef __USE_FILE_OFFSET64 9 extern int open (const char *__file, int __oflag, ...) __nonnull ((1)); 10 #else 11 # ifdef __REDIRECT 12 extern int __REDIRECT (open, (const char *__file, int __oflag, ...), open64) 13 __nonnull ((1)); 14 # else 15 # define open open64 16 # endif 17 #endif 18 19 #ifdef __USE_ATFILE 20 /* Similar to `open' but a relative path name is interpreted relative to 21 the directory for which FD is a descriptor. 22 23 NOTE: some other `openat' implementation support additional functionality 24 through this interface, especially using the O_XATTR flag. This is not 25 yet supported here. 26 27 This function is a cancellation point and therefore not marked with 28 __THROW. */ 29 # ifndef __USE_FILE_OFFSET64 30 extern int openat (int __fd, const char *__file, int __oflag, ...) 31 __nonnull ((2)); 32 # else 33 # ifdef __REDIRECT 34 extern int __REDIRECT (openat, (int __fd, const char *__file, int __oflag, 35 ...), openat64) __nonnull ((2)); 36 # else 37 # define openat openat64 38 # endif 39 # endif 40 # ifdef __USE_LARGEFILE64 41 extern int openat64 (int __fd, const char *__file, int __oflag, ...) 42 __nonnull ((2)); 43 # endif 44 #endif
各參數釋義以下:async
path:打開或者建立文件的名字。
oflag:文件狀態標誌,以下:
常量 | 釋義 |
O_RDONLY | 只讀打開 |
O_WRONLY | 只寫打開 |
O_RDWR | 讀寫打開 |
O_EXEC | 只執行打開 |
O_SEARCH | 只搜索打開(僅應用於目錄) |
O_APPEND | 寫文件時,追加到文件末尾 |
O_CLOEXEC | 把FD_CLOEXEC設置爲文件描述符標誌 |
O_CREATE | 建立文件,參數mode用於指定訪問權限 |
O_DIRECTORY | 若是path不是目錄,則出錯 |
O_EXCL | 測試文件是否存在。不能與O_CREAT公用,不然會報錯 |
O_NOCTTY | 若是path是終端設備,則不降該設計分配爲此進場的控制終端 |
O_NOFOLLOW | 若是path是符號連接,則出錯 |
O_NONBLOCK | 若是path時FIFO、塊特殊文件、字符特殊文件,則將本次打開操做及後續IO操做設置爲非阻塞 |
O_SYNC | 每次write等待IO操做完成 |
O_TRUNK | 文件存在,且爲寫打開(包括只寫、讀寫),則將文件長度截斷爲0。即寫指針移位到0 |
O_TTY_INIT | |
O_DSYNC | write等待IO操做完成,若是寫操做不影響讀取剛寫入的數據,則不須要等待文件屬性被更新 |
O_RSYNC | read操做等待,直至全部對文件同一部分掛起的寫操做都完成 |
openat與open區別以下:
建立一個新文件,頭文件fcntl.h。成功返回文件描述符,失敗返回-1
1 /* Create and open FILE, with mode MODE. This takes an `int' MODE 2 argument because that is what `mode_t' will be widened to. 3 4 This function is a cancellation point and therefore not marked with 5 __THROW. */ 6 #ifndef __USE_FILE_OFFSET64 7 extern int creat (const char *__file, mode_t __mode) __nonnull ((1)); 8 #else 9 # ifdef __REDIRECT 10 extern int __REDIRECT (creat, (const char *__file, mode_t __mode), 11 creat64) __nonnull ((1)); 12 # else 13 # define creat creat64 14 # endif 15 #endif 16 #ifdef __USE_LARGEFILE64 17 extern int creat64 (const char *__file, mode_t __mode) __nonnull ((1)); 18 #endif
等價於open(path, O_WRONLY | O_CREAT | O_TRUNK, mode)
關閉一個打開的文件,頭文件 unistd.h。成功返回0,失敗返回-1
1 /* Close the file descriptor FD. 2 This function is a cancellation point and therefore not marked with 3 __THROW. */ 4 5 extern int close (int __fd);
關閉一個文件會釋放該進程加在文件上的因此記錄鎖。
當一個進場終止時,內核會自動關閉它打開的全部文件,不少程序利用這一功能而不顯示調用close關閉文件。
操做文件偏移量,頭文件 unistd.h。成功返回文件偏移量,失敗返回-1
1 /* Move FD's file position to OFFSET bytes from the 2 beginning of the file (if WHENCE is SEEK_SET), 3 the current position (if WHENCE is SEEK_CUR), 4 or the end of the file (if WHENCE is SEEK_END). 5 Return the new file position. */ 6 #ifndef __USE_FILE_OFFSET64 7 extern __off_t lseek (int __fd, __off_t __offset, int __whence) __THROW; 8 #else 9 # ifdef __REDIRECT_NTH 10 extern __off64_t __REDIRECT_NTH (lseek, 11 (int __fd, __off64_t __offset, int __whence), 12 lseek64); 13 # else 14 # define lseek lseek64 15 # endif 16 #endif
參數whence釋義以下:
若是文件描述符是FIFO、pipe、socket,則返回-1,errno設置爲ESPIPE。
注意
文件偏移量多是負值,測試返回結果要測試 -1 == lseek( ... )
lseek不引起任何IO操做
文件偏移量能夠大於文件當前長度,可是文件中沒有寫過的字節都會被設置爲0。這部分文件數據被稱爲文件空洞
從打開的文件中讀數據,頭文件 unistd.h。返回讀到的字節數,0表示文件末尾EOF,-1表示出錯
1 /* Read NBYTES into BUF from FD. Return the 2 number read, -1 for errors or 0 for EOF. 3 4 This function is a cancellation point and therefore not marked with 5 __THROW. */ 6 extern ssize_t read (int __fd, void *__buf, size_t __nbytes) __wur;
向打開的文件中寫數據,頭文件 unistd.h。返回值應當於寫入的數據長度相同,不然就表示出錯。
1 /* Write N bytes of BUF to FD. Return the number written, or -1. 2 3 This function is a cancellation point and therefore not marked with 4 __THROW. */ 5 extern ssize_t write (int __fd, const void *__buf, size_t __n) __wur;
write出錯的常見緣由是磁盤滿,或者超過一個給定進程的文件長度限制。
.
內核使用三種數據結構表示打開文件,它們之間的關係決定文件共享方面一個進程對另一個進程可能產生的影響。
understanding v-node(virtual node) : https://www.ibm.com/support/knowledgecenter/en/ssw_aix_72/kernelextension/virtual_nodes.html
understanding i-noe : https://www.ibm.com/support/knowledgecenter/en/ssw_aix_72/kernelextension/generic_inodes.html
圖3-1表示進程打開了2個不一樣類型的文件,標準輸入0,標準輸出1。
若是不一樣進程打開同一個文件,則有下圖所示關係。
如下兩種操做並不等價
1 //1: lseek + write 2 lseek(fd, 0 ,SEEK_END); 3 write (fd, buf, bufsize); 4 5 //2: APPEND + write 6 open (path, O_WRONLY | O_APPEND); 7 write (fd, buf, bufsize);
第一種操做在每次write以前調用lseek將文件偏移量設置到文件末尾,再執行寫操做。使用2個函數將沒法保證操做的原子性。
第二種操做使用APPEND,內核在每次寫操做以前將偏移量設置到該文件末尾,原子性由操做系統保證。
open函數的O_CREAT(建立)和O_EXCL(檢測文件是否存在)互斥,都是原子操做。
用於複製一個現有的文件描述符,頭文件 unistd.h。成功返回新的文件描述符,失敗返回-1
1 /* Duplicate FD, returning a new file descriptor on the same file. */ 2 extern int dup (int __fd) __THROW __wur; 3 4 /* Duplicate FD to FD2, closing FD2 and making it open on the same file. */ 5 extern int dup2 (int __fd, int __fd2) __THROW;
由dup返回的新文件描述符必定是當前可用文件描述符的最小數值。對於dup2,若是fd2已經打開,則先將其關閉。若是fd等於fd2,則直接返回fd2。
當linux向文件寫入數據時,內核一般先將數據複製到緩衝區,再排入隊列,晚些時候再寫入磁盤,這種方式被稱爲延遲寫。爲保證磁盤上實際文件與緩衝區一致,UNIX提供了sync、fsync、fdatasync三個函數。
頭文件 unistd.h。成功返回0,失敗返回-1。
1 /* Make all changes done to all files actually appear on disk. */ 2 extern void sync (void) __THROW; 3 4 /* Make all changes done to FD actually appear on disk. 5 This function is a cancellation point and therefore not marked with __THROW. */ 6 extern int fsync (int __fd); 7 8 /* Synchronize at least the data part of a file with the underlying media. */ 9 extern int fdatasync (int __fildes);
區別:
能夠改變打開的文件屬性,頭文件fcntl.h。成功,依賴cmd返回。失敗,返回-1。
1 /* Do the file control operation described by CMD on FD. 2 The remaining arguments are interpreted depending on CMD. 3 4 This function is a cancellation point and therefore not marked with 5 __THROW. */ 6 extern int fcntl (int __fd, int __cmd, ...);
fcntl函數有如下5個功能:
控制IO操做的函數,第三章其他函數功能的補集。頭文件unistd.h和sys/ioctl.h。
1 /* Perform the I/O control operation specified by REQUEST on FD. 2 One argument may follow; its presence and type depend on REQUEST. 3 Return value depends on REQUEST. Usually -1 indicates error. */ 4 extern int ioctl (int __fd, unsigned long int __request, ...) __THROW;
打開文件 /dev/fd/n 等同於複製描述符 n。
例如:fd = open("/dev/fd/0「, mode)等價於 fd = dup(0)。
/dev/stdin == /dev/fd/0
/dev/stdout == /dev/fd/1
/dev/stderr == /dev/fd/2
以下圖,利用cat顯示,輸入爲/dev/stdin ,輸出到/dev/stdout