《Linux/Unix系統編程手冊》讀書筆記 目錄
第5章:
主要介紹了文件I/O更深入的一些內容。
原子操作,將一個系統調用所要完成的所有動作作為一個不可中斷的操作,一次性執行;這樣可以避免競爭狀態(兩個或多個共享資源的進程或線程的運行結果是一個無法預期的順序)。
以獨占方式創建一個文件:對文件是否存在的檢查和創建文件屬于同一個原子操作。防止新建文件的時候因為檢查文件是否存在和新建文件之間發生中斷(而其他進程也在新建相同文件名的文件),導致兩個進程都認為自己是文件的創建者。
向文件尾部追加數據:將文件的偏移量的移動與數據的寫操作屬于同一個原子操作。防止多個進程同時往同一個文件尾部添加數據導致數據混亂。
fcntl(),對一個打開的文件描述符執行一系列的操作。
1 #include <fcntl.h>2 3 int fcntl(int fd, int cmd, ...);
fd為文件描述符,cmd是決定具體操作,第三個參數(可選)用來設置為不同的類型。
cmd參數(部分),具體查看man手冊:
| F_DUPFD | 復制文件描述符 |
| F_GETFD | 獲取文件描述符 |
| F_SET_FD | 設置文件描述符 |
| F_GETFL | 獲取文件訪問模式和狀態標志 |
| F_SETFL | 設置文件訪問模式和狀態標志 |
文件描述符與打開文件之間的關系:多個文件描述符可以指向同一個打開文件。他們的關系如下

文件描述符表、打開文件表和i-node表。打開文件表的條目成為打開文件句柄(open file handle)。
PS:如果兩個不同的文件描述符指向同一個打開文件句柄,這兩個文件描述符將共享相同的文件偏移量。(打開文件句柄里包含文件偏移量file offset)。
dup(),復制一個打開的文件描述符oldfd,并返回新的描述符。
dup2(),復制oldfd指定的文件描述符,返回newfd參數指定的描述符。
dup3(),參數與dup2()相同,添加了flags,用于修改系統調用行為。
1 #include <unistd.h>2 3 int dup(int oldfd);4 5 int dup2(int oldfd, int newfd);6 7 int dup3(int oldfd, int newfd, int flags);
成功調用返回新的文件描述符,失敗返回-1。
PRead()和pwrite(),在指定參數所指定的位置進行文件I/O操作,但不改變文件的偏移量。
1 #include <unistd.h>2 3 ssize_t pread(int fd, void *buf, size_t count, off_t offset);4 5 ssize_t pwrite(int fd, const void *buf, size_t count, off_t offset);
fd為文件描述符,buf為緩沖區, count為緩沖區字節數, offset為偏移量。
pread()成功調用返回讀取的字節數,失敗返回-1
pwrite()成功調用返回寫入的字節數,失敗返回-1
------------------------省略分散輸入和集中輸出,截斷文件,非阻塞I/O和大文件I/O等一些知識點---------------
練習:
5-1,請使用標準文件I/O系統調用(open()和lseek())和off_t數據類型修改程序清單5-3中的程序。將宏_FILE_OFFSET_BITS的值設置為64進行編譯,并測試該程序是否能夠成功創建一個大文件。

