linux gcc编译下的文件读写操作
linux下的文件操作
| 所有目錄 ??????????? 1.文件及文件系統的定義 ??????????? 2.linux文件的類型 ??????????? 3.linux文件的權限 ??????????? 4.文件操作 ??????????????????? 4.1 文件的創建 ??????????????????? 4.2 文件的打開及關閉 ??????????????????? 4.3 文件的讀寫操作 ??????????????????? 4.4 文件上鎖 ??????????????????? 4.5 文件的定位 ??????????????????? 4.6 特殊文件的操作 ???????????????????????????? 4.6.1 目錄文件的操作 ???????????????????????????? 4.6.2 鏈接文件的操作 ??????????? 5.部分函數說明 ************************************************************* 正文 ************************************************************* 1.文件及文件系統的定義 ?????? 文件是指有名字的一組相關信息的集合。文件系統是指按照一定規律組織起來的有序的文件組織結構,是構成系統中所有數據的基礎。linux系統中,文件的準確定義是不包含有任何其他結構的字符流。換句話說,文件中的字符和字符之間除了同屬于一個文件之外,不存在任何其他的關系。linux系統提供的文件系統,是樹形層次結構系統。Linux中常用的文件系統主要有ext3、ext2及reiserfs 。 2.linux文件的類型 ?????? linux下最常見的文件類型有5種,它們是普通文件,目錄文件,鏈接文件,字符設備文件和塊設備文件,管道文件,套接口文件。 ?????? 我們用 ls -lh 來查看某個文件的屬性,可以看到有類似 -rw-r--r-- ,值得注意的是第一個符號是 - ,這樣的文件在Linux中就是普通文件。這些文件一般是用一些相關的應用程序創建,比如圖像工具、文檔工具、歸檔工具... .... 或 cp工具等。這類文件的刪除方式是用rm 命令。當我們在某個目錄下執行,看到有類似 drwxr-xr-x ,這樣的文件就是目錄,目錄在Linux是一個比較特殊的文件。注意它的第一個字符是d。創建目錄的命令可以用 mkdir 命令,或cp命令,cp可以把一個目錄復制為另一個目錄。刪除用rm 或rmdir命令。我們可以看到/dev/tty的屬性是 crw-rw-rw- ,注意前面第一個字符是 c ,這表示字符設備文件。比如貓等串口設備我們看到 /dev/hda1 的屬性是 brw-r----- ,注意前面的第一個字符是b,這表示塊設備,比如硬盤,光驅等設備;這個種類的文件,是用mknode來創建,用rm來刪除。目前在最新的Linux發行版本中,我們一般不用自己來創建設備文件。因為這些文件是和內核相關聯的。當我們啟動MySQL服務器時,會產生一個mysql.sock的文件。注意這個文件的屬性的第一個字符是 s。我們了解一下就行了。當我們查看文件屬性時,會看到有類似 lrwxrwxrwx,注意第一個字符是l,這類文件是鏈接文件。是通過ln -s 源文件名 新文件名 。上面是一個例子,表示setup.log是install.log的軟鏈接文件。怎么理解呢?這和Windows操作系統中的快捷方式有點相似。 ??????? 這兒有個程序,他可以列出當前目錄下的文件信息,以及系統“/dev/sda1”和“/dev/lp0”的文件信息。 #include <cstring> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdio.h> int main() { ??? int newret; ??? printf("列出當前目錄下的文件信息:\n"); ??? newret=system("ls -l"); ??? printf("列出“/dev/sda1”的文件信息:\n"); ??? newret=system("ls /dev/sda1 -l"); ??? printf("列出“/dev/ lp0”的文件信息:\n"); ??? newret=system("ls /dev/lp0 -l"); ??? return 0; } 大家可以調試一下。其中system()函數說明如下:表頭文件 ??????? #i nclude<stdlib.h> 定義函數 ??????? int system(const char * string); 函數說明 ??????? system()會調用fork()產生子進程,由子進程來調用/bin/sh-c string來執行參數string字符串所代表的命令,此命>令執行完后隨即返回原調用的進程。在調用system()期間SIGCHLD 信號會被暫時擱置,SIGINT和SIGQUIT 信號則會被忽略。 返回值 ? =-1:出現錯誤?? ? =0:調用成功但是沒有出現子進程?? ? >0:成功退出的子進程的id ??????? 如果system()在調用/bin/sh時失敗則返回127,其他失敗原因返回-1。若參數string為空指針(NULL),則返回非零值>。 如果system()調用成功則最后會返回執行shell命令后的返回值,但是此返回值也有可能為 system()調用/bin/sh失敗所返回的127,因此最好能再檢查errno 來確認執行成功。 附加說明 ??????? 在編寫具有SUID/SGID權限的程序時請勿使用system(),system()會繼承環境變量,通過環境變量可能會造成系統安全的問題。 3.文件的權限 ??????? linux文件的權限可分為四種:可讀取(Readable),可寫入(writable),可執行(eXecute)和無權限,分別用r,w,x和-表示。在linux系統中,對于任意一個文件來說,他都有一個特定的所有者,也是創建此文件的用戶,同時,由于linux中用戶是按組分類的,一個用戶屬于一個或多個組。文件所有者以外的用戶,又可以分為文件所有者的同組用戶和其他用戶。因此,linux系統按文件所有者,文件所有者同組用戶和其他用戶三類規定了了不同的文件訪問權限。用ls -lh顯示的作為權限的10個字符,可分為四部分:1).第一位:一般表示文件類型。2).第二到第四位:表示文件所有者的訪問權限。3).第五位到第七位:表示文件所有者同組用戶的訪問權限。4).第八位到第十位:表示其他用戶的訪問權限。上面介紹了system()函數,可以用system函數編程來實現對文件權限的修改。當然還有個chmod()函數也可以用來設置文件的權限。如果希望每個新建的文件都能被賦予某種權限,則需要由系統的權限掩碼設置函數umask來決定。下面關于umask函數的用法有個例子。 #include <cstring> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdio.h> int main() { ??? mode_t new_umask,old_umask; ??? new_umask=0666; ??? old_umask=umask(new_umask); ??? printf("系統原來的權限掩碼是:%o\n",old_umask); ??? printf("系統新的權限掩碼是:%o\n",new_umask); ??? system("touch hu1"); ??? printf("創建了文件hu1\n"); ??? new_umask=0444; ??? old_umask=umask(new_umask); ??? printf("系統原來的權限掩碼是:%o\n",old_umask); ??? printf("系統新的權限掩碼是:%o\n",new_umask); ??? system("touch hu2"); ??? printf("創建了文件hu2\n"); ??? system("ls hu1 hu2 -l"); ??? return 0; } 該程序的運行結果是:系統原來的權限掩碼是:22 系統新的權限掩碼是:666 創建了文件hu1 系統原來的權限掩碼是:666 系統新的權限掩碼是:444 創建了文件hu2 ---------- 1 hubin hubin 0 2009-08-02 11:07 hu1 --w--w--w- 1 hubin hubin 0 2009-08-02 11:07 hu2 程序結果說明:先將系統的權限掩碼為0666,所以新建的文件hu1訪問權限為0000,即"--------".再將系統的權限掩碼為0444,所以新建的文件hu2訪問權限為0444,即"--w--w--w-".語句system("touch hu1")的作用是調用system函數來運行shell命令"touch hu1",touch命令的作用是更改時間標記,如文件不在,則新建文件。 unmask函數說明: 所需頭文件:#include <sys/types.h> ??????????????????? #include <sys/stat.h> 函數功能:設置建立新文件時的權限掩碼 函數原型:mode_t umask(mode_t mask); 函數傳入值:4位八進制數 函數返回值:返回值為原先系統的umask值 ?????? 要想獲得文件的其他屬性,可以使用stat函數或者是fstat函數,其函數說明如下,例子見附件的st_mode.c. 函數原型 #include <sys/stat.h> int stat(const char *restrict?pathname, struct stat *restrict?buf); 提供文件名字,獲取文件對應屬性。 int fstat(int?filedes, struct stat *buf); 通過文件描述符獲取文件對應的屬性。 int lstat(const char *restrict?pathname, struct stat *restrict?buf); 連接文件描述命,獲取文件屬性。 2 文件對應的屬性 struct stat { ??????? mode_t???? st_mode;?????? //文件對應的模式,文件,目錄等 ??????? ino_t????? st_ino;?????? //inode節點號 ??????? dev_t????? st_dev;??????? //設備號碼 ??????? dev_t????? st_rdev;?????? //特殊設備號碼 ??????? nlink_t??? st_nlink;????? //文件的連接數 ??????? uid_t????? st_uid;??????? //文件所有者 ??????? gid_t????? st_gid;??????? //文件所有者對應的組 ??????? off_t????? st_size;?????? //普通文件,對應的文件字節數 ??????? time_t???? st_atime;????? //文件最后被訪問的時間 ??????? time_t???? st_mtime;????? //文件內容最后被修改的時間 ??????? time_t???? st_ctime;????? //文件狀態改變時間 ??????? blksize_t st_blksize;??? //文件內容對應的塊大小 ??????? blkcnt_t?? st_blocks;???? //偉建內容對應的塊數量 ????? }; 可以通過上面提供的函數,返回一個結構體,保存著文件的信息。 4.文件操作 ??????? Linux系統把目錄,設備和連接等操作都等同于文件的操作。它通過一個文件描述符來進行區分和引用特定的文件。文件描述符是一個非負的整數,是一個索引值,指向內核中每個進程打開的文件的記錄表。 ??????? Linux系統中,基于文件描述符的文件的操作有:不帶緩存的文件I/O操作和帶緩存的流文件I/O操作。不帶緩存的文件I/O操作又稱系統調用I/O操作,符合POSIX標準,設計的程序能在兼容POSIX標準的系統間方便的移植。主要的函數有:creat,open,close,read,write,lseek,flock,fcntl等。帶緩存的流文件I/O操作是在內存開辟一個“緩存區”,為程序中的每一個文件使用。帶緩存的文件I/O操作又稱標準I/O操作,符合ANSI C標準,比不帶緩存的文件I/O程序方便移植。主要函數有fopen,fclose,fgetc,fputc,fgets,fread,fwrite,fseek,rewind,ftell等。由于流文件的I/O操作在C語言經常用到,一般的C語言書上也有介紹,所以就不多說了,其函數原型見部分函數說明。 4.1 文件的創建。 ?????? 在linuxC程序設計中,創建文件可以調用creat函數。 ?????? int creat(const char *filename, mode_t mode); 參數mode指定新建文件的存取權限,它同umask一起決定文件的最終權限(mode&umask),其中umask代表了文件在創建時需要去掉的一些存取權限。umask可通過系統調用umask()來改變:下面有個例子說明該函數的用法,程序是在“/home”目錄下創建一個“file”的文件,并把此文件的權限為所有者具有只讀權限。 #include <cstring> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdio.h> int main() { ??? int fd; ??? fd=creat("/home/file",S_IRUSR);/*所有者具有只讀權限*/ ??? system("ls /home/file -l"); ??? return 0; } 有興趣的可以調一下。如果所創建的文件原本就存在,則就不能創建該文件了。 附creat函數的說明: creat函數:創建一個文件 相關頭文件:#include 函數表達式:int creat(const char *pathname,mode_t mode); 參數說明:參數pathname表示需要創建的文件的路徑。參數mode表示文件的文件權限。 返回值說明:如果成功創建一個文件則返回新的文件描述符,失敗則返回-1. 函數功能詳解:creat函數創建一個新文件,并以“只寫”的方式打開,返回新文件的文描述符。 函數使用說明: ? ?? ?? ? crea函數用于創建一個文件,并且將其以“只寫” 形式打開。因此程序是無法讀該文件的。如果需要讀這個新文件中的內容,則必須將其關閉后從新打開。 ? ?? ?? ? creat函數的第一個參數表示的文件不存在,則創建該文件并且以“只寫”方式打開;如果該文件愛你存在則將其截短謂0,再以“只寫”方式打開。因此使用creat函數要注意文件重名時可能帶來的問題。以下是定義于?<sys/stat.h>?中的九種文件訪問權限位(用于構成參數 mode): ????????? S_IRUSR?? // user-read(文件所有者讀) ????????? S_IWUSR?? // user-write(文件所有者寫) ????????? S_IXUSR?? // user-execute(文件所有者執行) ????????? S_IRGRP?? // group-read ????????? S_IWGRP?? // group-write ????????? S_IXGRP?? // group-execute ????????? S_IROTH?? // other-read ????????? S_IWOTH?? // other-write ????????? S_IXOTH?? // other-execute 其中 user 指文件所有者,group 指文件所有者所在的組,other 指其他用戶。 creat 函數只能以只讀方式創建新文件。如果我們要以讀寫方式創建新文件,可以用 open 函數:由于open函數比creat函數好用,所以open函數用得較多。 4.2 文件的打開和關閉 ????? 文件的打開可以用open,fopen函數。關閉用close和fclose函數。open和close屬于不帶緩存的文件I/O操作而fopen和fclose函數屬于流文件操作。當用open函數時,即使原來的文件不存在,也可以用open函數創建文件。在打開或者創建文件時,可以指定文件的屬性及用戶的權限等參數。close函數則關閉一個打開的文件而fopen和fclose函數在C語言編程就用得很多了,這里就不舉例子 例:要求在“./home”下以可讀寫的方式打開一個名為"file"的文件。如果該文件不存在,則創建此文件;如果存在,將文件清空后關閉。 #include <stdio.h> #include <stdlib.h> #include <fcntl.h> int main() { ??? int fd; ??? if((fd=open("/home/file",O_CREAT|O_TRUNC|O_WRONLY,0600))<0) ??? { ????????? printf("打開文件出錯"); ????????? exit(1); ??? } ??? else ??? { ????????? printf("打開(創建)文件“file”,文件描述符為:%d",fd); ??? } ??? if(close(fd)<0) ??? { ????????? printf("關閉文件出錯"); ????????? exit(1); ??? } ??? system("ls /home/file -l"); ??? return 0; } open 函數的簡單描述 #include?<fcntl.h> int open(const char *pathname, int oflag, ... /* mode_t mode */); 返回值:成功則返回文件描述符,否則返回 -1 對于 open 函數來說,第三個參數(...)僅當創建新文件時才使用,用于指定文件的訪問權限位(access permission bits)。pathname 是待打開/創建文件的路徑名(如 C:/cpp/a.cpp);oflag 用于指定文件的打開/創建模式,這個參數可由以下常量(定義于 fcntl.h)通過邏輯或構成。 ????? O_RDONLY????? 只讀模式 ????? O_WRONLY????? 只寫模式 ????? O_RDWR??????? 讀寫模式 打開/創建文件時,至少得使用上述三個常量中的一個。以下常量是選用的: ????? O_APPEND?????? 每次寫操作都寫入文件的末尾 ? ????? O_CREAT??????? 如果指定文件不存在,則創建這個文件? ????? O_EXCL???????? 如果要創建的文件已存在,則返回 -1,并且修改 errno 的值 ????? O_TRUNC??????? 如果文件存在,并且以只寫/讀寫方式打開,則清空文件全部內容? ????? O_NOCTTY?????? 如果路徑名指向終端設備,不要把這個設備用作控制終端。 ????? O_NONBLOCK???? 如果路徑名指向 FIFO/塊文件/字符文件,則把文件的打開和后繼 I/O設置為非阻塞模式(nonblocking mode) 以下三個常量同樣是選用的,它們用于同步輸入輸出 ????? O_DSYNC??????? 等待物理 I/O 結束后再 write。在不影響讀取新寫入的數據的前提下,不等待文件屬性更新。 ????? O_RSYNC??????? read 等待所有寫入同一區域的寫操作完成后再進行 ????? O_SYNC???????? 等待物理 I/O 結束后再 write,包括更新文件屬性的 I/O open 返回的文件描述符一定是最小的未被使用的描述符。 close函數說明: 頭文件:#include?<unistd.h> 函數原型:int close(int filedes); 返回值:0(成功)或者 -1(出錯) 進程結束時,該進程打開的所有文件都會自動被內核(kernel)關閉。 4.3 文件的讀寫 ?????當文件按指定的工作方式打開以后,就可以執行對文件的讀和寫。下面按文件的性質分類進行操作。針對文本文件和二進制文件的不同性質,對文本文件來說,可按字符讀寫或按字符串讀寫;對二進制文件來說,可進行成塊的讀寫或格式化的讀寫。?下面是流文件的讀寫操作。 1. 讀寫字符 ??? C提供fgetc和fputc函數對文本文件進行字符的讀寫,其函數的原型存于stdio.h頭文件中,格式為: ??? int fgetc(FILE *stream) ??? fgetc( )函數從輸入流的當前位置返回一個字符,并將文件指針指示器移到下一個字符處,如果已到文件尾,函數返回EOF,此時表示本次操作結束,若讀寫文件完成,則應關閉文件。 ??? int fputc(int ch,FILE *stream) ??? fputc()函數完成將字符c h的值寫入所指定的流文件的當前位置處,并將文件指針后移一位。fputc()函數的返回值是所寫入字符的值,出錯時返回EOF 2. 讀寫字符串 ??? C提供讀寫字符串的函數原型在stdio.h頭文件中,其函數形式為: ??? Char *fgets(char *str,int num,FILE *stream) ??? fgets() 函數從流文件stream中讀取至多num-1個字符,并把它們放入str指向的字符數組中。讀取字符直到遇見回車符或E O F(文件結束符)為止,或讀入了所限定的字符數。 ??? int fputs(char *str,FILE *stream) ??? fputs( )函數將str指向的字符串寫入流文件。操作成功時,函數返回0值,失敗返回非零值 3. 格式化的讀寫 前面的程序設計中,我們介紹過利用scanf( )和printf( )函數從鍵盤格式化輸入及在顯示器上進行格式化輸出。對文件的格式化讀寫就是在上述函數的前面加一個字母f成為fscanf( )和fprintf( )。其函數調用方式: int?fscanf(FILE *stream,char *format,arg_list) int?fprintf(FILE *stream,char *format,arg_list) 其中,stream為流文件指針,其余兩個參數與scanf( )和printf( )用法完全相同。 4. 成塊讀寫 前面介紹的幾種讀寫文件的方法,對其復雜的數據類型無法以整體形式向文件寫入或從文件讀出。C語言提供成塊的讀寫方式來操作文件,使其數組或結構體等類型可以進行一次性讀寫。成塊讀寫文件函數的調用形式為: int fread(void *buf,int size,int count,FILE *stream) int fwrite(void *buf,int size,int count,FILE *stream) fread()函數從stream 指向的流文件讀取count (字段數)個字段,每個字段為size(字段長度)個字符長,并把它們放到b u f(緩沖區)指向的字符數組中。 fread()函數返回實際已讀取的字段數。若函數調用時要求讀取的字段數超過文件存放的字段數,則出錯或已到文件尾,實際在操作時應注意檢測。 fwrite( )函數從buf(緩沖區)指向的字符數組中,把count(字段數)個字段寫到stream所指向的流中,每個字段為size個字符長,函數操作成功時返回所寫字段數。 關于成塊的文件讀寫,在創建文件時只能以二進制文件格式創建。 ?????? 這些流文件的讀寫操作我們在C語言中經常用到,所以就不舉例子了,下面的是不帶緩存的文件讀寫,主要用到read函數,write函數和lseek函數。read函數用于將指定的文件描述符中讀出數據。write函數用于向打開的文件寫數據,寫操作從文件當前位置開始。lseek函數用于在指定的文件描述符中將文件指針定位到相應的位置。 1. read 頭文件:#include?<unistd.h> 函數原型:ssize_t read(int filedes, void *buf, size_t nbytes); 返回值:讀取到的字節數;0(讀到 EOF);-1(出錯) read 函數從 filedes 指定的已打開文件中讀取 nbytes 字節到 buf 中。以下幾種情況會導致讀取到的字節數小于 nbytes : A. 讀取普通文件時,讀到文件末尾還不夠 nbytes 字節。例如:如果文件只有 30 字節,而我們想讀取 100 字節,那么實際讀到的只有 30 字節,read 函數返回 30 。此時再使用 read 函數作用于這個文件會導致 read 返回 0 。 B. 從終端設備(terminal device)讀取時,一般情況下每次只能讀取一行 C. 從網絡讀取時,網絡緩存可能導致讀取的字節數小于 nbytes 字節。 ?D. 讀取 pipe 或者 FIFO 時,pipe 或 FIFO 里的字節數可能小于 nbytes 。 ?E. 從面向記錄(record-oriented)的設備讀取時,某些面向記錄的設備(如磁帶)每次最多只能返回一個記錄。 ?F. 在讀取了部分數據時被信號中斷。 讀操作始于 cfo 。在成功返回之前,cfo 增加,增量為實際讀取到的字節數。 2. write 頭文件:#include?<unistd.h> 函數原型:ssize_t write(int filedes, const void *buf, size_t nbytes) 返回值:寫入文件的字節數(成功);-1(出錯) write 函數向 filedes 中寫入 nbytes 字節數據,數據來源為 buf 。返回值一般總是等于 nbytes,否則就是出錯了。常見的出錯原因是磁盤空間滿了或者超過了文件大小限制。對于普通文件,寫操作始于 cfo 。如果打開文件時使用了 O_APPEND,則每次寫操作都將數據寫入文件末尾。成功寫入后,cfo 增加,增量為實際寫入的字節數。 例程:編寫一個程序,在當前目錄下創建用戶可讀寫文件“hello.txt”,在其中寫入“Hello, software weekly”,關閉該文件。再次打開該文件,讀取其中的內容并輸出在屏幕上 #include <cstring> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdio.h> #define LENGTH 100 main() { int fd, len; char str[LENGTH]; fd = open("hello.txt", O_CREAT | O_RDWR, S_IRUSR | S_IWUSR); /* 創建并打開文件 */ if (fd) { write(fd, "Hello, Software Weekly", strlen("Hello, software weekly")); /* 寫入 Hello, software weekly字符串 */ close(fd); } fd = open("hello.txt", O_RDWR); len = read(fd, str, LENGTH); /* 讀取文件內容 */ str[len] = '\0'; printf("%s\n", str); close(fd); } 4.4 文件的上鎖 ????? Linux是多用戶操作系統,多個用戶共同使用,操作同一個文件的事情很容易就發生。linux為了避免這種情況,就給這個文件上鎖,以避免共享資源產生競爭,導致數據讀寫錯誤。Linux中給文件上鎖主要有建議性鎖和強制性鎖。建議性鎖是這樣規定的:每個使用上鎖文件的進程都要檢查是否有鎖存在,當然還得尊重已有的鎖。內核和系統總體上都堅持不使用建議性鎖,它們依靠程序員遵守這個規定。強制性鎖是由內核執行的。當文件被上鎖來進行寫入操作時,在鎖定該文件的進程釋放該鎖之前,內核會阻止任何對該文件的讀或寫訪問,每次讀或寫訪問都得檢查鎖是否存在。給文件加建議性鎖的是flock函數,加強制性鎖的是fcntl函數。,一般情況下,系統使用強制性鎖,而很少使用建議性鎖。 fcntl()用來操作文件描述詞的一些特性。參數fd代表欲設置的文件描述詞,參數cmd代表欲操作的指令。文件描述詞,當一個文件打開后,系統會分配一部分資源來保存該文件的信息.以后對文件的操作就可以直接引用該部分資源了,文件描述詞可以認為是該部分資源的一個索引,在打開文件時返回.fcntl是用來對文件的一些屬性進行設置的,需要一個文件描述詞參數.有以下幾種情況: F_DUPFD用來查找大于或等于參數arg的最小且仍未使用的文件描述詞,并且復制參 數fd的文件描述詞。執行成功則返回新復制的文件描述詞。請參考dup2()。F_GETFD取得close-on-exec旗標。若此旗標的 FD_CLOEXEC位為0,代表在調用exec()相關函數時文件將不會關閉。 F_SETFD 設置close-on-exec 旗標。該旗標以參數arg 的FD_CLOEXEC位決定。 F_GETFL 取得文件描述詞狀態旗標,此旗標為open()的參數flags。 F_SETFL 設置文件描述詞狀態旗標,參數arg為新旗標,但只允許O_APPEND、O_NONBLOCK和O_ASYNC位的改變,其他位的改變將不受影響。 F_GETLK 取得文件鎖定的狀態。 F_SETLK 設置文件鎖定的狀態。此時flcok 結構的l_type 值必須是F_RDLCK、F_WRLCK或F_UNLCK。如果無法建立鎖定,則返回-1,錯誤代碼為EACCES 或EAGAIN。 F_SETLKW F_SETLK 作用相同,但是無法建立鎖定時,此調用會一直等到鎖定動作成功為止。若在等待鎖定的過程中被信號中斷時,會立即返回-1,錯誤代碼為EINTR。參數lock指針為flock 結構指針,定義如下 struct flcok { short int l_type; /* 鎖定的狀態*/ short int l_whence;/*決定l_start位置*/ off_t l_start; /*鎖定區域的開頭位置*/ off_t l_len; /*鎖定區域的大小*/ pid_t l_pid; /*鎖定動作的進程*/ }; l_type 有三種狀態: F_RDLCK 建立一個供讀取用的鎖定 F_WRLCK 建立一個供寫入用的鎖定 F_UNLCK 刪除之前建立的鎖定 l_whence 也有三種方式: SEEK_SET 以文件開頭為鎖定的起始位置。 SEEK_CUR 以目前文件讀寫位置為鎖定的起始位置 SEEK_END 以文件結尾為鎖定的起始位置。 返回值 成功則返回0,若有錯誤則返回-1,錯誤原因存于errno. 該函數可以改變已打開的文件的性質。 #include int fcntl(int fields, int cmd, .../* int arg */); //若成功則依賴于cmd,若出錯則返回-1 第三個參數總是一個整數,與上面所示函數原型中的注釋部分相對應。但是在作為記錄鎖用時,第三個參數則是指向一個結構的指針。 fcntl函數有5種功能: 1.復制一個現有的描述符(cmd=F_DUPFD). 2.獲得/設置文件描述符標記(cmd=F_GETFD或F_SETFD). 3.獲得/設置文件狀態標記(cmd=F_GETFL或F_SETFL). 4.獲得/設置異步I/O所有權(cmd=F_GETOWN或F_SETOWN). 5.獲得/設置記錄鎖(cmd=F_GETLK,F_SETLK或F_SETLKW). 例子:打開“/home/hubin”下的一個“file”文件,打開后對其加上強制性的寫入鎖,然后釋放寫入鎖。程序源代碼見附件中的lock_set.c.編譯成功后,打開兩個終端,都執行lock_set,程序結果如下: 終端1: 加上寫入鎖的是:7800 釋放強制性鎖:7800 終端2: 加上寫入鎖的是:7800 加上寫入鎖的是:7803 釋放強制性鎖:7803 由程序運行結果可知,此程序在終端1中運行后的第一個進程,先打開或創建文件"file",接著給“file”加上寫入鎖,鎖定文件,并打印輸出加鎖信息和給文件加鎖進程的進程號,這兒加的是“寫入鎖”,這臺機器上第一個給文件加鎖進程 的進程號是“7800”,最后等待用戶按任意鍵后解除鎖定,并打印輸出解鎖信息和給文件解鎖進程的進程號。如果在第一個進程解除鎖定前,此程序在另一個終端中在運行,即運行第二個進程,此時文件已經被第一進程上鎖,那么第二個進程無法上鎖,只能先打印輸出文件已經上鎖和第一個進程的進程號的信息。直到前一個進程解鎖后,后一個進程才能上鎖,解鎖。 4.5 文件的定位 ????? 文件中有一個位置指針,指向當前讀寫的位置。如果順序讀寫一個文件,每次讀寫一個字符,則讀寫完一個字符后,該位置指針自動移動指向下一個字符位置。當需要改變文 件順序讀寫的次序時,根據需要隨時指定文件讀寫的位置,也就是能實現文件的隨機讀寫操作,這種功能是由 C 語言提供的文件定位函數來實現的。 文件的定位函數有lseek,fseek,rewind,ftell等函數。其中lseek是不帶緩存的文件操作,其他的是流文件操作。 4.5.1.lseek函數 ????? 所有打開的文件都有一個當前文件偏移量(current file offset),以下簡稱為 cfo。cfo 通常是一個非負整數,用于表明文件開始處到文件當前位置的字節數。讀寫操作通常開始于 cfo,并且使 cfo 增大,增量為讀寫的字節數。文件被打開時,cfo 會被初始化為 0,除非使用了?O_APPEND?。使用 lseek 函數可以改變文件的 cfo 。函數說明: 頭文件: #include?<unistd.h> 函數原型:off_t lseek(int filedes, off_t offset, int whence); 返回值:新的偏移量(成功),-1(失敗) 參數 offset 的含義取決于參數 whence: 1. 如果 whence 是?SEEK_SET,文件偏移量將被設置為 offset。 2. 如果 whence 是?SEEK_CUR,文件偏移量將被設置為 cfo 加上 offset,offset 可以為正也可以為負。 3. 如果 whence 是?SEEK_END,文件偏移量將被設置為文件長度加上 offset,offset 可以為正也可以為負。 SEEK_SET、SEEK_CUR 和 SEEK_END 是 System V 引入的,在這之前使用的是 0、1 和 2。 lseek 的以下用法返回當前的偏移量: off_t??? currpos; currpos = lseek(fd, 0, SEEK_CUR); 這個技巧也可用于判斷我們是否可以改變某個文件的偏移量。如果參數 fd(文件描述符)指定的是 pipe(管道)、FIFO 或者 socket,lseek 返回 -1 并且置 errno 為?ESPIPE。對于普通文件(regular file),cfo 是一個非負整數。但對于特殊設備,cfo?有可能是負數。因此,我們不能簡單地測試 lseek 的返回值是否小于 0 來判斷 lseek 成功與否,而應該測試 lseek 的返回值是否等于 -1 來判斷 lseek 成功與否。lseek 僅將 cfo 保存于內核中,不會導致任何 I/O 操作。這個 cfo 將被用于之后的讀寫操作。如果 offset 比文件的當前長度更大,下一個寫操作就會把文件“撐大(extend)”。這就是所謂的在文件里創造“空洞(hole)”。沒有被實際寫入文件的所有字節由重復的 0?表示。空洞是否占用硬盤空間是由文件系統(file system)決定的。例程: #include?<stdio.h> #include?<fcntl.h> #include?<unistd.h> #include?<sys/stat.h> ? char??? buf1[] = "abcdefghij";char??? buf2[] = "ABCDEFGHIJ"; int main(void) { ? ?? int? fd, size; ???? if ((fd = creat("file.hole", S_IRUSR|S_IWUSR)) < 0) ? ?? { ?????????? printf("creat error\n"); ?????????? return -1; ? ?? } ???? size = sizeof(buf1); ???? if (write(fd, buf1, size) != size) ???? {?????? ??????????? printf("buf1 write error\n"); ??????????? return -1; ???? } ???? /* offset now = 10 */ ???? if (lseek(fd, 16384, SEEK_SET) == -1) ???? { ? ? ? ? ?? printf("lseek error\n"); ?????????? return -1; ? ?? } ? ? ? /* offset now = 16384 */ ???? size = sizeof(buf2); ???? if (write(fd, buf2, size) != size) ???? { ??????????? printf("buf2 write error\n"); ??????????? return -1; ???? } ? ? ? /* offset now = 16394 */ ??? return 0; ? ? } 4.5.2 定位讀寫指針函數fseek ( ) ????? 該函數的功能是將文件的讀寫指針從某個位置移到指定的位置,該函數將為C語言對文件的隨機讀寫提供了方法。該函數調用格式如下: fseek(fp, f<偏移量),(起始位置)) 其中,fseek是該函數的函數名;fp是指向被操作文件的文件指針;<偏移量)是表示移動當前讀寫指針的距離量,該參數的類型為long int型;<起始位置)是偏移量的相對位置。例如,起始位置為文件頭,偏移量為50,則表示將讀寫指針移到相對文件頭距離為50個字節的位置。起始 位置的設置方法有三; ??? 0表示相對于文件頭 ??? 1表示相對于文件的當前位置 ??? 2表示祖對于文件尾 ??? 實際中,常用宏定義來替代起始位置,規定如下: ??? SEEK_SE'C表示文件頭 ??? SEEK_CUR表示當前位置 ??? SEEK_END表示文件尾 ??? 例如: ??? fseek(fp,200L,0);將讀寫指針移到離文件頭20.個字節處。 ??? fseek(fp, 80L,1);將讀寫指針移到離當前位置80個字節處。 ??? fseek (fp , -50L,0);將讀寫指針移到從文件尾向后退50個字節處。 ??? 該函數一般用于二進制文件。如果用于文本文件要發生字符轉換,計算位置時會發生誤差。 4.5.3 歸位讀寫指針函數rewind () ?????? 該函數的功能是將某個文件的讀寫指針歸位于文件頭。該函數的調用格式如下: rewind(fp)其中,rewind是該函數的函數名,fp是被操作文件的文件指針。使用該函數后,會使被操作文件的讀寫指針指向文件頭。該函數的功能與下列函數功能相同。 (seek (fp,OL.0); 4.5.4 返回讀寫指針函數ftell ( ) ?????? 該函數的功能是返回指定文件當前讀寫指針的位置,該位置是用相對于文件頭所相隔的字節數來表示。例如,該函數返回某個文件的當前讀寫指針的位置是100字節,即表示當前讀寫指針在離文件頭有100個字節處。該函數調用格式如下:ftell(fp)它返回一個表示字節數的long int型數值。 例程:建立一個數據文件,隨機讀取其中的某個數據。使用fprintf( )函數建立一個數據文件xy. dat ,然后,指定從某個數據起連續讀出若干個數據,最后,再讀出這組數據的起始數據。程序內容如下: #inciude <stdio.h> main ( ) { ????int i,x,y; ????FLLE * fp; ????fp=fopen("xy. dat" ,"wb十rb"); ????for (i=O;i<20;i++) ??????fprintf (fp,,%5d",i+1); ????printf("\nlnput x:"). ????scanf(^%d"&x), ????for(i=o;i<5:i十十) ????{ ??????fseek(fp, (long) (5*(x一l+i),0); ??????fscanf (fp ,"%d",&y); ??????printf("%d\t",y). ????} fseek(fp,(Iong)(5*x一5),0): fscan((fp,"%d",&c ), rewind(fp); printf("\n%d\n",&x); fclose(fp); 4.6 特殊文件的操作????? linux中,除了普通文件外,還有積累重要的特殊文件。特殊文件的操作和普通文件操作類似,最常用的主要有目錄文件和鏈接文件。 4.6.1 目錄文件的操作 ????? 對目錄文件的操作可以使用mkdir函數,opendir函數,closedir函數,readdir函數和scandir函數。例程:讀取系統目錄文件"/etc/rc.d"中的所有目錄結構。 #include <cstring> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdio.h> int main() { ??? DIR *dir; ??? struct dirent *ptr; ??? int i; ??? dir=opendir("/etc/rc.d"); ??? while((ptr=readdir(dir))!=NULL) ????????? printf("目錄:%s\n",ptr->d_name); ??? closedir(dir); } opendir函數說明: 所需頭文件:#include <sys/types.h> ?????????????????? #include <dirent.h> 函數功能:打開目錄句柄 函數原型:DIR *opendir(const char *name); 函數傳入值:打開參數name指定的目錄,并返回DIR *形態的目錄流,對目錄的讀取和搜索都要使用此返回值 函數返回值:成功則返回DIR *形態的目錄流,打開失敗則返回NULL。 readdir函數說明: 所需頭文件:#include <sys/types.h> ?????????????????? #include <sys/stat.h> ?????????????????? #include <fcntl.h> 函數功能:讀取目錄文件 函數原型:struct dirent *readdir(DIR *dir); 函數傳入值:返回參數dir目錄流的下個目錄進入點 ??????????????????? dirent結構定義如下: ??????????????????? struct dirent ??????????????????? { ????????????????????????? ino_t d_ino; ????????????????????????? ff_t d_off; ????????????????????????? signed short int d_reclen; ????????????????????????? unsigned char d_type; ????????????????????????? har d_name[256]; ?????????????????? } ?????????????????? d_ino 此目錄進入點的inode ?????????????????? d_off 目錄文件開頭至此目錄進入點的位移 ?????????????????? d_reclen_name的長度,不包含NULL字符 ?????????????????? d_type d_name所指的文件類型 ?????????????????? s_name 文件名 函數返回值:成功則返回下個目錄進入點,有錯誤發生或讀取到目錄文件尾則返回NULL closedir函數說明: 所需頭文件:#include <sys/types.h> ?????????????????? #include <dirent.h> 函數功能:關閉目錄文件 函數原型:int closedir(DIR *dir) 函數傳入值:參數dir:目錄流 函數返回值:關閉成功則返回0,失敗返回-1。 4.6.2 鏈接文件的操作 ?????? Linux系統中的鏈接文件,有點類似于Windows系統中的“快捷方式”,但并不完全一樣,Linux系統中的鏈接方式有兩種:軟鏈接和硬鏈接。 1.軟鏈接文件 ????? 軟鏈接又叫符號鏈接,這個文件包含了另一個文件的路徑名。可以是任意文件或目錄,可以鏈接不同文件系統的文件。鏈接文件甚至可以鏈接不存在的文件,這就產生了一般稱之為“斷鏈”的問題,鏈接文件甚至可以循環鏈接自己,類似于編程語言中的遞歸。例程:設計一個程序,要求為“/etc/passwd”文件建立軟鏈接“link”,并查看此鏈接文件和“/etc/passwd”文件。 #include <unistd.h> int main() { ??? symlink("/etc/passwd","link"); ??? system("ls link -l"); ??? system("ls /etc/passwd -l"); ??? return 0; } 程序運行結果如下: hubin@hubin-desktop:~/linux_c$ ./slink lrwxrwxrwx 1 hubin hubin 11 2009-08-03 08:29 link -> /etc/passwd -rw-r--r-- 1 root root 1487 2009-04-23 17:57 /etc/passwd 從程序運行結果看,在新創建的“link”文件代表權限的10個字符中,第一位是"l",而且最后顯“->/etc/passwd”,表明鏈接目標是"/etc/passwd"文件。 symlink函數說明: 所需頭文件:#include <unistd.h> 函數功能:建立軟鏈接 函數原型:int symlink(const char *oldpath,const char *newpath); 函數傳入值:參數newpath:鏈接名稱 ??????????????????? 參數oldpath:已存在文件路徑或文件名 函數返回值:成功返回0,失敗返回-1 2. 硬鏈接文件 ?????? 對硬鏈接文件進行讀寫和刪除操作時候,結果和軟鏈接相同。但如果刪除硬鏈接文件的源文件,硬鏈接文件依然存在,而且保留了原有的內容,只是,系統就“忘記了”它曾經是硬鏈接文件,而把它當成一個普通文件。硬鏈接文件有兩個限制:1).不允許給目錄創建硬鏈接;2).只有在同一文件系統中的文件之間才能創建鏈接。 ?????? 例程:設計一個程序,為“/etc/passwd”文件建立硬鏈接文件“hpasswd”,并查看此鏈接文件和“/etc/passwd”文件。 #include <unistd.h> int main() { ??? link("/etc/passwd","hardlink"); ??? system("ls hardlink -l"); ??? system("ls /etc/passwd -l"); ??? return 0; } 程序運行結果: -rw-r--r-- 1 hubin hubin 0 2009-08-03 08:49 hardlink -rw-r--r-- 1 root root 1487 2009-04-23 17:57 /etc/passwd ? ? ? 從程序結果看,兩個文件表面上是一樣的,除了文件名,新建立的文件跟原文件顯示的屬性一模一樣,而且第二項是“1”,因此,確實建立了一個硬鏈接文件。 link函數說明: 所需頭文件:#include <unistd.h> 函數功能:建立硬鏈接 函數原型:int link(const char *oldpath,const char *newpath); 函數傳入值:參數newpath:鏈接名稱 ??????????????????? 參數oldpath:已存在文件路徑或文件名 函數返回值:成功返回0,失敗返回-1 備注:如果參數newpath指定的名稱為一不存在的文件,則不會建立鏈接。? 5.部分函數說明 chmod?--?改變文件模式 說明bool?chmod?( string filename, int mode)嘗試將?filename?所指定文件的模式改成?mode?所給定的。注意?mode?不會被自動當成八進制數值,而且也不能用字符串(例如 "g+w")。要確保正確操作,需要給?mode?前面加上 0: |
?
總結
以上是生活随笔為你收集整理的linux gcc编译下的文件读写操作的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 十六、Python操作excel(.xl
- 下一篇: 简易的遍历文件加密解密