您的位置:首页 > 运维架构 > Linux

linux下服务器程序的几种基本模型-【一】单/多进程模式/多进程池(prefork)模型

2012-10-19 21:38 274 查看
文章来自:/article/5751159.html

服务器的基本模型,不知道这么扯对不对,其实就是linux下服务器和客户端的通信模式。也就是面对客户端如狼似渴的访问需求,服务器该如何快速的响应。

我总结下来有这么几种:

单进程提供服务
多进程提供服务
多进程池服务(prefork)
io复用提供服务(select,poll)
epoll(其实也是一种IO复用)
多线程提供服务
多线程池提供服务
信号驱动提供服务

一一按照自己的想法写出来,还想和大家后续一起探讨下非阻塞io,异步io,共享内存,进程间通信等服务器常用技术。废话不说,直接开始。

单进程提供服务

这种模式只存在于我们的学习中,一个客户端请求由服务器响应后,这个客户完全占有了服务器,这回如何再来一个新客户,他必须等待服务器伺候完现有的这个客户。伺候不完,服务器是不会为新客户提供服务的,这个就是完全占有。

服务器和客户端的行为:

server : bind -> listen -> accept one request -> do request, send response -> close accept fd -> accept next request ....

client : connect -> send request -> wait response -> recieve response -> close connect

服务器的行为,一般是创建一个socket,然后把相关的端口,ip使用bind捆绑到socket上,通过listen监听该端口,当有客户请求到达时,处理请求,给出回应。处理完一个客户请求后,关闭该请求,在处理下一个。

客户端的行为,通过connect连接到server上去,发送请求,等待回应,收到答复,关闭连接。

这种方式的弊端,显而易见,在一个客户请求未处理完毕时,另一个客户必须等待,直到被accept。在web服务这种高并发请求中,这种服务器模型显然不行。

有一个参数这里我一直觉得很诡异,就是listen的第二个参数 backlog ,按照说明 这个参数是在建立三次握手中的连接数和完成网络连接但尚未被accept的连接数的和的最大值,但貌似在各个内核中实现又有所差异。在自己的本中实现了下,貌似超过了还会connect ok 。

贴上我实验用的server代码,为了节约代码量,所有的错误处理均被忽略

?
  可以用 telnet 127.0.0.1 99999 测试,可以看到但一个客户端使用telnet请求时,下一个必须等待。

多进程提供服务

既然一个进程提供服务已经应付不过来,不如多生几个儿子来处理请求,作为老子只管对儿子进行监控就好。多进程应该就是这么个道理。每个请求都fork一个子进程来处理。

这个模式相对于第一种的优点就是 可以对多个请求进行处理,响应及时。

缺点就是每次请求都要生成一个新进程,处理完毕,还要销毁。成本有些高,在并发请求较高的时候,会把cpu耗尽。毕竟进程这个东西还是稍微有些重的东西。

修改程序很简单 ,在 accept后面插入代码即可,插入的代码为

?
 还缺少一步,就是防止子进程成为僵尸进程,要对信号SIGCHLD进行处理,使其在接到该信号后调用waitpid函数 回收子进程。

多进程池的服务

每次请求都生成新进程其实必要性并不大,大部分并发服务器处理的每秒并发量一般最多就在几百左右,因此一般几个或者十几个进程循环提供服务就可以hold住,为了减少每次请求建立新进程的成本,我们的前辈又发明了多进程池(prefork)的模式,预先生成若干进程来处理请求。

见过两种多进程池的实现,一种是父进程只管listen,子进程对每个请求accept。另一种是父进程负责accept,然后把accept后得到的confd句柄传递给子进程。

这里我先说下第一种实现,关于第二种的实现我会在select模式中来说,原因就是第二种模式的实现配合select的效果更佳。

第一种的测试代码为:(这里没有添加错误的处理程序)

?
  

这里在原来有一个比较纠结的地方,就是accept,原来的linux版本会有惊群现象,也就是当一个请求到来时,多个子进程同时在accept阻塞中被唤醒,导致资源消耗过大,这个比较纠结的问题在现在的较新的linux内核中已经解决,另外一个纠结的问题时select的冲突,这个咱们在select中再续。先说到这里把。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: