守护进程:
在Linux操作系统中,是一种运行在后台的特殊进程,独立于控制终端并且周期性的执行某种任务或等待处理某些发生的事件。因为由于在Linux中,每个系统与用户进行交流的界面称为终端,每个从此终端开始运行的进程都会依附于这个终端,这个终端被称为这些进程的控制终端,当控制终端被关闭的时候,相应的进程都会自动关闭。但是守护进程却能突破这种限制,它脱离终端的目的是为了避免进程在运行的过程中的信息在任何终端中显示并且进程也不会被任何终端所产生的终端信息所打断。它从被执行的时候开始运转,直到整个系统关闭才退出。
守护进程的步骤为:
1)创建子进程,父进程退出;
因为守护程序是脱离控制终端的,所以要给shell终端制造一个程序已经运行完毕的假象,即创建完子进程后,所有的工作都在子进程中完成,shell终端可以用以执行其他命令,可以形式上做到与控制终端脱离。这时要提及一个“孤儿进程”,这是因为父进程先于子进程退出的。每当出现一个出现一个孤儿进程的时候,系统会自动地会将它变为1号进程即init进程的子进程
2)在子进程中创建新的会话;
使用系统函数setsid()来创建新的会话,并担任该会话组组长。
为什么要调用setsid函数来创建守护函数呢?因为创建进程的第一步中调用fork()函数来创建了子进程,又退出了父进程。在调用fork()函数时,子进程完全了拷贝了父进程的会话期、进程组、控制终端等,所以即便符进程退出了,但所拷贝的会话期、进程组控制终端等并没有改变,并没有做到完全独立。而setsid()函数可以将进程完全独立于其他进程的控制。
3)改变当前目录为根目录;
改变工作目录的常见函数是chdir(),使用fork()创建的子进程所继承的目录是父进程的当前目录,在程序运行的过程中会出现问题,所以更改当前目录为根目录
4)重设文件权限掩码;
文件权限掩码是指屏蔽掉文件权限中的对应位,fork()函数创建的子进程同样地也继承了父进程的文件权限掩码,把文件权限掩码设置为0,可以大大增强守护进程的灵活性,通常用umask(0)来设置
5)关闭文件描述符;
还是因为fork()函数所创建的子进程对父进程的完全继承,如果父进程打开了一些文件,在子进程里也会同样的打开,如果子进程并不会用到这些文件,那么打开这些文件所用到的文件描述符将成为一种资源的浪费,消耗系统资源,同时也会导致所在的文件系统无法卸载。其实因为守护进程已经与所属的控制终端脱离,因而从终端上输入的字符不可能达到守护进程,守护进程中用常规方法输出的字符也不可能在终端上显示出来,理应被关闭,关闭的方法:
for(i = 0;i < MAXFILE;i++)
close(i);
6)守护进程退出处理。
利用kill发出signal信号从而达到进程的正常退出。
如下图所示:
示例代码:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#define MAXFILE 65535
void sigterm_handler(int arg);
volatile sig_atomic_t _running = 1;
int main()
{
pid_t pc;
pid_t pid;
int i;
int fd;
int len;
int flag = 1;
char *buf = "This is a Dameon";
len = strlen(buf);
pc = fork(); //创建子进程,父进程退出
if(pc < 0)
{
printf("Error!\n");
exit(1);
}
else
{
if(pc > 0)
{
exit(0); //退出父进程
}
}
pid = setsid(); //在子进程中创建新的会话
if(pid < 0)
{
printf("Error!\n");
}
chdir("/"); //将当前目录改变为根目录
umask(0); //重设文件权限掩码
for(i = 0;i < MAXFILE;i++) //关闭文件描述符
{
close(i);
}
signal(SIGTERM,sigterm_handler); //守护进程退出处理
while(_running)
{
fd = open("hello.txt",O_CREAT | O_WRONLY | O_APPEND,0600);
if((1 == flag) && (fd < 0))
{
printf("Error!\n");
flag = 0;
exit(1);
}
write(fd,buf,len);
close(fd);
sleep(1);
}
return 0;
}
void sigterm_handler(int arg)
{
_running = 0;
}
这时的文件hello.txt 是存放在根目录下的,因为守护进程已经将当前目录改为根目录,而hello.txt是存放在当前目录下的。