守护进程概述
Linux Daemon(守护进程)是运行在后台的一种特殊进程。它独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。它不需要用户输入就能运行而且提供某种服务,不是对整个系统就是对某个用户程序提供服务。Linux系统的大多数服务器就是通过守护进程实现的。常见的守护进程包括系统日志进程syslogd、 web服务器httpd、邮件服务器sendmail和数据库服务器mysqld等。
守护进程一般在系统启动时开始运行,除非强行终止,否则直到系统关机都保持运行。守护进程经常以超级用户(root)权限运行,因为它们要使用特殊的端口(1-1024)或访问某些特殊的资源。
一个守护进程的父进程是init进程,因为它真正的父进程在fork出子进程后就先于子进程exit退出了,所以它是一个由init继承的孤儿进程。守护进程是非交互式程序,没有控制终端,所以任何输出,无论是向标准输出设备stdout还是标准出错设备stderr的输出都需要特殊处理。
守护进程的名称通常以d结尾,比如sshd、xinetd、crond等
创建守护进程步骤
- 调用fork(),创建新进程,它会是将来的守护进程
- 在父进程中调用exit,保证子进程不是进程组组长
- 调用setsid创建新的会话期
- 将当前目录改为根目录(如果把当前目录作为守护进程的目录,当前目录不能被卸载,它作为守护进程的工作目录了。)
- 将标准输入、标准输出、标准错误重定向到/dev/null
概念:
进程组 :
- 每个进程也属于一个进程组
- 每个进程主都有一个进程组号,该号等于该进程组组长的PID号 .
- 一个进程只能为它自己或子进程设置进程组ID号
会话期:
会话期(session)是一个或者多个进程组的集合,通常一个会话开始与用户登录,终止于用户退出。在此期间,该用户运行的所有进程都属于这个会话期
setsid()函数可以建立一个会话期:
如果,调用setsid的进程不是一个进程组的组长,此函数创建一个新的会话期。
(1)此进程变成该会话期的首进程
(2)此进程变成一个新进程组的组长进程。
(3)此进程没有控制终端,如果在调用setsid前,该进程有控制终端,那么之后该终端的联系被解除。 如果该进程是一个进程组的组长,此函数返回错误。
(4)为了保证这一点,我们先调用fork()然后exit(),此时只有子进程在运行
pid_t setsid(void);
/*当调用进程不是一个进程组的组长时,Setsid创建一个新的会话;调用者进程会是这个会话期唯一的一个进程,且是该进程组的组长;调用者进程id是组id,也是会话期的id。不能用进程组组长去调用setsid函数。新产生的group ID 和 session ID 被设置成调用进程的PID。
成功返回会话ID,错误返回(pid_t) -1*/
//示例:
int main()
{
pid_t pid = fork();
if (pid == -1)
err_exit("fork error");
else if (pid != 0)
exit(EXIT_SUCCESS);
// //查看下面这一部分代码在注释的前后有什么变化
// pid_t id = setsid();
// if (id == -1)
// err_exit("setsid error");
// else
// cout << "new session id = " << id << endl;
cout << "getpid = " << getpid() << endl;
cout << "getpgid = " << getpgid(getpid()) << endl;
return 0;
}
Linux中的守护进程API
int daemon(int nochdir, int noclose);
参数:
nochdir:=0将当前目录更改至“/”
noclose:=0将标准输入、标准输出、标准错误重定向至“/dev/null”
//示例:自己动手写daemon函数(一个比较简单的示例)
bool myDaemon(bool nochdir, bool noclose)
{
umask(0);
pid_t pid = fork();
if (pid == -1)
err_exit("fork error");
else if (pid != 0) //parent
exit(0);
setsid();
if (nochdir == 0)
chdir("/");
if (noclose == 0)
{
int i;
for (i=0; i < 3; ++i)
close(i);
open("/dev/null", O_RDWR); //相当于把0号文件描述符指向/dev/null
dup(0); //把0号文件描述符 赋值给空闲的文件描述符 1
dup(0); //把0号文件描述符 赋值给空闲的文件描述符 2
}
return true;
}
//测试
int main(int argc, char *argv[])
{
myDaemon(0, 0); //0表示做出改变(当前目录,文件描述符),1表示不改变
printf("test ...\n");
while (true)
{
sleep(1);
}
return 0;
}