首先是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网络编程学习笔记之三 -----多进程并发服务端的更多相关文章

  1. linux网络编程学习笔记之五 -----并发机制与线程�

    进程线程分配方式 简述下常见的进程和线程分配方式:(好吧,我仅仅是举几个样例作为笔记...并发的水太深了,不敢妄谈...) 1.进程线程预分配 简言之,当I/O开销大于计算开销且并发量较大时,为了节省 ...

  2. linux网络编程学习笔记之四 -----多-threaded服务器

    对于使用过程中并发.通过实现更轻量级线程. 每个线程都是一个独立的逻辑流. 主题是CPU在执行调度的最小独立单位,这个过程是资源分配单元.当然,这是在微内核操作系统说.总之,这是唯一的一个操作系统内核 ...

  3. linux网络编程学习笔记之二 -----错误异常处理和各种碎碎(更新中)

    errno 在unix系统中对大部分系统调用非正常返回时,通常返回值为-1.并设置全局变量errno(errno.h),如socket(), bind(), accept(), listen(). e ...

  4. 转 网络编程学习笔记一:Socket编程

    题外话 前几天和朋友聊天,朋友问我怎么最近不写博客了,一个是因为最近在忙着公司使用的一些控件的开发,浏览器兼容性搞死人:但主要是因为这段时间一直在看html5的东西,看到web socket时觉得很有 ...

  5. Linux网络编程学习路线

    转载自:https://blog.csdn.net/lianghe_work/article 一.网络应用层编程   1.Linux网络编程01——网络协议入门 2.Linux网络编程02——无连接和 ...

  6. Linux Shell编程学习笔记——目录(附笔记资源下载)

    LinuxShell编程学习笔记目录附笔记资源下载 目录(?)[-] 写在前面 第一部分 Shell基础编程 第二部分 Linux Shell高级编程技巧 资源下载 写在前面 最近花了些时间学习She ...

  7. Linux网络编程学习计划

    由于网络编程是很重要的一块,自己这一块也比较欠缺,只知道一些皮毛,从今天开始系统学习<Linux网络编程>一书,全书分为十四个章节: 第一章   概论   P1-16 第二章   UNIX ...

  8. 多线程编程学习笔记——异步调用WCF服务

    接上文 多线程编程学习笔记——使用异步IO 接上文 多线程编程学习笔记——编写一个异步的HTTP服务器和客户端 接上文 多线程编程学习笔记——异步操作数据库 本示例描述了如何创建一个WCF服务,并宿主 ...

  9. Linux C网络编程学习笔记

    Linux C网络编程总结报告 一.Linux C 网络编程知识介绍: 网络程序和普通的程序有一个最大的区别是网络程序是由两个部分组成的--客户端和服务器端. 客户端:(client) 在网络程序中, ...

随机推荐

  1. 我们究竟什么时候可以使用Ehcache缓存(转)

    一.Ehcache是什么 EhCache是Hibernate的二级缓存技术之一,可以把查询出来的数据存储在内存或者磁盘,节省下次同样查询语句再次查询数据库,大幅减轻数据库压力. 二.Ehcache的使 ...

  2. java实现点卡生成

    点卡主要有2部分:卡号和密码.卡号一般由数字组成,密码就不多说了. java中随机数很强大,大家可以自己查.卡号生成使用java中随机数,密码使用uuid,密码可以自己再加点东西之类的.下面是完整代码 ...

  3. heap表和iot表排序规则不同

    heap 和iot 对比 OBJECT_NAME OBJECT_TYPE --------------------------------------------------------------- ...

  4. SilkTest Q&A 8

    Q72.如何在一个testplan中运行所有的testcase? A72. 1.打开testplan 2.点击Run/Run All Tests菜单,SilkTest开始执行testplan中所有的t ...

  5. Webcast / 技术小视频制作方法——自己动手录制video轻松搞定

    Webcast / 技术小视频制作方法——自己动手录制video轻松搞定 http://blog.sina.com.cn/s/blog_67d387490100wdnh.html 最近申请加入MSP的 ...

  6. qt事件传递过程和处理

    处理监控系统的时候遇到问题,在MainWidget中创建多个子Widget的时候,原意是想鼠标点击先让MainWidget截获处理后再分派给子Widget去处理,但调试后发现如果子Widget重新实现 ...

  7. ORACLE 更改username

    曾经一直常常改动oracle的用户password,但非常少改动username的. 曾经仅仅能创建一个用户1.然后将用户2数据导入到用户1.然后经用户1删掉,这样很麻烦并且耗时,今天就整理了下怎样改 ...

  8. rsyslog+LogAnalyzer 日志收集

    Linux 之rsyslog+LogAnalyzer 日志收集系统 一.LogAnalyzer介绍 LogAnalyzer工具提供了一个易于使用,功能强大的前端,用于搜索,查看和分析网络活动数据,包括 ...

  9. zoj 3870

    题目链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=5518 题意:n个数,从中选出两个数,问这两个数的异或值大于两个数较大 ...

  10. delphi 回调函数

    program Project2; {$APPTYPE CONSOLE} uses SysUtils; type //定义一个对象事件方法 TCallbackFunc = function (i: I ...