zoukankan      html  css  js  c++  java
  • 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
    The WEXITSTATUS return code is %d 
    The WIFEXITED return code is %d
    ", 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 !
    ", 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


  • 相关阅读:
    oracle 中增加、修改、删除字段
    Oracle 中int , number的区别
    [转]信息系统项目管理师考试论文写作技巧
    项目论证
    java中异步计算之Future
    vmstat例子
    页面重构工作者的必备素质,转载
    办公室人员对号入座太经典啦!
    大家多注意身体!
    经典!牛人汽车防盗秘笈
  • 原文地址:https://www.cnblogs.com/bhlsheji/p/5126191.html
Copyright © 2011-2022 走看看