首先是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. JSP的学习(2)——语法知识一

    上一篇<JSP的学习>讲述了JSP的一些基础知识和底层原理,本篇将来学习JSP所需掌握的语法知识等. JSP的语法主要包括以下几个部分的内容: 1)         JSP模板元素 2)  ...

  2. jquery用div模拟一个下拉列表框

    原文 jquery用div模拟一个下拉列表框 今天分享一个用我自己用jquery写的,用div模拟下拉列表select,这个效果网上有很多,但是写一个有自己思路的代码效果,更有成就感,先看截图: 自我 ...

  3. 爬虫总结_java

    基于webmagic的爬虫项目经验小结 大概在1个月前,利用webmagic做了一个爬虫项目,下面是该项目的一些个人心得,贴在这里备份: 一.为什么选择webmagic? 说实话,开源的爬虫框架已经很 ...

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

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

  5. 循环灯控制器,该控制器控制红、绿、黄三个发光管循环发亮(VHDL语言)

    设计一个循环灯控制器,该控制器控制红.绿.黄三个发光管循环发亮.要求红发光管亮2秒,绿发光管亮3秒,黄发光管亮1秒.(假设外部提供频率为1MHz的方波信号) library ieee; use iee ...

  6. 【IUML】支持向量机SVM

    从1995年Vapnik等人提出一种机器学习的新方法支持向量机(SVM)之后,支持向量机成为继人工神经网络之后又一研究热点,国内外研究都很多.支持向量机方法是建立在统计学习理论的VC维理论和结构风险最 ...

  7. navicat for mysql 如何将表ID排序重1开始?

    如果是自增字段,删除数据,他是根据原来的继续往后排的 1.你可以删除这个字段,重新建立个自增字段就可以了 2.好像也可以重新设置排序起始 alter table table_name AUTO_INC ...

  8. 无锁队列--基于linuxkfifo实现

    一直想写一个无锁队列,为了提高项目的背景效率. 有机会看到linux核心kfifo.h 原则. 所以这个实现自己仿照,眼下linux我们应该能够提供外部接口. #ifndef _NO_LOCK_QUE ...

  9. codeforces 598D Igor In the Museum

    题目链接:http://codeforces.com/problemset/problem/598/D 题目分类:dfs 题目分析:处理的时候一次处理一片而不是一个,不然会超时 代码: #includ ...

  10. 给工程师的 10 条哲理(浅薄者迷信运气,强者相信因果,软件复制成本为零,文凭不重要,AWS使得创业成本为零,每个手机都是口袋里的强大电脑)

    无论是主题分布式数据库,微服务,Soylent,尤伯杯,或者矮人要塞,我们试图从物质分离出来炒作,推迟叙事的客人.与尊重有软件工程日报的社论部分客观性. 一位渠道的成员说,“当软件工程每日的意见公布, ...