我寫這篇文章的目的是為了對read和write兩個函數的用法做一個總結,同時提醒自己不要忘記:
一、原型介紹
#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);
參數:
fd: 将要讀取資料的檔案描述詞。
buf: 所讀取到的資料的記憶體緩沖。
count: 需要讀取的資料量。
read()會把參數fd所指的檔案傳送nbyte個位元組到buf指針所指的記憶體中。若參數nbyte為0,則read()不會有作用并傳回0。傳回值為實際讀取到的位元組數,如果傳回0,表示已到達檔案尾或無可讀取的資料。錯誤傳回-1,并将根據不同的錯誤原因适當的設定錯誤碼。
EAGAIN:打開檔案時設定了O_NONBLOCK标志,并且目前沒有資料可讀取
EBADF:檔案描述詞無效,或者檔案不可讀
EFAULT:參數buf指向的空間不可通路
EINTR:資料讀取前,操作被信号中斷
EINVAL:一個或者多個參數無效
EIO:讀寫出錯
EISDIR:參數fd索引的時目錄
二、read和write的正确用法
①不完善的用法一:
int ret = read(fd, buf, len);
if(ret == -1){
exit(-1);
}else if(ret == 0){
close(fd);
}
忽略了 errno 的處理. 仔細看文檔, 函數傳回 -1 不能完全代表 fd 錯誤, 還需要結合 errno。
②不完善用法二:
int ret = read(fd, buf, len);
if(ret == -1){
if(errno == EINTR){
// 怎麼辦?
}else if(errno == EAGAIN){
// 怎麼辦
}
exit(-1);
}else if(ret == 0){
close(fd);
}
EINTR 表示 read() 函數調用被系統中斷了, 調用者和 fd 都沒有問題, 有問題的是作業系統。而 EAGAIN 是在非阻塞 IO 時會出現. 上面的代碼判斷了 errno, 但不知道下一步該怎麼做, 還不行。
③正确用法
while(1){
int ret = read(fd, buf, len);
if(ret == -1){
if(errno == EINTR){
continue;
}else if(errno == EAGAIN){
// 根據你和調用者的約定, 傳回一個數值告訴它再次重試
// 一般是結合 select/epoll 等 IO 多路複用函數
}
exit(-1);
}else if(ret == 0){
close(fd);
}
// proc
break;
}
write函數也有類似用法。