Linux:守护进程

守护进程也称精灵进程(Daemon):

    是运行在后台的一种特殊进程。他独立与控制终端并且周期性的执行某种任务或者处理某些发生的事情。守护进程是一种很有用的进程,在操作系统中,维护系统各种设施的进程。

    在Linux中,大多数服务器就是守护进程实现的:比如:Internet服务器inetd,Web服务器httpd


在上一篇博文例行性工作中。crond就是守护进程进行的任务。

系统服务进程不受用户注销登录的影响,只随系统关闭而关闭。


用ps axj命令查看系统中的进程。参数a表示不仅列当前用户的进程,也列出所有其他用户的进程,参数x表示不仅列有控制终端的进程,也列出所有无控制终端的进程,参数j表示列出与作业控制相关的信息。


下面我们来看一下系统中的守护进程:

wKioL1cxcdaRBhboAAHUInN1BzA299.png

    凡是TPGID一栏写着-1的都是没有控制终端的进程,也就是守护进程。在COMMAND一列用[]括起来的 名字表示内核线程,这些线程在内核里创建,没有用户空间代码,因此没有程序文件名和命令行, 通常采用以k开头的名字,表示Kernel。init进程我们已经很熟悉了,udevd负责维护/dev目录下的 设备文件,acpid负责电源管理,syslogd负责维护/var/log下的日志文件,可以看出,守护进程通常采用以d结尾的名字,表示Daemon。


既然理解了守护进程的概念,那么我们来了解一下如何自己创建一个守护进程呢?

守护进程创建函数:

#include<unistd.h>
pid_t setsid(void);

该函数调用成功时返回新创建的Session的id(其实也就是当前进程的id),出错返回-1。注意,调用这个函数之前,当前进程不允许是进程组的Leader,否则该函数返回-1。要保证当前进程不是进程组的Leader也很容易,只要先fork再调用setsid就行了。fork创建的子进程和父进程在同一个进程组中,进程组Leader必然是该组的第一个进程,所以子进程不可能是该组的第一个

进程,在子进程中调用setsid就不会有问题了。

成功调用该函数的结果是:

1. 创建一个新的Session,当前进程成为Session Leader,当前进程的id就是Session的id。

2. 创建一个新的进程组,当前进程成为进程组的Leader,当前进程的id就是进程组的id。

3. 如果当前进程原本有一个控制终端,则它失去这个控制终端,成为一个没有控制终端的进

程。所谓失去控制终端是指,原来的控制终端仍然是打开的,仍然可以读写,但只是一个普

通的打开文件而不是控制终端了。

创建守护进程

1. 调用umask将文件模式创建屏蔽字设置为0.

2. 调用fork,父进程退出(exit)。原因:1)如果该守护进程是作为一条简单的shell命令

启动的,那么父进程终止使得shell认为该命令已经执行完毕。2)保证子进程不是一个

进程组的组长进程。

3. 调用setsid创建一个新会话。setsid会导致:1)调用进程成为新会话的首进程。 2)调用

进程成为一个进程组的组长进程 。3)调用进程没有控制终端。(再次fork一次,保证

daemon进程,之后不会打开tty设备)

4. 将当前工作目录更改为根目录。

5. 关闭不在需要的文件描述符。

6. 其他:忽略SIGCHLD信号。


下面我们来自己创建一个守护进程:

#include<stdio.h>
#include<signal.h>
#include<unistd.h>
#include<stdlib.h>
#include<fcntl.h>
#include<sys/stat.h>

void creat_deamon()
{
	int i ;
	int fd0;
	pid_t pid;
	struct sigaction sa;
	umask(0);
	if((pid = fork()) < 0)
	{
		printf("fork error");
	}
	else if(pid != 0)
	{
		exit(0);
	}

	setsid();

	sa.sa_handler = SIG_IGN;
	sigemptyset(&sa.sa_mask);
	sa.sa_flags = 0;

	if(sigaction(SIGCHLD,&sa,NULL) < 0)
	{
		return ;
	}

	if((pid = fork()) < 0)
	{
		printf("fork error to child");
		return;
	}
	else if(pid != 0)
	{
		exit(0);
	}
	if(chdir("/") < 0)
	{
		printf("child dir error");
		return;
	}
	close(0);
	fd0 = open("dev/null",O_RDWR);
	dup2(fd0,1);
	dup2(fd0,2);
}

int main()
{
	creat_deamon();

	while(1)
	{
		sleep(1);
	}
	return 0;
}

运行结果:

wKiom1cxd4Ty2988AAAysFH4qKc867.png

其实对于创建守护进程而言,我们需要做的是:

(1)在父进程中执行fork并exit推出;

(2)在子进程中调用setsid函数创建新的会话;

(3)在子进程中调用chdir函数,让根目录 ”/” 成为子进程的工作目录;

(4)在子进程中调用umask函数,设置进程的umask为0;

(5)在子进程中关闭任何不需要的文件描述符


好了守护进程就是跟随着操作系统一直在运行,知道操作系统关闭时结束的进程,在操作系统中大多数进程任务就是守护进程,在开始的时候就进行启动控制操作系统监视的。

说一下为什么守护进程在创建时需要2次fork();

1 、第一次fork的作用是让shell 认为本条命令 已经终止,不用挂在终端输入上。还有一个作用是为后面setsid服务。setsid的调用者不能是进程组组长(group leader). 此时父进程是进程组组长。

    

    2 、setsid() 是本函数最重要的一个调用。它完成了daemon函数想要做的大部分事情。调用完整个函数。子进程是会话组长(sid==pid),也是进程组组长(pgid == pid),并且脱离了原来控制终端。到了这一步,基本上不管控制终端如何怎么样。新的进程都不会收到那些信号。

    3  、经过前面2个步骤,基本想要做的都做了。第2次fork不是必须的。也看到很多开源服务没有fork第二次。fork第二次主要目的是。防止进程再次打开一个控制终端。因为打开一个控制终端的前提条件是该进程必须是会话组长。再fork一次,子进程ID != sid(sid是进程父进程的sid)。所以也无法打开新的控制终端。


本文出自 “剩蛋君” 博客,请务必保留此出处http://memory73.blog.51cto.com/10530560/1771756

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值