Chinaunix首页 | 论坛 | 博客
  • 博客访问: 869404
  • 博文数量: 82
  • 博客积分: 2283
  • 博客等级: 大尉
  • 技术积分: 2007
  • 用 户 组: 普通用户
  • 注册时间: 2012-07-15 22:19
文章分类

全部博文(82)

文章存档

2012年(82)

分类:

2012-11-02 18:56:26

原文地址:socket缓冲区死锁问题 作者:buaa_zhaoc

昨天为了比较UNIX socket跟TCP/IP socket的性能,写了一个echo server简单的比较了两者的性能,通过初步不详细的结果,能够看出UNIX的网络交互性能确实要比TCP/IP性能高。但是,除了这个问题解决了之后,还遇到了自己意想不到的收获。

echo server中的处理请求示例代码如下所示,这也是从unix网络编程第五章摘抄的代码 如下:

  1. void str_echo(int sockfd)
  2. {
  3.     ssize_t n;
  4.     char buf[MAX_LEN];

  5. again:
  6.     while ((n = read(sockfd, buf, MAX_LEN)) > 0)
  7.     {
  8.         write(sockfd, buf, n);
  9.     }
  10.     
  11.     if (n < 0 && errno == EINTR)
  12.         goto again;
  13.     else if (n < 0)
  14.         perror("str_echo: read error:");
  15. }
这段服务器端代码其实没什么问题,只是在write函数返回的时候没有做判断,但这个小瑕疵并不影响我后续说的主要问题。
这段代码在一般的情形下能够很好的服务客户端请求,使用telnet能够看到echo回来的字符串。

但是,在客户端请求一个大字符串长度的时候(比如10000000),这段程序就会造成整个客户端与服务器之间的死锁。

在socket中,一般都会为发送与接受双方向维护缓冲区,如果发送段发送数据时,如果缓冲区有空闲的地方,那么就是将数据放入到发送缓冲区中,如果缓冲区没有空闲区域,那么根据socket的类型决定相应操作:
  1. 如果socket是阻塞的write,那么write将阻塞,直到发送缓冲区有数据出现。
  2. 如果socket是非阻塞的write,那么write将立即返回,errno = EAGAIN或者EWOULDBLOCK,由调用方决定后续操作。
如果client端使用的是阻塞write(server_fd, big_buf, 10000000),写一个巨大的buffer,client端在内核部分会将这么大的buffer分批次发送,server这端接受到被拆分的数据后直接调用自己的write函数写回给client端,数据则是先到了server的发送缓冲区,然后经过网络又到了client的接受缓冲区,但是由于client段还在write函数中,并没有read,所以数据就只能停留在自己的接收缓冲区中。

这就像一个水龙头源源不断的出水,水流向一个封着口的管子中,水龙头必须把水放完了之后,才能打开管子的封口把水放出来。管子的容量是有限的,但是水的量是不确定的。如果水的量比较少,那么这套系统可以正常工作,如果水量大于管子的容量了,那么这套系统就死锁了。

  1. 首先,被填满的就是client的接受缓冲区,其实也不是填满,由于TCP的滑动窗口,当达到了窗口限制之后,发送端就不再发送了。也许缓冲区会比窗口大,这个没看过具体实现,尚不做硬性关联。总之,client接受被填满了。
  2. 然后,server端的发送缓冲区由于数据越来越多,又不能发给client,总之慢慢的就被填满了。带来的结果就是,server的程序被阻塞到write函数中了,这样导致server就不能够read了。
  3. 接着,server程序被阻塞到write函数中了,那么也自然就不会read了,慢慢的,慢慢的,server端的TCP接收窗口也就满了,再也不能从client端读数据了。管子就此也就满了三截了。
  4. 最后,因为server端的TCP接受窗口已经满了,client端的内核就不能将数据发给server段了,就只能在自己的发送缓冲区存了,渐渐的,渐渐的,client端的发送缓冲区也满了。但是,这个时候的write还没有返回,client端也阻塞在了write中。
  5. 最后的最后,系统就死锁了。:-),这应该是分布式中最简单的死锁了……

昨天遇到那个问题,今天用了一天的闲暇时光总算是把这个问题想通了。其实这个死锁问题还是典型的,满足死锁发生的各种条件。最根本原因就是系统的资源无法满足,然后又没能够合理的安排。最简单的解决办法就是客户端这边发一点,读一点,这个问题也就避免了,使得不会一次请求那么多的资源,这个问题也就解决了。

阅读(3381) | 评论(0) | 转发(0) |
0

上一篇:java实现web服务器

下一篇:HTTP1.1 特性

给主人留下些什么吧!~~