码迷,mamicode.com
首页 > 系统相关 > 详细

linux网络编程学习笔记之三 -----多进程并发服务端

时间:2014-06-16 12:19:31      阅读:248      评论:0      收藏:0      [点我收藏+]

标签:网络编程   linux   多进程   

首先是fork()函数,移步APUE 8.3.  比较清晰的解释可以参考http://blog.csdn.net/lingdxuyan/article/details/4993883http://www.oschina.net/question/195301_62902

补充一点是:fork返回后,原进程中的每个文件或套接口描述符的引用计数加1(相当于被多打开了一次),每调用一次close,引用计数减1,只有当引用计数减到0时才会真正关闭该套接字。

可执行文件被linux执行的唯一方式就是调用exec,把当前进程映像替换成新的程序文件,从该程序的main开始执行。

linux典型做法是,fork之后开始exec,通常用COW技术。


关于僵尸进程

进程在exit()结束后,进程表中任然会保留如进程号、退出状态、运行时间等信息。虽然它已经放弃了内存空间,不能被调度。由于linux对进程数量有限制,过多的僵尸进程将会占用可用进程号,导致新的进程无法生成,所以必须及时地清除。当时我就纳闷了。。。那僵尸进程存在的意义何在?答:保留子进程退出的状态,等待父进程收尸时确定死因。(在父进程中调用各种宏:WEXITSTATUS(status)等)

处理方式:

1、父进程调用wait()或waitpid()来等待子进程结束,当然这会导致父进程挂起

2、如果父进程并不关心子进程何时结束,可以调用signal(SIGCHLD,SIG_IGN)来通知内核,内核会在子进程结束后自动回收。注意:此方法不是可移植的,Stevent在UNP5.9中提到,这个处理并不是POSIX标准,只是在某些系统上可用

3、同样使用异步处理的方式,信号处理函数signal,安装handler来处理SIGCHLD信号。这样,父进程可以在handler中调用wait()进行回收。但是由于相同的信号不排队的原因,要注意处理同时提交的信号,即对同时终止的子进程做处理。UNP5.10详细地讨论了该问题,并给出了安全的方法。

4、通过给信号处理函数设置SA_NOCLDWAIT标志。使调用进程的子进程终止时不创建僵尸进程(APUE10.14)

signala.sa_handler = SIG_IGN;
signala.sa_flags = SA_NOCLDWAIT;
sigemptyset(&signala.sa_mask);
sigaction(SIGCHLD, &signala, NULL);

5、由于进程的特性,在其父进程退出后,子进程将被过继给init进程,如果子进程已是僵尸进程,init直接将其回收,因为它的管理者父进程已经不存在了,init不关心其死因。因此可以采取连续两次fork()的方式,子进程在fork之后直接退出,由孙进程来进行处理,孙进程结束后,资源被init回收。但注意子进程的清理还得由父进程来完成。


信号处理:

在此小结下,每个内核的信号,操作系统都有默认的处理方式,比如前面的SIGCHLD,默认是忽略,因此不处理会留下僵尸。我们可以自己定义函数来处理信号,注意SIGKILL和SIGSTOP是不能被捕获和忽略的。

信号处理函数是进程相关的,为全进程所有线程公用。

信号处理函数中,尽量不要使用printf这样的不可重入函数(UNP11.18)

一个信号被信号处理函数响应,在处理过程中,该信号被屏蔽。标准的信号实现没有排队的功能,所以信号可能会被丢失,多个连续的信号来不及处理。如果通过sa_mask设置了信号集,集合中的信号也会被阻塞。

建立信号处理函数的POSIX方法是调用sigaction(),简单些直接调用signal(),虽然它不是POSIX函数,但多数平台用signal()来实现sigaction(),以实现向后兼容。


下面是一个简单的多进程服务端程序:

#include"simon_socket.h"

#define SERV_PORT 12345

extern pid_t waitpid(pid_t, int *, int);

void handler(int signo)
{
	pid_t pid;
	int status;
	while ((pid = waitpid(-1, &status, WNOHANG)) > 0)
	{
		printf("Child process %d terminated\nThe WEXITSTATUS return code is %d \nThe WIFEXITED return code is %d\n", pid, WEXITSTATUS(status), WIFEXITED(status));
	}
}

int main()
{
	int sockfd, acfd;
	struct sockaddr_in client_addr;

	size_t sin_len = sizeof(client_addr);

	sockfd = init_tcp_psock(SERV_PORT);

	signal(SIGCHLD, handler);

/*   another method :
 *
	struct sigaction signala;
	signala.sa_handler = SIG_IGN;
	signala.sa_flags = SA_NOCLDWAIT;
	sigemptyset(&signala.sa_mask);
	sigaction(SIGCHLD, &signala, NULL);
*/	
	while (1)
	{
		if ((acfd = accept(sockfd, (struct sockaddr *)&client_addr, &sin_len)) == -1)
		{
			perror("Accept request failed: ");
			return 1;
		}
		else
			printf("Get a connection from %s:%d !\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));

		pid_t pid;
		if ((pid = fork()) > 0)
		{
			close(acfd);
			continue;
		}
		else if(pid == 0)
		{
			close(sockfd);
			process_client(acfd, &client_addr);
			close(acfd);
			exit(0); 
		}
		else
		{
			perror("Fork error");
			exit(0);
		}
	}
	close(sockfd);
	return 0;
}

代码中一些调用函数的实现,移步我的github:https://github.com/simon-xia/lnp


linux网络编程学习笔记之三 -----多进程并发服务端,布布扣,bubuko.com

linux网络编程学习笔记之三 -----多进程并发服务端

标签:网络编程   linux   多进程   

原文地址:http://blog.csdn.net/simon_xia_uestc/article/details/20076661

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!