守护进程及其创建

一、概念

Linux Daemon(守护进程)是运行在后台的一种特殊进程。它独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。它不需要用户输入就能运行而且提供某种服务(要么是对整个系统,要么是对某个用户程序提供服务)。

Linux 系统的大多数服务器就是通过守护进程实现的。常见的守护进程包括系统日志进程 syslogd、 web 服务器 httpd、邮件服务器 sendmail 和数据库服务器 mysqld 等。守护进程的名称通常以 d 结尾,比如 sshd、xinetd、crond 等。

守护进程一般在系统启动时开始运行,除非强行终止,否则直到系统关机都保持运行。守护进程经常以超级用户(root)权限运行,因为它们要使用特殊的端口(1-1024)或访问某些特殊的资源。

一个守护进程的父进程是 init 进程,因为它真正的父进程在 fork 出子进程后就先于子进程 exit 退出了,所以守护进程是一个由 init 继承的孤儿进程(实际上 init 也是一个守护进程,它专门用于托管那些父进程死掉但子进程还继续运行的孤儿进程)。

守护进程是非交互式程序,没有控制终端,所以任何输出(无论是向标准输出设备 stdout 还是标准出错设备 stderr 的输出)都需要特殊处理。

二、命令行创建守护进程

当用户注销(logout)或者网络断开时,终端会收到 HUP(hangup)信号从而关闭其所有子进程。因此,我们的解决办法就有两种途径:要么让进程忽略 HUP 信号,要么让进程运行在新的会话里从而成为不属于此终端的子进程。

方法一:忽略 HUP 信号
① nohup
nohup 能通过忽略 HUP 信号来使我们的进程避免中途被中断:

# nohup 命令或进程 &

其中 & 代表想要以后台的方式运行这个进程(将它变成一个后台作业),如果不加这个参数,命令行就不能继续输入别的命令了。标准输出和标准错误缺省会被重定向到 nohup.out 文件中。例如:

# nohup ping www.ibm.com &
[1] 3059
nohup: appending output to `nohup.out'
# ps -ef | grep 3059
root      3059   984  0 21:06 pts/3    00:00:00 ping www.ibm.com
root      3067   984  0 21:06 pts/3    00:00:00 grep 3059

② setsid
换个角度思考,如果我们的进程不属于接受 HUP 信号的终端的子进程,那么自然也就不会受到 HUP 信号的影响了。setsid 就能帮助我们做到这一点:

# setsid ping www.ibm.com
# ps -ef | grep www.ibm.com
root     31094     1  0 07:28 ?        00:00:00 ping www.ibm.com
root     31102 29217  0 07:29 pts/4    00:00:00 grep www.ibm.com

方法二:让进程运行在新的会话里
③ ( &)
将命令和"&"放入“()”中,会发现所提交的作业并不在作业列表中,也可以躲过 HUP 信号的影响:

# (ping www.ibm.com &)
# ps -ef | grep www.ibm.com
root     16270     1  0 14:13 pts/4    00:00:00 ping www.ibm.com
root     16278 15362  0 14:13 pts/4    00:00:00 grep www.ibm.com

从上例中可以看出,新提交的进程的父 ID(PPID)为1(init 进程的 PID),并不是当前终端的进程 ID。因此不属于当前终端的子进程,也就不会受到当前终端的 HUP 信号的影响了。
④ disown
如果已经未加任何处理就提交了命令,该如何补救才能让它避免 HUP 信号的影响呢?这时想加 nohup 或者 setsid 已经为时已晚,只能通过作业调度和 disown 来解决这个问题了:

使某个作业忽略 HUP 信号:
# disown -h 作业号
使所有的作业都忽略 HUP 信号:
# disown -ah
使正在运行的作业忽略 HUP 信号:
# disown -rh

例子:

# cp -r testLargeFile largeFile &
[1] 4825
# jobs
[1]+  Running                 cp -i -r testLargeFile largeFile &
# disown -h %1
# ps -ef | grep largeFile
root      4825   968  1 09:46 pts/4    00:00:00 cp -i -r testLargeFile largeFile
root      4853   968  0 09:46 pts/4    00:00:00 grep largeFile

需要注意的是,当使用过 disown 之后,会把目标作业从作业列表中移除,我们将不能再使用 jobs 来查看它,但是依然能够用 ps -ef 查找到它。

总结:
nohup/setsid 无疑是临时需要时最方便的方法,disown 能帮助我们来事后补救当前已经在运行了的作业。

三、编程创建守护进程

在编程实现时一般采取上文介绍到的 setsid 方法。一个会话期开始于用户 login,结束于 logout。一般 login 的是 shell 终端,所以 shell 终端又是此次会话期的首进程。对于非进程组长,它可以调用 setsid() 创建一个新的会话,脱离父进程的会话期。

void mydaemon(void)
{    
    pid_t pid;
    int fd, i, nfiles;
    struct rlimit rl;

    pid = fork();
    if(pid < 0)
        ERROR_EXIT("First fork failed!");

    // 父进程退出, 使子进程成为后台进程
    if(pid > 0)
        exit(EXIT_SUCCESS);

    // 子进程成为新会话的组长, 脱离父进程的会话期
    if(setsid() == -1)
        ERROR_EXIT("setsid failed!");

    pid = fork();
    if(pid < 0)
        ERROR_EXIT("Second fork failed!");

    // 因为会话组的组长有权限重新打开控制终端, 所以这里将子进程结束, 保留孙进程
    // 孙进程不是会话组的组长所以没有权利再打开控制终端, 这样整个程序就与控制终端隔离了
    if(pid > 0)
        exit(EXIT_SUCCESS);

    #ifdef RLIMIT_NOFILE
    // 关闭从父进程继承来的文件描述符
    // 如不关闭, 将会浪费系统资源, 造成进程所在的文件系统无法卸下以及引起无法预料的错误
    if (getrlimit(RLIMIT_NOFILE, &rl) == -1)
        ERROR_EXIT("getrlimit failed!");
    nfiles = rl.rlim_cur = rl.rlim_max;
    setrlimit(RLIMIT_NOFILE, &rl);
    for(i=3; i<nfiles; i++)
        close(i);
   #endif

    // 将标准的 3 个文件描述符 0, 1, 2 重定向到 /dev/null
    if(fd = open("/dev/null", O_RDWR) < 0)
        ERROR_EXIT("open /dev/null failed!");
    for(i=0; i<3; i++)
        dup2(fd, i);
   if(fd > 2) close(fd);

    // 进程活动时, 其工作目录所在的文件系统不能卸下, 一般需要将工作目录改变到根目录
    // 进程从创建它的父进程那里继承了文件创建掩码, 它可能修改守护进程所创建的文件的存取位. 为防止这一点, 将文件创建掩码清除
    chdir("/");
    umask(0);
}

四、使用库函数 daemon() 创建守护进程

#include <unistd.h>
int daemon(int nochdir, int noclose);
/*
参数:
nochdir:=0 时将当前目录更改至“/”
noclose:=0 时将标准输入、标准输出、标准错误重定向至“/dev/null”
返回值:
成功:0
失败:-1
**/
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,496评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,407评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,632评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,180评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,198评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,165评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,052评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,910评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,324评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,542评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,711评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,424评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,017评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,668评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,823评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,722评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,611评论 2 353