最近空闲时间重新仔细看了一下memcached的使用说明文档,硬着头皮看了一点源码,有时候看到一些晦涩的c函数感觉实在恍惚只能跳过。不过也不算是全无收获,终于LZ还敢再看c语言,终于LZ又看起了c语言,终于近期的睡眠质量明显好了很多。扯淡到此结束,下面记录一下自己的学习心得。

一、Unix Daemon Process

memcached的守护进程机制使用经典的Unix daemon模式(daemon.c),它的实现部分源码如下:

memcached daemon.c
#if defined __SUNPRO_C || defined __DECC || defined __HP_cc
# pragma ident "@(#)$Header: /cvsroot/wikipedia/willow/src/bin/willow/daemon.c,v 1.1 2005/05/02 19:15:21 kateturner Exp $"
# pragma ident "$NetBSD: daemon.c,v 1.9 2003/08/07 16:42:46 agc Exp $"
#endif#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>#include "memcached.h"int daemonize(int nochdir, int noclose)
{int fd;switch (fork()) { /*fork一个子进程*/case -1:return (-1);case 0:break;default:_exit(EXIT_SUCCESS);/*终结父进程*/}if (setsid() == -1)/*创建新的session(setsid),使当前进程成为该session的头进程*/return (-1);if (nochdir == 0) {if(chdir("/") != 0) {/*改变工作目录到“/”根目录*/ perror("chdir");return (-1);}}if (noclose == 0 && (fd = open("/dev/null", O_RDWR, 0)) != -1) {/*重定向标准输入输出到/dev/null*/if(dup2(fd, STDIN_FILENO) < 0) {/*复制STDIN_FILENO、STDOUT_FILENO和STDERR_FILENO的描述符到fd*/perror("dup2 stdin");return (-1);}if(dup2(fd, STDOUT_FILENO) < 0) {perror("dup2 stdout");return (-1);}if(dup2(fd, STDERR_FILENO) < 0) {perror("dup2 stderr");return (-1);}if (fd > STDERR_FILENO) {if(close(fd) < 0) {perror("close");return (-1);}}}return (0);

具体工作流程处理如下:

看源码和处理流程图感觉好像也是平淡无奇。学校里老师讲操作系统,必然提到我们所熟知的单用户单任务操作系统PC DOS(Disk Operating System), 单用户单任务操作系统是指一台计算机同时只能有一个用户在使用,该用户一次只能提交一个作业,一个用户独自享用系统的全部硬件和软件资源。和DOS截然不同的是...哎,说来话长,Unix在二十世纪七十年代就发明了fork函数,真正实现多任务的操作系统,这样Unix系统可以多人多任务地并行处理。所以站在操作系统发展的历史角度,每个看上去很简单的逻辑都是非常牛的跨越,还是觉得很玄乎,这里有必要抄一段daemon的概念来给自己解惑一下。

1、什么是Daemon Process(守护进程)
Daemon(守护进程)是运行在后台的一种特殊进程。它独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。它不需要用户输入就能运行而且提供某种服务,不是对整个系统就是对某个用户程序提供服务。*nix系统的大多数服务器就是通过守护进程实现的。常见的守护进程包括系统日志进程syslogd、 web服务器httpd、邮件服务器sendmail和数据库服务器mysqld等(看到这里会不会产生“这tmd不就是windows服务”的错觉?)。守护进程一般在系统启动时开始运行,除非强行终止,否则直到系统关机都保持运行。守护进程经常以超级用户(root)权限运行,因为它们要使用特殊的端口(1-1024)或访问某些特殊的资源。一个守护进程的父进程是init进程,因为它真正的父进程在fork出子进程后就先于子进程exit退出了,所以它是一个由init继承的孤儿进程。守护进程是非交互式程序,没有控制终端,所以任何输出,无论是向标准输出设备stdout还是标准出错设备stderr的输出都需要特殊处理。

2、工作原理
*nix守护进程的工作模式是服务器/客户机(Server/Client),服务器在一个特定的端口上监听(Listen)等待客户连接,连接成功后服务器和客户端通过端口进行数据通信。守护进程的工作就是打开一个端口,并且监听(Listen)等待客户连接。如果客户端产生一个连接请求,守护进程就创建(fork)一个子服务器响应这个连接,而主服务器继续监听其他的服务请求。

看过上面的两点阐述和说明,熟悉windows的朋友一定会联想到windows服务。虽然windows下子进程的概念很少有人提,但它也确实是存在的,至于windows内部是不是通过fork实现子进程的,只能恕我孤陋寡闻了。

二、fork函数

从memcached的处理流程上可以看到实现守护进程机制的第一步是先fork一个子进程。看过园子里T2写的一篇文章讲fork的一道编程题,印象非常深刻。下面可以看一个广为流传的更简单直接的讲解fork函数的例子:

aboutfork#include <unistd.h>;#include <sys/types.h>;main (){pid_t pid;  //fork函数返回值pid=fork(); //通过fork函数给进程id赋值if (pid < 0)printf("error in fork!");else if (pid == 0)printf("i am the child process, my process id is %d\n",getpid());elseprintf("i am the parent process, my process id is %d\n",getpid());

这个函数最牛的地方是,表面上它的条件判断只能有一个为真(即只能打印(printf)一次),实际的输出让不熟悉*nix的普通开发人员如区区在下感到非常不解,在linux下运行,它的实际输出为:

i am the child process, my process id is 3279
i am the parent process, my process id is 3278

上面的输出可能在不同的linux内核(kernel)实现上输出顺序也不一样,但是总是输出两行。开始笔者一下子也是想不到为什么两行都打印出来,因为从常规程序运行角度理解,一个进程顺序执行,不管pid是多少,都应该只打印一行才对,可是fork(分叉)函数却可以打破我们这种思维定势。下面摘抄一段fork函数及子进程工作原理:

由fork 创建的新进程被称为子进程(该进程几乎是当前进程的一个完全拷贝),fork 函数被调用一次,但是返回两次。两次返回的唯一区别是子进程的返回值是0,而父进程的返回值则是新子进程的ID。将子进程的ID 返回给父进程的理由是:一个进程可以有多个子进程,并且没有该进程几乎是当前进程的一个完全拷贝函数是一个进程获得其子进程的进程ID。fork 在子进程中返回0的理由是:一个进程只能有一个父进程,并且可以通过getppid 函数获得其父进程的ID。子进程和父进程继续执行fork 调用后的指令,子进程是父进程的副本。例如:子进程可获得父进程的数据空间、堆和栈的副本。

上面这段话看上去好像比较深奥,实际上,如果我们学习过操作系统和c语言,应该能够读懂它的大致意思,当然这里必须要正确理解父进程和子进程也就是进程的概念。

那么什么是进程呢?

我们可能已经看到过很多面试题和参考书在讨论什么是进程和线程以及二者之间的关系。这里也不能免俗,顺带提一下进程的概念加深大家的理解:一个进程在内存里有三部分数据,即"代码段"、"堆栈段"和"数据段",这三个部分是构成一个完整的可执行单元的必要的组成。"代码段"就是存放了程序代码的数据,假如机器中有数个进程运行相同的一个程序,那么它们就可以使用相同的代码段。"堆栈段"存放的就是子程序的返回地址、子程序的参数以及程序的局部变量。而数据段则存放程序的全局变量,常数以及动态数据分配的数据空间(如用malloc之类的函数取得的空间)。
随着开发语言的发展,现在有很多的应用都是通过有比较完善的内存管理机制(比如.NET的托管环境(CLR)、Java虚拟机等)的高级语言开发的,所以普通开发者平时对内存分配和管理几乎不用怎么费心,所以有些底层基本的东西理解的也不是很清楚。但是实际上高级语言最终还是必须要经过类似IL(MS中间语言)或者二进制字节码(bytecode)“翻译”成原生代码(native code)通过CPU执行,所以本质上我们通过高级语言编写的程序执行时还是需要在内存里维护"代码段"、"堆栈段"和"数据段"这三部分。

哪位就要说了,既然进程需要内存维护这三部分,创建子进程不就需要多分配内存多维护数据了吗?fork的系统开销不会非常大吗?哈哈,这个也正是我的疑问,这里由于本人非常不熟悉*nix,虽然道听途说看到了一些解释,但是自我感觉理解的还不是太透彻,这里就暂时保留自己的看法,希望有心的你能够查阅一些资料深入学习一下。

下面来看点看上去及其简单的据传说可以很快搞死操作系统的c函数:

   void main() { for( ; ; ) fork(); } 

系统是怎么被搞死的呢?据说原理就是这个程序什么也不做,就是死循环地fork,让程序不断产生进程,而这些进程又不断产生新的进程…其结果是系统产生了很多的进程,直到资源不足而崩溃。

其实这个死循环在我看来还不是最牛的。分析上面的代码,for循环一次产生一个子进程,循环两次产生两个子进程…其实进程个数还是按照代数级数增长。想象一下如果有个函数执行一次,然后能够让进程产生子进程,子进程产生孙子进程……如此递归执行,进程按照指数规模膨胀,这个难道就是传说中的fork炸弹吗(这个函数应该怎么编写呢?网上看到一个,很简短,欢迎您的解答)?

总体感觉fork的运行机制还是非常好玩的。实际上现在最让我感兴趣的是多进程之间如何像多线程编程一样实现数据共享和同步。不管是多进程还是多线程,数据共享和同步(通信)一直是个头等难题,有时间一定抽空再好好整理总结一下。

参考:

http://www.cnblogs.com/leoo2sk/archive/2009/12/11/talk-about-fork-in-linux.html

http://pubs.opengroup.org/onlinepubs/009695399/functions/fork.html

http://www.berlinix.com/unix_daemon.html

http://www.phpx.com/happy/thread-131124-1-1.html

http://kb.cnblogs.com/page/48194/

http://code.google.com/p/memcached/wiki/NewCommands

转载于:https://www.cnblogs.com/jeffwongishandsome/archive/2011/10/26/2224464.html

浅显理解*nix下的守护进程机制及fork函数相关推荐

  1. arch下aria2c守护进程等配置小结

    arch下aria2c守护进程等配置小结 前言 aria2c是个好东西,可以把他简单理解为是下载管理器的后端,配合一些前端程序,比如ariaNG,可以管理其下载的各类东西,再配合百度云导出真实下载地址 ...

  2. windows下创建守护进程A和B 互相监视 挂掉拉起

    在windows下创建守护进程A和B ,在其中一个挂掉以后,另一个会把挂掉的拉起来. 下面展示一些 内联代码片. 这里只列出了A的代码,B和A类似. #include<iostream> ...

  3. window系统下C语言找不到fork()函数

    window系统下C语言找不到fork()函数 有次模拟进程时,想在window系统下调用fork()函数,但是就是引入相关头文件都无法找到函数,一开始是怀疑是否引入的头文件有误或者缺失,发现并没有缺 ...

  4. Linux系统编程之进程控制(进程创建,fork函数,进程中止,进程等待,程序替换)

    进程创建 fork()------复制,返回值,写时复制 vfork()创建子进程-子进程与父进程共用同一块虚拟地址空间, 为了防止调用栈混乱,因此阻塞父进程直到子进程调用exit()退出或者进行程序 ...

  5. linux进程管理fork,Linux -- 进程管理之 fork() 函数

    一个进程调用fork()函数后,系统先给新的进程分配资源,例如存储数据和代码的空间.然后把原来的进程的所有值都复制到新的新进程中,只有少数值与原来的进程的值不同.相当于克隆了一个自己. Test1 f ...

  6. linux下daemon守护进程的实现(以nginx代码为例)

    ngx_int_t ngx_daemon(ngx_log_t *log) {int fd;// 让init进程成为新产生进程的父进程:// 调用fork函数创建子进程后,使父进程立即退出.这样,产生的 ...

  7. 嵌入式linux系统下简单守护进程(daemon)的编写

    最近公司项目需要,需要在我们的嵌入式linux设备中创建一个守护进程,用于保护系统中的主进程,防止某些不可预期的意外导致主进程异常结束后,系统完全宕机没有任何反应,破坏用户体验感.但是,查阅诸多资料之 ...

  8. windows下的守护进程C++

    1 守护进程 1.1 需求分析 我有三个程序需要不断运行,有可能出现某些未知的原因而宕掉,需要本守护程序来进行守护,发现它运行不管,死掉就重启它,并且服务器开机就启动. 1.2 使用方法 将该程序与需 ...

  9. linux c控制进程并发量,浅谈Linux环境下并发编程中C语言fork()函数的使用

    由fork创建的新进程被称为子进程(child process).fork函数被调用一次,但返回两次.子进程的返回值是0,而父进程的返回值则是新进程的进程ID.将子进程ID返回给父进程的理由是:因为一 ...

最新文章

  1. 卷积神经网络中10大拍案叫绝的操作!
  2. 10 个牛逼的单行代码编程技巧,你会用吗?
  3. 图像处理:镜头频率(衍射极限) 和 相机采样:显微镜的采样定理
  4. Oracle的DECODE函数
  5. MySQL: Starting MySQL….. ERROR! The server quit without updating PID file解决办法
  6. SAP Spartacus页面css类的赋值逻辑
  7. STM32 - 定时器的设定 - 基础 01.1 - Repetition counter
  8. 微信公众平台开发者原理图解
  9. Java Web GenericServlet
  10. 静态树表的查找(最优查找树和次优查找树)
  11. 0-50A-400A霍尔电流传感器应用案例分享
  12. leetcode 51. N-Queens N 皇后(困难)
  13. 计算机组成原理实验报告一静态随机存储器
  14. jzoj3457. 【NOIP2013模拟联考3】沙耶的玩偶
  15. 【信息检索导论】第一章 布尔检索
  16. 2020-12-20|西瓜更新Cookies ttwid获取方法
  17. 如果IT产品经理转行最适合的职业是什么(超乎想象)
  18. html轮播图片加超链接,求助HTML5 图片轮播
  19. python后端面经_2019 Python后端开发面经总结:网易、滴滴、老虎证券
  20. 今天我抓了个 HTTPS 的包

热门文章

  1. jQuery Hello世界
  2. 在C ++中将String转换为Integer并将Integer转换为String
  3. java stack 实现_Swift Stack实现
  4. Android TableLayout
  5. mockito 静态方法_Mockito模拟静态方法– PowerMock
  6. 约瑟夫环数学公式求解
  7. 开课吧课堂:人工智能对金融行业的影响
  8. Java新职篇:for循环
  9. React server rendering —— 网易美学主站同构实录
  10. 基于Java的Selenium学习笔记——启动Chrome