Linux多进程开发(三)进程创建之守护进程的学习 - _Liang_Happy_Life__Dream - 51CTO技术博客 - Google Chrome (2013/10/11 16:48:27)
# include <unistd.h># include <sys/types.h># include <signal.h># include <sys/param.h># include <sys/stat.h># include <time.h># include <syslog.h># include <stdio.h>int init_daemon(void)
{ int pid;
int i;
/*忽略终端IO信号,STOP信号*/
signal(SIGTTOU, SIG_IGN);
signal(SIGTTIN, SIG_IGN);
signal(SIGTSTP, SIG_IGN);
signal(SIGHUP, SIG_IGN);
pid = fork();
if(pid > 0)
{/*结束父进程,使子进程成为后台进程*/
exit(0);
}
else if(pid < 0)
{
return -1;
}
/*建立一个新的进程组,在这个新的进程组中,子进程成为这个进程组的首进程,以使该进程
脱离所有终端*/
setsid();
/*再次新建一个子进程,退出父进程,保证该进程不是进程组长,同时让该进程无法再打开一个
新的终端*/
pid = fork();
if(pid > 0)
{
exit(0);
}
else if(pid < 0)
{
return -1;
}
/*关闭所有父进程继承的不再需要的文件描述符*/
for(i = 0; i < NOFILE; close(i++))
{
;
}
/*改变工作目录,使得进程不与任何文件系统联系*/
chdir("/");
/*将文件屏蔽字设置为0*/
umask(0);
/*忽略SIGCHLD信号*/
signal(SIGCHLD, SIG_IGN);
return 0;
}int main(void)
{ time_t now;
init_daemon();
syslog(LOG_USER | LOG_INFO, "Test daemon \n");
while(1)
{
sleep(8);
time(&now);
syslog(LOG_USER | LOG_INFO, "system time : \t%s\t\t\n", ctime(&now));
}
return 0;
}让程序在后台运行,关闭终端不会使程序退出的方式 (2013/10/11 15:43:53)
使用nohup或将程序改成守护进程。
linux的进程类型分:交互进程,批处理进程和守护进程;只有守护进程可以做到用终端打开后,关闭终端而程序不关闭。
终端(控制终端)由两部分组成,控制进程(一般为shell进程)和终端设备(TTY或pts),一个终端打开的所有进程(包括控制进程)同属一个会话,但守护进程在打开后,会新建会话,脱离原会话的影响,同时托管于init进程,因此与原终端再没有关系。
通过nohup命令启动的程序会忽略所有SIGHUP信号。在关闭控制终端时,系统会向该控制终端会话组的所有进程发SIGHUP信号,该信号 默认会让进程终止。显然忽略此信号后进程就不会终止。
用nohup命令让程序在后台执行(Unix/Linux) - 非黑白的生活 - 色彩源于心态,所以,我很花心。 - Google Chrome (2013/10/11 15:41:26)
在主机上跑程序,挂后台以后发现有时程序会退出,有时又不会。很奇怪,查了一下资料,整理如下。
& 只是放在后台运行,但是没有脱离控制终端,当终端断开的时候,进程将收到SIGHUP信号,这个信号默认动作是结束进程。用nohup将使进程脱离控制终端,进程无法进行终端操作,所有输出到终端的会被重定向。而 & 可以进行终端操作,如果发生读写终端,进程被挂起,用fg命令可以使进程获得终端控制权并继续运行。
Unix/Linux下一般想让某个程序在后台运行,很多都是使用 & 在程序结尾来让程序自动运行。比如我们要运行mysql在后台: /usr/local/mysql/bin/mysqld_safe –user=mysql &
但是我们很多程序并不象mysqld一样可以做成守护进程,可能我们的程序只是普通程序而已,一般这种程序即使使用 & 结尾,如果终端关闭,那么程序也会被关闭。为了能够后台运行,我们需要使用nohup这个命令,比如我们有个start.sh需要在后台运行,并且希望在后台能够一直运行,那么就使用nohup: nohup /root/start.sh & 在shell中回车后提示: [~]$ appending output to nohup.out
原程序的的标准输出被自动改向到当前目录下的nohup.out文件,起到了log的作用。但是有时候在这一步会有问题,当把终端关闭后,进程会自动被关闭,察看nohup.out可以看到在关闭终端瞬间服务自动关闭。咨询红旗Linux工程师后,他也不得其解,在我的终端上执行后,他启动的进程竟然在关闭终端后依然运行。在第二遍给我演示时,我才发现我和他操作终端时的一个细节不同:他是在当shell中提示了nohup成功后还需要按终端上键盘任意键退回到shell输入命令窗口,然后通过在shell中输入exit来退出终端;而我是每次在nohup执行成功后直接点关闭程序按钮关闭终端.。所以这时候会断掉该命令所对应的session,导致nohup对应的进程被通知需要一起shutdown。
这个细节有人和我一样没注意到,所以在这儿记录一下了。
附:nohup命令参考 nohup 命令
用途:不挂断地运行命令。
语法:nohup Command [ Arg ... ] [ & ]
描述:nohup 命令运行由 Command 参数和任何相关的 Arg 参数指定的命令,忽略所有挂断(SIGHUP)信号。在注销后使用 nohup 命令运行后台中的程序。要运行后台中的 nohup 命令,添加 & ( 表示”and”的符号)到命令的尾部。
无论是否将 nohup 命令的输出重定向到终端,输出都将附加到当前目录的 nohup.out 文件中。如果当前目录的 nohup.out 文件不可写,输出重定向到 $HOME/nohup.out 文件中。如果没有文件能创建或打开以用于追加,那么 Command 参数指定的命令不可调用。如果标准错误是一个终端,那么把指定的命令写给标准错误的所有输出作为标准输出重定向到相同的文件描述符。
退出状态:该命令返回下列出口值:
126 可以查找但不能调用 Command 参数指定的命令。
127 nohup 命令发生错误或不能查找由 Command 参数指定的命令。
否则,nohup 命令的退出状态是 Command 参数指定命令的退出状态。
nohup命令及其输出文件
nohup命令:如果你正在运行一个进程,而且你觉得在退出帐户时该进程还不会结束,那么可以使用nohup命令。该命令可以在你退出帐户/关闭终端之后继续运行相应的进程。nohup就是不挂起的意思( n ohang up)。
该命令的一般形式为:nohup command &
使用nohup命令提交作业
如果使用nohup命令提交作业,那么在缺省情况下该作业的所有输出都被重定向到一个名为nohup.out的文件中,除非另外指定了输出文件:
nohup command > myout.file 2>&1 &
在上面的例子中,输出被重定向到myout.file文件中。
刚才又试了一下,这样也可以:
nohup command >/dev/null 2>/dev/null &
或者
nohup command &>/dev/null
使用 jobs 查看任务。
使用 fg %n 关闭。
另外有两个常用的ftp工具ncftpget和ncftpput,可以实现后台的ftp上传和下载,这样就可以利用这些命令在后台上传和下载文件了。
使用nohup让程序在远程主机后台运行-月光博客 - Google Chrome (2013/10/11 15:38:15)
因为我购买的一个国外主机居然开放了Telnet权限,因此我也使用Telnet登录上去玩玩Linux,但发现一关闭窗口就自动和主机断开了,和Windows的终端不一样,所以就上网找啊找,找到了一个从后台一直运行某个程序的方法。
Unix/Linux下一般比如想让某个程序在后台运行,很多都是使用 & 在程序结尾来让程序自动运行。比如我们要运行mysql在后台:
/usr/local/mysql/bin/mysqld_safe --user=mysql &
但是加入我们很多程序并不象mysqld一样做成守护进程,可能我们的程序只是普通程序而已,一般这种程序使用 & 结尾,但是如果终端关闭,那么程序也会被关闭。但是为了能够后台运行,那么我们就可以使用nohup这个命令,比如我们有个test.php需要在后台运行,并且希望在后台能够定期运行,那么就使用nohup:
nohup /root/test.php &
提示:
[~]$ appending output to nohup.out
嗯,证明运行成功,同时把程序运行的输出信息放到当前目录的 nohup.out 文件中去。
附:nohup命令参考
nohup 命令
用途:不挂断地运行命令。
语法:nohup Command [ Arg ... ] [ & ]
描述:nohup 命令运行由 Command 参数和任何相关的 Arg 参数指定的命令,忽略所有挂断(SIGHUP)信号。在注销后使用 nohup 命令运行后台中的程序。要运行后台中的 nohup 命令,添加 & ( 表示“and”的符号)到命令的尾部。
无论是否将 nohup 命令的输出重定向到终端,输出都将附加到当前目录的 nohup.out 文件中。如果当前目录的 nohup.out 文件不可写,输出重定向到 $HOME/nohup.out 文件中。如果没有文件能创建或打开以用于追加,那么 Command 参数指定的命令不可调用。如果标准错误是一个终端,那么把指定的命令写给标准错误的所有输出作为标准输出重定向到相同的文件描述符。
退出状态:该命令返回下列出口值:
126 可以查找但不能调用 Command 参数指定的命令。
127 nohup 命令发生错误或不能查找由 Command 参数指定的命令。
否则,nohup 命令的退出状态是 Command 参数指定命令的退出状态。
nohup命令及其输出文件
nohup命令:如果你正在运行一个进程,而且你觉得在退出帐户时该进程还不会结束,那么可以使用nohup命令。该命令可以在你退出帐户/关闭终端之后继续运行相应的进程。nohup就是不挂起的意思( n ohang up)。
该命令的一般形式为:nohup command &
使用nohup命令提交作业
如果使用nohup命令提交作业,那么在缺省情况下该作业的所有输出都被重定向到一个名为nohup.out的文件中,除非另外指定了输出文件:
nohup command > myout.file 2>&1 &
在上面的例子中,输出被重定向到myout.file文件中。
使用 jobs 查看任务。
使用 fg %n 关闭。
另外有两个常用的ftp工具ncftpget和ncftpput,可以实现后台的ftp上传和下载,这样我就可以利用这些命令在后台上传和下载文件了。
Linux守护进程的编程实现 - hairetz的专栏 - 博客频道 - CSDN.NET - Google Chrome (2013/10/11 10:12:39)
从输出可以发现test守护进程的各种特性满足上面的要求。
3. 守护进程 - Google Chrome (2013/10/9 11:23:05)
3. 守护进程 请点评
Linux系统启动时会启动很多系统服务进程,例如第 1.3 节 “网络登录过程”讲的inetd,这些系统服务进程没有控制终端,不能直接和用户交互。其它进程都是在用户登录或运行程序时创建,在运行结束或用户注销时终止,但系统服务进程不受用户登录注销的影响,它们一直在运行着。这种进程有一个名称叫守护进程(Daemon)。
下面我们用ps axj命令查看系统中的进程。参数a表示不仅列当前用户的进程,也列出所有其他用户的进程,参数x表示不仅列有控制终端的进程,也列出所有无控制终端的进程,参数j表示列出与作业控制相关的信息。
$ ps axj
PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
0 1 1 1 ? -1 Ss 0 0:01 /sbin/init
0 2 0 0 ? -1 S< 0 0:00 [kthreadd]
2 3 0 0 ? -1 S< 0 0:00 [migration/0]
2 4 0 0 ? -1 S< 0 0:00 [ksoftirqd/0]
...
1 2373 2373 2373 ? -1 S<s 0 0:00 /sbin/udevd --daemon
...
1 4680 4680 4680 ? -1 Ss 0 0:00 /usr/sbin/acpid -c /etc
...
1 4808 4808 4808 ? -1 Ss 102 0:00 /sbin/syslogd -u syslog
...
凡是TPGID一栏写着-1的都是没有控制终端的进程,也就是守护进程。在COMMAND一列用[]括起来的名字表示内核线程,这些线程在内核里创建,没有用户空间代码,因此没有程序文件名和命令行,通常采用以k开头的名字,表示Kernel。init进程我们已经很熟悉了,udevd负责维护/dev目录下的设备文件,acpid负责电源管理,syslogd负责维护/var/log下的日志文件,可以看出,守护进程通常采用以d结尾的名字,表示Daemon。
创建守护进程最关键的一步是调用setsid函数创建一个新的Session,并成为Session Leader。
#include <unistd.h> pid_t setsid(void);
该函数调用成功时返回新创建的Session的id(其实也就是当前进程的id),出错返回-1。注意,调用这个函数之前,当前进程不允许是进程组的Leader,否则该函数返回-1。要保证当前进程不是进程组的Leader也很容易,只要先fork再调用setsid就行了。fork创建的子进程和父进程在同一个进程组中,进程组的Leader必然是该组的第一个进程,所以子进程不可能是该组的第一个进程,在子进程中调用setsid就不会有问题了。
成功调用该函数的结果是:
创建一个新的Session,当前进程成为Session Leader,当前进程的id就是Session的id。
创建一个新的进程组,当前进程成为进程组的Leader,当前进程的id就是进程组的id。
如果当前进程原本有一个控制终端,则它失去这个控制终端,成为一个没有控制终端的进程。所谓失去控制终端是指,原来的控制终端仍然是打开的,仍然可以读写,但只是一个普通的打开文件而不是控制终端了。
为了确保调用setsid的进程不是进程组的Leader,首先fork出一个子进程,父进程退出,然后子进程调用setsid创建新的Session,成为守护进程。按照守护进程的惯例,通常将当前工作目录切换到根目录,将文件描述符0、1、2重定向到/dev/null。Linux也提供了一个库函数daemon(3)实现我们的daemonize函数的功能,它带两个参数指示要不要切换工作目录到根目录,以及要不要把文件描述符0、1、2重定向到/dev/null。
$ ./a.out
$ ps
PID TTY TIME CMD
11494 pts/0 00:00:00 bash
13271 pts/0 00:00:00 ps
$ ps xj | grep a.out
1 13270 13270 13270 ? -1 Rs 1000 0:05 ./a.out
11494 13273 13272 11494 pts/0 13272 S+ 1000 0:00 grep a.out
(关闭终端窗口重新打开,或者注销重新登录)
$ ps xj | grep a.out
1 13270 13270 13270 ? -1 Rs 1000 0:21 ./a.out
13282 13338 13337 13282 pts/1 13337 S+ 1000 0:00 grep a.out
$ kill 13270
运行这个程序,它变成一个守护进程,不再和当前终端关联。用ps命令看不到,必须运行带x参数的ps命令才能看到。另外还可以看到,用户关闭终端窗口或注销也不会影响守护进程的运行。