1,write原型,参数及返回值。
#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t nbytes);
------函数write 只是将用户进程中的数据拷贝到内核缓冲区中,拷贝数据的大小取决于内核缓冲区的大小与nbytes。
参数:
fd:标示某个套接字;
buf:进程中需要往对端发送的数据;
nbytes:需要发送的数据长度。
返回值:
-1:相应的错误码会写到error中。
error :EAGAIN 或 EWOULDBLOCK,表示缓冲区已满,此时无法写入,需要等待下次机会。(EAGAIN和EWOULDBLOCK 两者本质一样,只是分别用于不同的系统,前者是gnu,即linux,后者是bsd系统,都表示缓冲区已满,无空间可写)
error:EPIPE,这是一种典型的错误场景,当client A 已经关闭了,server B 由于网络延迟等原因未察觉,依然向这个已关闭的fd,执行write时,内核会向B 返回 EPIPE错误码,并产生SIGPIPE,默认情况下,进程对该信号的动作是自动退出,所以一般服务器需要处理该信号。
>0:写入内核缓冲区的数据大小。
=0:返回一般不会为0,根据第3个参数的值,如果为0,则表示啥都不执行,write返回0。
2,write 在阻塞和非阻塞下的区别。
- 阻塞下
当内核缓冲区大小不足以容纳需要写入的 nbytes,write一直阻塞等待,直到缓冲区大小 >= nbytes 时,write一次性copy,等待的过程中,有可能会被信号中断。
当内核缓冲区大小能够容纳需要写入的nbytes,write copy到缓冲区,并返回 nbytes。
- 非阻塞
坚持的原则是有多少写多少,如果内核缓冲区空间足够,同阻塞情况,一次性copy;如果缓冲区不够,则缓冲区的大小是有多少就写入多少,并返回该值,显然返回值小于nbytes,此种情况需要循环的方式将nbytes数据写到缓冲区;如果缓冲区是满的,则返回-1,并置error值为EAGAIN 或 EWOULDBLOCK。
3,write 的循环写入。
int nwrite(int fd, char *buf, int nbytes)
{
int len = 0;
char *ptr = buf;
int left = nbytes;
while(left)
{
len = write(fd, ptr, left);
if(len < 0)
{
if(error == EAGAIN)
len = 0;
else
return -1; //真正的错误
}
if(len == 0)
{
break;
}
left -= len;
ptr += len;
}
return nbytes;
}
4,write 遗留几个问题?
write非阻塞的时候需要检测中断吗?
write一次性能最大写入缓冲区多少字节?
5,总结
write | 阻塞下 | 非阻塞 |
缓冲区满 | 一直等待 | 返回-1,并置error为EAGAIN |
有缓冲区,但不够指定写的大小 | 一直等待,直到有足够空间 | 写入一部分,并返回该写入的部分大小 |
有缓冲区,而且大小也够 | 一次性写入 | 一次性写入 |