底层文件访问之之一:write系统调用
说明:本文主要是对man 帮助文档和《linux程序设计》第四版的摘录,部分理解来源于个人,请酌情参考。
在linux学习过程中,如果我们想学习如何使用库函数或者系统调用,都会想赶快查一下?你通常会google,百度,还是使用man命令?其实对于一个初级程序员,学习使用man命令是很有益的,我们看到的很多网上的说明都是对man帮助文档的翻译。所以,如果你英文不是很差,就自己来翻译一下你想要查询的信息吧!
打开linux的控制台,我们输入“man 2 write”:会进入帮助文档。
Write
每个运行中的程序被称为进程,它有一些与之关联的文件描述符,通常我们定义他们为fd。这是一些小值整数。你可以通过它们访问打开的设备或者文件。有多少文件描述符可用取决于系统的配置情况。当一个程序开始运行的时,它一般会有3个已经打开的文件描述符:
0:标准输入
1:标准输出
2:标准错误
除此之外我们可以通过调用open,把其他文件描述符与文件和设备相关联。一个自动或者手动打开的文件描述符就可以使用write系统调用来创建一些简单的程序了。
write:系统调用write的作用是把缓冲区buf的前n bytes 个字节写入与文件描述符fd相关联的文件中。
概要:
write函数在<unistd.h>
头文件中定义。
原型是:ssize_t write(int fd, const void *buf, size_t count);
说明:
write()函数尝试从buf开头的缓冲区中读取count个字节,并将其写入到文件描述符fd中。
写入的字节数可能会小于count。比如:底层的物理截止没有足够的空间,或者遇到RLIMIT_FSIZE 资源限制,或者函数调用的时候被信号打断,没有写完。
对于可搜索文件(意思是可以使用lseek函数的文件,一般常规文件都可以),写入的位置是当前的文件偏移量上,写完之后文件偏移量按字节递增。如果这个文件的打开方式是使用open函数,参数是“O_APPEND”,那么文件的偏移量就被设置到文件的末尾。这时候写入到文件,就属于在最后追加。
我们在调整文件偏移量并做写入数据时,要保证是原子操作,即不被中断,环境也不改变。
符合POSIX的文件系统,在write之后调用read,读出来的是刚写入的新数据。并不是所有的文件系统都是这样。
**ssize_t:**有符号整型,与long类似。typedef long size_t
size_t:无符号的ssize_t,:typedef unsigned long size_t
返回值
调用成功返回写入的字节数,文件指示符指到对应的位置。
调用失败返回-1。错误码保存到全局变量errno中。
如果count参数是0,fd指示一个常规文件,函数如果检测到某一种错误会返回一个错误状态。如果没有检测到会返回0。如果count参数是0,fd指示一个非常规文件中,结果不确定。
错误码
EAGAIN:文件描述符指向一个非套接字的文件,并且已经被标记为非阻塞。这时候如果写阻塞,返回这个错误。
EAGAIN /EWOULDBLOCK:文件描述符指向一个套接字的文件,并且已经被标记为非阻塞。然后发生写阻塞。这两个错误码都有可能会被设置,所以在检查的时候应该二者都检查。
EBADF:fd不是一个有效的文件描述符,或者没有打开。
EDESTADDRREQ:fd指向了一个套接字,而这个套接字没有使用connect连接过。
EFAULT :buf在你可以访问的地址空间之外
EFBIG :当试图写入的数据超过文件的最大限制的时候。
EINTR :写入被一个信号打断。
EINVAL:fd指向的文件是一个不稳定的写入对象,或者文件在打开的时候带有O_DIRECT标志,或者buf,count指定的值,当前文件的便宜位置没有适当的对齐。
EIO:I/O错误。例如,当进程处于后台进程组中,试图从它的控制tty读取时,或者忽略SIGTTIN时,阻塞SIGTTIN时,或者它的进程组成为孤儿时,就会发生这种情况。当从磁盘或磁带读取时存在低级I/O错误时,也可能发生这种情况。
ENOSPC :没有包含fd的设备没有数据空间
EPIPE:fd指示的是管道或者套接字,其已经关闭,而我们还要写入。出现这种情况的时候,系统会收到一个SIG_PIPE信号,如果没有处理这个信号,可能会中断程序。
其他错误可能会发生,这取决于连接到FD的对象。
注意
一次写入成功并不能保证磁盘上数据的有效性。甚至不能保证磁盘上保留这些数据与否。唯一可以确保的方式是在调用write之后调用fsync函数
如果在开始写之前,打断write会返回EINTR,一旦已经开始写入,再打断,就会返回写入的字节数,表示写入成功。
建议
学习了write,还可以学习一下:
close(2), fcntl(2), fsync(2), ioctl(2), lseek(2), open(2), pwrite(2),read(2), select(2), writev(2), fwrite(3)
(2):表示man手册的第二部分。
测试代码
#include <unistd.h>
#include <stdlib.h>
int main(int argc,char *argv[])
{
if((write(1,"Here is some data\n",18)!=18))
write(2,"A write error has occurred on file descriptor 1\n",46);
exit(0);
}
运行结果:(控制台输出)
Here is some data