通过read()来读取
#include <unistd.h>
ssize_t read(int fd, void *buf, size_t len);
读取成功,返回写入BUF的字节数。
读取失败,返回-1并且设置errno。
如果fd表示的对象无法查找(例如,字符设备文件),则读取总是发生在“当前”位置。
返回值
read()返回一个比len小的正数是合法的。
read()还可能返回0。它只是表示文件位置已经超过了文件中的最后一个有效偏移量,因此没有其他可读取的内容。
在从套接字或设备文件读取的情况下, 对len字节进行读取,但是没有字节可用于读取,调用将阻塞(睡眠),直到有字节读到为止。
有些错误是可以恢复的。例如,如果对read()的调用在读取任何字节之前被信号中断,它将返回−1(0可能与EOF混淆),并将errno设置为EINTR。在这种i情况下,可以并且应该重新提交所读的内容。
调用返回−1,并将errno设置为EAGAIN。这表示由于当前没有可用的数据,因此读取将阻塞,请求应在稍后重新发出。这只发生在非阻塞模式。
读取所有的字节
int readAllBytes(int fd, char* buf, size_t len)
{
if(len > SSIZE_MAX){
len = SSIZE_MAX;
}
ssize_t ret = 0;
while(len != 0 && (ret = read(fd, buf, len)) != 0){
if(ret == -1){
if(errno == EINTR){
continue;
}
perror("read");
break;
}
len -= ret;
buf += ret;
}
}
非阻塞读取
int readNonBlocking(int fd, char* buf, size_t len)
{
if(len > SSIZE_MAX){
len = SSIZE_MAX;
}
ssize_t nr;
start:
nr = read(fd, buf, len);
if( nr == -1){
if(errno == EINTR){
goto start;
}
if(errno == EAGAIN){
/*resubmit later and do something else first*/
}
else{
perror("read");
}
}
}
如果读取len的值为0,那么read()调用会立即返回0.
通过write()来写入
#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);
由不支持查找的对象(例如字符设备)支持的文件总是从“头”开始写。
正确的调用write的方式:
int writeAllBytes(int fd, char *buf, size_t len) {
ssize_t ret, nr;
while(len != 0 && (ret = write(fd, buf, len)) != 0){
if(ret == -1){
if(errno == EINTR){
continue;
}
perror("write");
break;
}
len -= ret;
buf += ret;
}
}
在附加模式下(通过O_APPEND)打开fd时,不会在文件描述符的当前文件位置进行写入。相反,它们发生在文件的当前结尾。
Append Mode
Append Mode避免了此问题。它确保文件位置始终设置为文件的末尾,因此所有写操作都要附加,即使有多个写入器。你可以把它看作是 对每个写入请求之前的文件位置进行原子更新。然后,文件位置被更新到新写入数据的结尾的点。这将不会影响下一次写入的调用,因为它自动更新文件位置,但下一次调用read()可能会有一些奇怪的原因。
Append Mode模式对某些任务(如前面提到的日志文件编写)有很多意义,但对其他任务几乎没有意义。
非阻塞write()
当FD以非阻塞模式打开(通过O_NONBLOCK),并且发出的写入通常会阻塞时,写()系统调用返回−1并将errno设置为EAGAIN。请求应在以后重新发布。通常,这不会出现在常规文件中。
write的写入大小限制
小于SSIZE_MAX,不然结果是未定义的
即使应用程序可能认为写入已经成功,但在系统崩溃的情况下,数据将永远无法到达磁盘。