C3 文件IO:APUE 筆記

C3:文件IO

1 引言

  本章描述的函數被成爲不帶緩衝的IO,涉及5個函數:open、read、write、lseek、close。html

  文件控制:dup、sync、fsync、fdatasync、fcntl、ioctl。node

2 文件描述符

  文件描述符爲非負整數,取值範圍爲0 ~ OPEN_MAX - 1,調用open、create返回參數路徑的文件描述符,能夠做爲傳遞給read、write。linux

  可以使用shell命令查詢系統對OPEN_MAX定義,grep -rn --col OPEN_MAX /usr/includeshell

  linux三個特殊的文件描述符:數據庫

  0:標準讀,STDIN_FILENO,該常亮在unistd.h中定義數據結構

  1:標準寫,STDOUT_FILENOapp

  2:標準錯誤,STDERR_FILENO異步

3 函數open與openat

  該方法能夠打開或者建立一個文件,頭文件 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區別以下:

  •   若是openat的path是絕對文件名,則fd參數被忽略,openat等同於open
  •   path參數是相對文件名,fd指定了相對路徑名的文件描述符,fd參數經過打開相對路徑名所在的目錄獲取文件。
  •   path指定相對路徑名,fd參數具備特殊值AT_FDCWD,則路徑名錶示爲當前目錄獲取,在操做上與open函數相似。

4 函數creat

  建立一個新文件,頭文件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)

5 函數close

  關閉一個打開的文件,頭文件 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關閉文件。

6 函數lseek

  操做文件偏移量,頭文件 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釋義以下:

  •   取值爲SEEK_SET,則將文件偏移量設置爲距離文件開始處offset個字節
  •   取值爲SEECK_CUR,則將文件偏移量設置爲當前值加上offset,offset可正可負
  •   取值爲SEEK_END,則將文件偏移量設置爲文件長度加上offset,offset可正可負

   若是文件描述符是FIFO、pipe、socket,則返回-1,errno設置爲ESPIPE。

  注意

  文件偏移量多是負值,測試返回結果要測試 -1 == lseek( ... )

  lseek不引起任何IO操做

  文件偏移量能夠大於文件當前長度,可是文件中沒有寫過的字節都會被設置爲0。這部分文件數據被稱爲文件空洞

7 函數read

  從打開的文件中讀數據,頭文件 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;

8 函數write

  向打開的文件中寫數據,頭文件 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出錯的常見緣由是磁盤滿,或者超過一個給定進程的文件長度限制。

9 UNIX的IO數據結構

 .

  內核使用三種數據結構表示打開文件,它們之間的關係決定文件共享方面一個進程對另一個進程可能產生的影響。

  •   進程表項:列表,每項包含一個文件描述符標誌,以及一個指向文件表項的指針
  •   文件表:包含文件狀態標誌、當前文件偏移量、指向文件v節點的指針
  •   v節點:v節點包含文件類型、對此文件進行各類操做函數的指針。v節點還包含文件的i節點,i節點包含文件的全部者、文件長度、文件在磁盤上的指針等

  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。

  若是不一樣進程打開同一個文件,則有下圖所示關係。

10 原子操做

10.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,內核在每次寫操做以前將偏移量設置到該文件末尾,原子性由操做系統保證。

10.2 建立文件

  open函數的O_CREAT(建立)和O_EXCL(檢測文件是否存在)互斥,都是原子操做。

11 函數dup和dup2

  用於複製一個現有的文件描述符,頭文件 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。

 12 函數sync、fsync、fdatasync

  當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);

   區別:

  •   sync只是將全部修改的塊緩衝區排入寫隊列,而後就返回,不等待寫磁盤操做完成。一般,稱爲update的系統守護進程週期性的調用sync函數。
  •   fsync函數只對文件描述符指定的文件起做用,等待磁盤操做完成後返回。可用於數據庫操做
  •   fdatasync隻影響文件的數據部分

13 函數fcntl

  能夠改變打開的文件屬性,頭文件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個功能:

  •   複製一個文件描述符(cmd = F_DUPFD 或者 F_DUPFD_CLOEXEC)。函數dup包含close和fcntl,是原子操做。fcntl非原子操做。
  •   獲取、設置文件描述符標誌 (cmd = F_GETFD 或 F_SETFD)。F_GETFD,當前只定義了一個文件描述符標誌FD_CLOEXEC
  •   獲取、設置文件狀態標誌 (cmd = F_GETFL 或 F_SETFL)。文件狀態標誌爲open函數的flag參數,見第三節
  •   獲取、設置異步IOS全部權 (cmd = F_GETOWN 或 F_SETOWN)
  •   獲取、設置記錄鎖 (cmd = F_GETLK、F_SETLK、F_SETLKW)

 14 函數ioctl

  控制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;

15 /dev/fd

  打開文件 /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

    

相關文章
相關標籤/搜索