1.1 认识守护进程
1、守护进程也称为精灵进程(daemon),是运行在后台的一种特殊进程。它独立于控制终端并且周期性的执行某种任务或等待某些发生的事件。守护进程是一种很有用的进程。Linux的大多数服务器就是用守护进程实现的。比如:ftp服务器,ssh服务器,web服务器httpd等。同时,守护进程完成许多系统任务。比如,作业规划进程crond等。
2、Linux系统启动时会启动很多系统服务进程,这些系统服务进程没有控制终端,不能直接和用户交互。其他进程都是在用户登录或运行程序时创建,在运行结束或用户注销时终止,但是系统服务进程(守护进程)不受用户登录和注销的影响,它们一直运行着。这种进程有一个名称叫守护进程(daemon)。
3、用ps -axj命令查看系统中的进程。参数a表示不仅列当前用户的进程,也列出所有其他用户的进程;参数x表示不仅列出有控制终端的进程,也列出所有无控制终端的进程;参数j表示列出与作业控制相关的进程。
在这里插入图片描述
解释:
(1)凡是TPGID一栏写着-1的都是没有控制终端的进程,也就是守护进程。
(2)在COMMAND一列用[]括起来的名字表示内核线程,这些线程在内核里创建,没有用户控件代码,因此没有程序文件名和命令行,通常采用以k开头的名字,表示Kernel。
(3)init进程是所有文件的父进程,udevd负责维护/dev目录下的设备文件,acpid负责电源管理syslogd负责维护/var/log下的日志文件。
(4)守护进程采用以d结尾的名字,表示daemon。
2.1 创建守护进程
创建守护进程最关键的一步是调用setsid函数创建一个新的session,并且成为session leader。
2.1.1 setsid语法

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

注意:
(1)该函数调用成功时返回新创建session的id(其实就是当前进程的id),出错返回-1。
(2)调用这个函数之前,当前进程不允许是进程组的leader,否则该函数返回-1。
(3)要保证当前进程不是进程组的leader也很容易,只要先fork再调用setsid就行了。fork创建的子进程和父进程在同一个进程组中,进程组的leader必然是该组的第一个进程,所以子进程不可能是该组的第一个进程,在子进程中调用setsid就不会出现问题了。
2.1.2 成功调用函数的结果
(1)创建一个新的session,当前进程成为session leader,当前进程id就是session的id。
(2)创建一个新的进程组,当前进程成为进程组的leader,当前进程的id就是进程组的id。
(3)如果当前进程原本有一个控制终端,则它失去这个控制终端,成为一个没有控制终端的进程。所谓失去控制终端是指原来的控制终端仍然是打开的,仍然可以读写,但只是一个普通的打开文件而不是控制终端了。
3.1 守护进程代码

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

void mydaemon(void)
{
		int i;
		int fd0;
		pid_t pid;
		struct sigaction sa;
		
		umask(0);
		
		pid = fork();
		if(pid < 0){
				perror("fork error!\n");
				exit(1);
		}else if(pid > 0){
				exit(0);
		}
		
		setsid(); //创建一个会话
		
		sa.sa_handler = SIG_IGN;
		sa.sa_flags = 0;
		sigemptyset(&sa.sa_mask);
		
		if(sigaction(SIGCHLD, &sa, NULL) < 0){//注册子进程退出忽略信号
				return ;
		}
		
		if(chdir("/") < 0){//将当前工作目录更改为根目录
				printf("chlid dir error!\n");
				return;
		}
		
		//关闭不需要的文件描述符,或者重定向到/dev/null
		close(0);
		fd0 = open("/dev/null", O_RDWR);
		dup2(fd0, 1);
		dup2(fd0, 2);
}

int main()
{
		mydaemon();
		while(1){
				sleep(1);
		}
}

运行结果:
在这里插入图片描述
释:
(1)chdir:更改当前目录或文件夹。头文件:#include<unistd.h>。定义函数:int chdir(const char* path)。返回值:成功返回0,失败返回-1。
(2)daemon:用于创建守护进程。头文件:#include<unistd.h>。定义函数:int daemon(int nochdir, int noclose)。返回值:成功返回0,失败返回-1。其中,当nochdir为0时,daemon将更改当前目录为根目录“/”;当noclose为0时,daemon将进程的STDIN、STDOUT和STDERR重定向为/dev/null。
(3)dup:复制文件句柄。头文件:#include<unistd.h>;。定义函数:int dup(int fd)。返回值:成功返回新的文件描述符;失败返回-1。
(4)dup2:复制文件句柄。头文件:#include<unistd.h>。定义函数:int dup2(int oldhandle, int newhandle)。返回值:成功返回新的文件描述符;失败返回-1。
(5)sigaction:查询或设置信号处理方式。头文件:#include<signal.h>。定义函数:int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact)。返回值:成功返回0;失败返回-1。该函数会依参数signum指定的信号编号设置信号的处理函数,参数signum可以指定SIGKILL和SIGSTOP以外的所有信号。act信号处理函数参数采用指针函数的方式,参数有:
1)sa_handle:与signal的参数handle相同,代表新的信号处理函数;
2)sa_mask:设置在处理该信号时暂时将sa_mask指定的信号集搁置;
3)sa_flags:用来设置信号处理的其他相关操作。

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