Linux网络编程等的解释和waitpid()()
在客户端断开连接,在服务器端大量僵尸进程。这是因为SIGCHLD信号是在服务器子进程终止发送给父进程,父进程的默许和默认的信号。为了避免僵尸进程的出现,每当我们创建子进程,主要过程需要等待子进程返回,以便清理过程。为此,我们将SIGCHLD信号处理功能的服务器程序。
复制代码代码如下所示:
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#定义serv_port 1113
#定义listenq 32
该#定义1024
***连接处理功能
无效str_echo(int fd);
无效
sig_chld(int该)
{
pid_t PID;
int属性;
pid =等待(统计);获取子进程。
printf(%d终止孩子
返回;
}
int
主要(int argc、argv { } { char *)
国际listenfd,connfd;
pid_t childpid;
socklen_t clilen;
结构sockaddr_in servaddr;
结构sockaddr_in cliaddr;
/ /结构sockaddr_in servaddr;
/ /结构sockaddr_in cliaddr;
如果((listenfd =插座(af_inet,sock_stream,0))= = 1){
Fprintf(stderr,套接字错误:%s
出口(1);
}
服务器结构填充SOCKADDR / * * /
BZero(servaddr,sizeof(servaddr));
servaddr.sin_family = af_inet;
servaddr.sin_addr.s_addr = htonl(inaddr_any);
servaddr.sin_port = htons(serv_port);
(SIGCHLD信号,sig_chld); / / SIGCHLD信号处理
捆绑listenfd描述符 / * * /
如果(绑定(listenfd,(struct sockaddr *)(servaddr),sizeof(struct sockaddr))= = 1){
Fprintf(stderr,绑定错误:%s
出口(1);
}
监控listenfd / * * /描述符
如果(听(listenfd,5)= = 1){
Fprintf(stderr,听错误:%s
出口(1);
}
为(;){
clilen = sizeof(cliaddr);
服务器被阻塞,构建到客户端程序连接为止。
如果((connfd =接受(listenfd,(struct sockaddr *)(cliaddr),clilen))<0){
*当一个子进程终止的sig_chld信号处理函数执行,
当函数返回时,接受系统调用会返回一个eintr误差,
一些内核会自动重新启动中断的系统调用,为了方便移植,将考虑eintr治疗。
如果(我= = eintr)
继续;
Fprintf(stderr,接受错误:%s
出口(1);
}
在与客户机建立连接之后。
如果((childpid)=叉()= = 0){ / * * /子过程
关闭(listenfd); / * * /密切监测插座
str_echo(connfd); / *客户端请求的处理
出口(0);
}
关闭(connfd); / *父进程关闭连接插座,继续等待其他连接的到来。
}
}
无效str_echo(int sockfd){
ssize_t n;
中字符{该};
再一次:
而((n =读(sockfd,buf,该))>0)
写(sockfd,buf,N);
如果(n<0 errno = = eintr) / /闯入
去了;
否则如果(n<0){错误
Fprintf(stderr,读取错误:%s
出口(1);
}
}
修改后的代码,当客户端断开连接,服务器的父进程的子进程收到SIGCHLD信号,然后执行sig_chld功能和清理的子过程,所以不会有僵尸的过程。在这一点上,当客户端断开时,积极,服务器端将输出以下信息:
孩子12306终止
等待和waitpid
在sig_chld功能在上述程序中,我们使用等待()明确终止子进程。也有类似的功能,wait_pid。让我们看一看这两个函数原型:
pid_t等待(int *状态);
pid_t waitpid(pid_t PID,int *状态,INT选项);
正式说明:所有这些系统调用都用于等待调用过程中的子类的状态更改,并获取状态更改的孩子的信息。一个国家的变化是:孩子在启发孩子停止;信号;或孩子恢复信号。对于被终止的孩子,执行等待可以让系统释放与孩子相关的资源,如果没有等待,那么执行终止的孩子仍然处于僵尸状态(请参阅下面的注释)。
等待和waitpid的区别与联系:
等待()系统调用暂停调用过程的执行直到一个
Waitpid(1,状态0);
的waitpid(系统)叫暂停调用进程执行直到孩子通过PID参数指定的状态已改变。默认情况下,waitpid(等),只为终止的孩子,但这种行为是可通过选择参数,如下所述。
就是等待()系统调用将挂断呼叫过程直到其任何子进程终止,呼叫等待的效果(状态)是调用waitpid相同(1,状态0)。
Waitpid()将挂起调用进程的PID参数的变化,直到指定的进程状态,默认情况下,waitpid()只等待子进程的终止状态。如果你需要,你可以处理非通过设置选项的值为终止状态:
期权的价值为零或以上。
wnohang立即返回,如果没有孩子的存在。
wuntraced也返回如果孩子已经停止(但不追查,但(2))。
wcontinued(因为Linux 2.6.10)如果一停孩子也已返回
等待一个非终止状态。
现在举个例子来看看之间的差异和waitpid等()()。
通过修改客户端程序,在客户端程序中设置5个套接字来立即连接到服务器,如下图所示(附加代码):
复制代码代码如下所示:
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#定义serv_port 1113
该#定义1024
无效str_cli(文件,int sockfd);
int
主要(int argc、argv char *)
{
int i,sockfd { 5 };
结构sockaddr_in servaddr;
如果(argc!= 2){
Fprintf(stderr,的用法:tcpcli ;
出口(0);
}
对于(i = 0;i < 5;+ i)建立的{服务器>和服务器五连接到服务器进程以创建5个孩子
如果((sockfd {我} =插座(af_inet,sock_stream,0))= = 1){
Fprintf(stderr,套接字错误:%s
出口(1);
}
客户机服务器数据填充
BZero(servaddr,sizeof(servaddr));
servaddr sin_family = af_inet;
servaddr sin_port = htons(serv_port);
如果(inet_pton(af_inet,argv { 1 },servaddr。sin_addr)<= 0){
Fprintf(stderr,inet_pton错误:%s
出口(1);
}
*客户端启动连接请求。
如果(连接(sockfd {我},(struct sockaddr *)(servaddr),sizeof(struct sockaddr))= = 1){
Fprintf(stderr,连接错误:%s
出口(1);
}
}
str_cli(stdin,sockfd { 0 }); / *只有第一个套接字与服务器通信。
出口(0);
}
无效
str_cli(文件,int sockfd)
{
int nbytes = 0;
焦sendline {该},recvline {该};
而(fgets(sendline,该,FP)!= NULL)从标准输入行读取
写(sockfd,sendline,strlen(sendline)); / /线路发送到服务器
如果((nbytes =读(sockfd,recvline,该))= = 0){ / /读取从服务器发送sockfd数据
Fprintf(stderr,str_cli:服务器提前终止;
出口(1);
}
recvline { nbytes } = 0;
作用是:将内容(recvline,stdout);
}
}
当客户端终止,所以打开描述符由内核自动关闭,所以在同一时间的5种基本连接,相当于5片也会发送到服务器,这将导致在同一时间终止5服务器进程,导致5 SIGCHLD信号几乎同时传递到服务器的父过程,如下图所示:
也就是说,几乎在同一时间,5个SIGCHLD信号给父进程,这将导致僵尸进程的存在。因为UNIX一般不排队的信号,这导致5 SIGCHLD提交,只执行一sig_chld功能,而剩下的四个子过程成为僵尸进程。在这种情况下,做正确的事是调用waitpid(),不等待()。
因此,我们在最后一个服务器端代码中对信号处理函数做了一些改变,并将其更改为以下内容:
复制代码代码如下所示:
无效
sig_chld(int该)
{
pid_t PID;
int属性;
而((PID = waitpid(- 1,统计,WNOHANG))>0)
printf(%d终止孩子
返回;
}
在这一点上,我们已经解决了网络编程中可能遇到的三种情况:
1。这必须捕获SIGCHLD信号当孩子出生时,代码段:(SIGCHLD信号,sig_chld);
2。当信号被捕获,被中断的系统调用必须被处理。代码段:如果(我= = eintr)继续;
的3.sigchld信号处理函数必须写正确防止僵尸进程。代码段:当(((PID = waitpid(- 1,统计,WNOHANG))>0)