1 /* 2 * ===================================================================================== 3 * 4 * Filename: large_file.c 5 * 6 * Description: 7 * 8 * Version: 1.0 9 * Created: 2014年03月17日 22時05分50秒10 * Revision: none11 * Compiler: gcc12 *13 * Author: alan (), alan19920626@Gmail.com14 * Organization: 15 *16 * =====================================================================================17 */18 19 #define _FILE_OFFSET_BITS 64 20 #include <sys/stat.h>21 #include <fcntl.h>22 #include "tlpi_hdr.h"23 24 int main(int argc, char *argv[]){25 int fd;26 off_t off;27 if(argc != 3 || strcmp(argv[1], "--help") == 0)28 usageErr("%s pathname offset/n", argv[0]);29 30 fd = open(argv[1], O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);31 32 if(fd == -1)33 errExit("open");34 35 off = atoll(argv[2]);36 if(lseek(fd, off, SEEK_SET) == -1)37 errExit("lseek");38 39 if(write(fd, "test", 4) == -1)40 errExit("write");41 exit(EXIT_SUCCESS);42 43 }View Code測試結果:
1 lancelot@debian:~/Code/tlpi$ ./large_file largefile 101112223332 lancelot@debian:~/Code/tlpi$ ls -l largefile 3 -rw------- 1 lancelot lancelot 10111222337 4月 9 23:22 largefile
5-2,編寫一個程序,使用O_APPEND標志并以寫方式打開一個已存在的文件,且將文件偏移量置于起始位置,再寫入數據。數據會顯示在文件中的哪個位置?為什么?

1 /* 2 * ===================================================================================== 3 * 4 * Filename: 5-2.c 5 * 6 * Description: 7 * 8 * Version: 1.0 9 * Created: 2014年03月17日 22時26分51秒10 * Revision: none11 * Compiler: gcc12 *13 * Author: alan (), alan19920626@gmail.com14 * Organization: 15 * 16 * =====================================================================================17 */18 19 #include <sys/stat.h>20 #include <ctype.h>21 #include <fcntl.h>22 #include "tlpi_hdr.h"23 24 int main(int argc, char *argv[]){25 int fd;26 off_t off;27 ssize_t numWritten;28 29 if(argc != 2 || strcmp(argv[1], "--help") == 0)30 usageErr("%s file", argv[0]);31 32 fd = open(argv[1], O_RDWR | O_APPEND, S_IRUSR | S_IWUSR);33 if(fd == -1)34 errExit("open");35 36 off = lseek(fd, 0, SEEK_SET);37 if(off == -1)38 errExit("lseek");39 40 numWritten = write(fd, "Kevin Durant/n", 13);41 if(numWritten == -1)42 errExit("write");43 44 close(fd);45 exit(EXIT_SUCCESS);46 }View Code測試結果:
1 lancelot@debian:~/Code/tlpi$ cat t1 2 This is the second line. 3 This is the third line. 4 This is the append line. 5 6 lancelot@debian:~/Code/tlpi$ ./write_append t1 7 lancelot@debian:~/Code/tlpi$ cat t1 8 This is the second line. 9 This is the third line.10 This is the append line.11 12 Kevin Durant
5-3,本習題的設計目標在于展示為何以O_APPEND標志打開文件來保障操作的原子性是必要的。請編寫一程序,可接收多達3個命令行參數:
$ automic_append filename num-bytes [x]
該程序應打開制定的文件,然后以每次調用write()寫入一個字節的方式,向文件尾部追加num-bytes個字節。缺省情況下,程序使用O_APPEND標志打開文件,但若存在第三個命令行參數(x),那么打開文件時將不再使用O_APPEND標志,代之以調用write()前調用lseek(fd, 0, SEEK_END)。同時運行該程序的兩個實例,不帶x參數,將100萬字節寫入同一個文件:
$automic_append f1 1000000 &automic_append f1 1000000
重復上述操作,將數據寫入另一個文件,但運行時加入x參數
$automic_append f2 1000000 x &automic_append f2 1000000 x
使用ls -l命令檢查文件f1和f2的大小, 并解釋兩文件大小不同的原因。

