Unix网络编程卷1读书笔记
随性记录
fork函数(P90,4.7节)
该函数是Unix中派生新进程的唯一方法。
fork函数的特点是:调用它一次,它却返回两次。
父进程中返回派生进程的进程ID号;
子进程中返回0;通过返回值可以判断当前进程为子进程还是父进程。
子进程可以通过调用getppid取得父进程ID。父进程只能通过fork返回值来记录各个子进程。
格式如下:
int pid=fork();
父子进程的描述符–引用计数(P92,4.8节)
针对以下代码(伪代码)
socket();//创建套接字
Bind();//绑定
Listen();//监听
for(;;)
{
connfd=Accept(listenfd);
if(pid=fork()==0)
{
//子进程执行
close(listenfd);
doit(connfd);//处理响应
close(connfd);
exit(0);
}
close(connfd);
}
在子进程中显示关闭已连接套接字并未必需。
因为最后的exit调用,进程终止处理的工作就包括关闭所有由内核打开的描述符。
对一个TCP套接字调用close会导致发送一个FIN,开始四次握手。
从这段代码可以看出,父进程与子进程都对connfd调用了close函数,而connfd在全局中应该只有一个。
问题1:为何父进程关闭connfd后,子进程还能对connfd进行响应?
每个文件或者套接字都有一个引用计数。引用计数在文件表项中维护。它表示当前打开着的引用该文件或者套接字的描述符个数。
上一段代码中,经过fork函数后,listenfd和connfd的引用计数均变为2。在引用计数大于1时,任何关闭操作都只能使引用计数-1,不会真正关闭。
下面几组图描述了整个过程
在服务器未接收到连接请求时,将阻塞于accept函数(阻塞模式)。一旦接受到连接请求,从accept返回后,就有了下面的状态。
接下来,调用了fork函数。
之后,子进程关闭listenfd,父进程关闭connfd。
这是两个套接字所期望的最终状态。子进程处理客户连接,父进程继续监听。
问题2:为什么父进程一定要关闭已连接套接字?
这是因为如果父进程不调用close。首先父进程最终将耗尽可用描述符,因为任何进程在任何时刻可拥有的打开着的描述符数量通常是有限的。还有更重要的一点,没有一个客户连接会被终止,因为子进程关闭套接字只会使引用计数-1,而父进程永远不关闭的话,将导致TCP连接始终打开着。