1. 程式人生 > >linux系統程式設計之檔案I/O

linux系統程式設計之檔案I/O

一、檔案描述符      

       linux系統中,所有開啟的檔案都對應一個數字,這個數字由系統來分配,稱為檔案描述符。

       PCB程序控制塊裡有檔案描述符表,以陣列形式存放於核心區。

        一個程序預設開啟3個檔案描述符  STDIN_FILENO 0   STDOUT_FILENO 1  STDERR_FILENO 2

        新開啟檔案返回檔案描述符表中未使用的最小檔案描述符。

二、open函式

        可以開啟或建立一個檔案。

        標頭檔案
        #include <sys/types.h>
        #include <sys/stat.h>
        #include <fcntl.h>

        函式原型
        int open(const char *pathname, int flags);
        int open(const char *pathname, int flags, mode_t mode);
        返回值:成功返回新分配的檔案描述符,出錯返回-1並設定errno

三、read/write 函式

標頭檔案

#include <unistd.h>

函式原型

ssize_t read(int fd, void *buf, size_t count);
返回值:成功返回讀取的位元組數,出錯返回-1並設定errno,如果在調read之前已到達檔案末尾,則這次read返回0

 

ssize_t write(int fd, const void *buf, size_t count);
返回值:成功返回寫入的位元組數,出錯返回-1並設定errno
 

四、阻塞與非阻塞

讀常規檔案是不會阻塞的不管讀多少位元組,read一定會在有限的時間內返回。從終端裝置或網路讀則不一定,如果從終端輸入的資料沒有換行符,呼叫read讀終端裝置就會阻
塞,如果網路上沒有接收到資料包,呼叫read從網路讀就會阻塞,至於會阻塞多長時間也是不確定的,如果一直沒有資料到達就一直阻塞在那裡。同樣,寫常規檔案是不會阻塞的,而
向終端裝置或網路寫則不一定。

阻塞(Block) 當程序呼叫一個阻塞的系統函式時,該程序被置於睡眠(Sleep)狀態,這時核心排程其它程序執行,直到該程序等待的事件發生了(比如網路上接收到資料包,或者呼叫sleep指定的睡眠時間到了)它才有可能繼續執行。與睡
眠狀態相對的是執行(Running)狀態,在Linux核心中,處於執行狀態的程序分為兩種情況:

(1正在被排程執行;(2)就緒狀態。

非阻塞I/O有一個缺點,如果所有裝置都一直沒有資料到達,呼叫者需要反覆查詢做無用功,如果阻塞在那裡,作業系統可以排程別的程序執行,就不會做無用功了。

用非阻塞I/O實現等待超時,既保證了超時退出的邏輯又保證了有資料到達時處理延遲較小 。現給出程式碼:

#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#define MSG_TRY "try again\n"
#define MSG_TIMEOUT "timeout\n"
int main(void)
{
char buf[10];
int fd, n, i;
fd = open("/dev/tty", O_RDONLY|O_NONBLOCK);
if(fd<0) {
perror("open /dev/tty");
exit(1);
}
for(i=0; i<5; i++) {
n = read(fd, buf, 10);
if(n>=0)
break;
if(errno!=EAGAIN) {
perror("read /dev/tty");
exit(1);
}
sleep(1);
write(STDOUT_FILENO, MSG_TRY, strlen(MSG_TRY));
}
if(i==5)
write(STDOUT_FILENO, MSG_TIMEOUT, strlen(MSG_TIMEOUT));
else
write(STDOUT_FILENO, buf, n);
close(fd);
return 0;
}


五、lseek函式

每個開啟的檔案都記錄著當前讀寫位置,開啟檔案時讀寫位置是0,表示檔案開頭,通常讀寫多少個位元組就會將讀寫位置往後移多少個位元組。但是有一個例外,如果以O_APPEND方
式開啟,每次寫操作都會在檔案末尾追加資料,然後將讀寫位置移到新的檔案末尾。

標頭檔案   

#include <sys/types.h>
  #include <unistd.h> 

函式原型

off_t lseek(int fd, off_t offset, int whence);
若lseek成功執行,則返回新的偏移量。
六、fcntl
可以用fcntl函式改變一個已開啟的檔案的屬性,可以重新設定讀、寫、追加、非阻塞等標誌(這些標誌稱為File Status Flag),而不必重新open檔案。

標頭檔案
#include <unistd.h>
#include <fcntl.h>

函式原型
int fcntl(int fd, int cmd);
int fcntl(int fd, int cmd, long arg);
int fcntl(int fd, int cmd, struct flock *lock);