1 /* 2 * ===================================================================================== 3 * 4 * Filename: atomic_append.c 5 * 6 * Description: 7 * 8 * Version: 1.0 9 * Created: 2014年03月17日 22時46分49秒10 * Revision: none11 * Compiler: gcc12 *13 * Author: alan (), alan19920626@gmail.com14 * Organization: 15 *16 * =====================================================================================17 */18 19 #include <sys/stat.h>20 #include <fcntl.h>21 #include "tlpi_hdr.h"22 23 int main(int argc, char *argv[]){24 int i, fd, flags, numBytes;25 off_t off;26 ssize_t numWritten;27 28 29 flags = O_RDWR | O_CREAT;30 if(argc < 3 || strcmp(argv[1], "--help") == 0 )31 usageErr("%s filename num-bytes [x]");32 33 if(argc != 4)34 flags = flags | O_APPEND;35 36 numBytes = getInt(argv[2], 0, "num-bytes");37 38 fd = open(argv[1], flags, S_IRUSR | S_IWUSR);39 if(fd == -1)40 errExit("open");41 42 /*if(argc == 4)43 if(lseek(fd, 0, SEEK_END) == -1)44 errExit("lseek");45 46 */47 48 for(i = 0; i < numBytes; ++i){49 if(argc > 3 && argv[3] == "x")50 if(lseek(fd, 0, SEEK_END) == -1)51 errExit("lseek");52 53 if(write(fd, "A", 1) != 1)54 fatal("write() failed");55 }56 57 exit(EXIT_SUCCESS);58 }View Code測試結果:
1 lancelot@debian:~/Code/tlpi$ ls -l f1 f22 -rw------- 1 lancelot lancelot 2000000 4月 9 23:45 f13 -rw------- 1 lancelot lancelot 1000000 4月 9 23:45 f2
5-4,使用fcntl()和close()來實現dup()和dup2()。務必牢記dup2()需要處理的一種特殊情況,即oldfd與newfd相等。這時,應檢查oldfd是否有效,測試fcntl(oldfd, F_GETFL)是否成功就能達到這一目標。若oldfd無效,則dup2()將返回-1,并將errno置為EBADF。

1 /* 2 * ===================================================================================== 3 * 4 * Filename: 5-4.c 5 * 6 * Description: 7 * 8 * Version: 1.0 9 * Created: 2014年03月19日 08時48分46秒10 * Revision: none11 * Compiler: gcc12 *13 * Author: alan (), alan19920626@gmail.com14 * Organization: 15 *16 * =====================================================================================17 */18 19 #include <sys/stat.h>20 #include <fcntl.h>21 #include "tlpi_hdr.h"22 23 int Dup(int oldfd);24 25 int Dup2(int oldfd, int newfd);26 27 int main(){28 int fd;29 int newfd;30 if((fd = open("t1", O_RDONLY)) == -1)31 errExit("open");32 33 //newfd = fcntl(fd, F_DUPFD, fd+1);34 newfd = Dup(fd);35 if(newfd == -1)36 errExit("Dup");37 38 printf("old fd is %d, new fd is %d/n", fd, newfd);39 40 printf("Input the new fd:");41 scanf("%d", &newfd);42 43 /*if(fcntl(oldfd, F_GETFL) == -1)44 errExit("fd is ")45 */46 47 newfd = Dup2(fd, newfd);48 if(newfd == -1)49 errExit("Dup2");50 printf("old fd is %d, new fd is %d/n", fd, newfd);51 52 exit(EXIT_SUCCESS);53 }54 55 int Dup(int oldfd){56 int newfd = fcntl(oldfd, F_DUPFD, oldfd+1);57 return newfd;58 }59 60 int Dup2(int oldfd, int newfd){61 int fd = newfd;62 if(fcntl(oldfd, F_GETFL) == -1){63 errno = EBADF;64 return -1;65 }66 if(oldfd == newfd)67 return newfd;68 else{69 if(fcntl(newfd, F_GETFL) != -1)70 close(newfd);71 newfd = fcntl(oldfd, F_DUPFD, fd);72 if(newfd != fd)73 newfd = fcntl(newfd, F_SETFD, fd);74 75 return newfd;76 }77 }View Code測試結果:
1 lancelot@debian:~/Code/tlpi$ ./my_dup 2 old fd is 3, new fd is 43 Input the new fd:134 old fd is 3, new fd is 13
新聞熱點
疑難解答