linux c 守护进程

守护进程,也即后台程序。不依赖于终端存在,即终端关闭进程依旧在后台默默的运行,我们可以通过ps命令查看计算机上的进程,其实守护进程还是很多的。

ps -ef

输出

UID        PID  PPID  C STIME TTY          TIME CMD
...       ....  ....  . .. .. ...      ..........................
root      3898  3876  0 01:23 ?        00:00:03 hald-addon-storage: polling /dev
root      3919     1  0 01:23 ?        00:00:00 /usr/bin/hidd --server
root      3949     1  0 01:23 ?        00:00:00 automount
root      3971     1  0 01:23 ?        00:00:00 ./hpiod
root      3976     1  0 01:23 ?        00:00:00 python ./hpssd.py
root      4017     1  0 01:23 ?        00:00:00 /usr/sbin/sshd
root      4044     1  0 01:23 ?        00:00:00 xinetd -stayalive -pidfile /var/
root      4067     1  0 01:23 ?        00:00:00 sendmail: accepting connections
smmsp     4075     1  0 01:23 ?        00:00:00 sendmail: Queue runner@01:00:00
root      4090     1  0 01:23 ?        00:00:00 gpm -m /dev/input/mice -t exps2
root      4104     1  0 01:23 ?        00:00:00 crond
xfs       4142     1  0 01:23 ?        00:00:00 xfs -droppriv -daemon
root      4169     1  0 01:23 ?        00:00:00 /usr/sbin/atd
avahi     4200     1  0 01:23 ?        00:00:00 avahi-daemon: running [localhost
avahi     4201  4200  0 01:23 ?        00:00:00 avahi-daemon: chroot helper
root      4232     1  0 01:23 ?        00:00:00 /usr/sbin/smartd -q never
root      4236     1  0 01:23 ?        00:00:00 login -- root
root      4237     1  0 01:23 tty2     00:00:00 /sbin/mingetty tty2
root      4241     1  0 01:23 tty3     00:00:00 /sbin/mingetty tty3
root      4242     1  0 01:23 tty4     00:00:00 /sbin/mingetty tty4
root      4243     1  0 01:23 tty5     00:00:00 /sbin/mingetty tty5
root      4261     1  0 01:23 tty6     00:00:00 /sbin/mingetty tty6
root      4292     1  0 01:23 ?        00:00:00 /usr/bin/python -tt /usr/sbin/yu
root      4294     1  0 01:23 ?        00:00:00 /usr/libexec/gam_server
root      4388  4236  0 01:26 tty1     00:00:00 -bash
root      4474  4017  0 01:27 ?        00:00:01 sshd: root@pts/0
root      4509  4474  0 01:28 pts/0    00:00:00 -bash
root      4899  4017  0 01:37 ?        00:00:00 sshd: root@pts/1
root      4914  4899  0 01:37 pts/1    00:00:00 -bash

那么 TTY 终端为 ? 问号程序就是守护进程, 而其他的, 比如 tty2 这是一个终端的名称, 如果使用这个终端登录的用户退出, 那么所有这个终端下的程序都会结束掉.
而守护进程不依赖于终端, 所以即使终端退出(比如远程连接的putty)只要电脑还开着, 那么它就默默的在后台运行.

setsid() 函数

setsid() 调用成功后,当前进程会脱离原有的会话(session, 终端与会话是对应的), 成为新的会话组长和新的进程组长,并与原来的登录会话和进程组脱离。
简单点可以理解, 刚登陆的时候有个session产生, 这个session与终端对应. 如果我把这个终端关掉(例如关掉连接中的 putty ) 那么与之相关的session还有进程组都会被干掉。
这个还是还比较好理解的,如果我用putty登陆,上面开了很多程序, 这个时候我把putty关掉, 那么系统也会把我刚刚登陆的这个putty(终端)上开的程序神马的统统关掉.
那么, 我不希望系统关掉我的程序, 我想让我的程序在后台一直运行, 那么就只有让程序脱离于putty对应的session和进程组(setsid就可以做到)

那么, 需要注意的是:进程组组长不能创建守护进程. 其实就是进程组组长不能调用 setsid , 因为他本身就是组长了.

下面看下实现的实例:

#include <stdio.h>
#include <stdlib.h>	// exit()
#include <unistd.h>	// fork() setsid() chdir() close()
#include <sys/types.h>	// fork() umask()
#include <sys/stat.h>	// umask()


// 与终端无关,在后台默默运行的守护进程
void mydaemon(int ischdir,int isclose)
{
	// 调用setsid() 的不能是进程组组长,当前程序有可能是进程组组长
	pid_t pid = fork();

	// 非子进程则退出
	if(pid != 0)
		exit(-1);

	// 父进程退出,留下子进程

	// 创建一个新的会话期,从而脱离原有的会话期
	// 进程同时与控制终端脱离
	setsid();

	// 此时子进程成为新会话期的领导和新的进程组长
	// 但这样的身份可能会通过fcntl去获到终端
	pid = fork();

	// 非子进程则退出
	if(pid != 0)
		exit(-1);

	// 此时留下来的是孙子进程,再也不能获取终端

	// 通常来讲, 守护进程应该工作在一个系统永远不会删除的目录下
	if(ischdir == 0)
	{
		chdir("/");
	}

	// 关闭输入输出和错误流 (通过日志来查看状态)
	if(isclose == 0)
	{
		close(0);
		close(1);
		close(2);
	}

	//去掩码位
	umask((mode_t)0);//sys/stat.h
}

int main(int argc, char *argv[])
{
	mydaemon(1,1);

	while(1)
		;

	exit(EXIT_SUCCESS);
}
gcc mydaemon.c -o mydaemon
./mydaemon

运行之后应该是什么效果都没有的, 这个时候通过ps命令来查看

ps -ef

输出:

其他省略n项...
root      4388  4236  0 01:26 tty1     00:00:00 -bash
root      4474  4017  0 01:27 ?        00:00:01 sshd: root@pts/0
root      4509  4474  0 01:28 pts/0    00:00:00 -bash
root      4899  4017  0 01:37 ?        00:00:00 sshd: root@pts/1
root      4914  4899  0 01:37 pts/1    00:00:00 -bash
root      9231     1 97 04:40 ?        00:01:19 ./mydaemon
root      9278  4509  0 04:41 pts/0    00:00:00 ps -ef

发现我们的程序已经运行了起来, 并且终端处是 ? 问号, 即成为了不依赖于任何终端而存在后台程序.

那么, 虽然有点小残忍, 但是这里还是要顺便提一下怎么杀死这样的进程:

#向进程id为9231的进程发送 SIGKILL 信号
kill -9 9231

详细见: Linux 查看进程和删除进程