Linux进程间通信—创建守护进程

守护进程

守护进程是生存期长的一种进程。
常常在系统引导装入时启动。
它们无控制终端,故称之后台运行。


为什么要引入守护进程?

由于在linux中,每一个系统与用户进行交流的界面称为终端,每一个从此终端开始运行的进程都会依赖这个终端,这个终端就称为这些进程的控制终端。当控制终端被关闭时,相应的进程都会自动关闭。但是守护进程却能突破这种限制,它被执行开始运转,直到整个系统关闭时才退出。

守护进程的特性:
1> 守护进程最重要的特性是后台运行。
2> 其次,守护进程必须与其运行前的环境隔离开来。
这些环境包括未关闭的文件描述符、控制终端、会话和进程组、工作目录以及文件创建掩码等。
这些环境通常是守护进程从父进程那里继承下来的。


守护进程的启动方式

(1)创建子进程,父进程退出

调用fork产生一个子进程,同时父进程退出,所有后续工作都在子进程中完成。
这样做会造成子进程没有父进程,变成一个孤儿进程(orphan)。
每当系统发现一个孤儿进程,就会自动由1号进程(init)收养它,这样,原先的子进程就会变成1号进程的子进程。
代码示例:

pid = fork();
if(pid>0)
    exit(0);

(2)在子进程中创建新会话

使用系统函数setsid()。
由于创建守护进程的第一步调用了fork函数来创建子进程,再将父进程退出。
由于在调用fork函数的时候,子进程全盘拷贝了父进程的会话期、进程组、控制终端等,虽然父进程退出了,但会话期、进程组、控制终端并没有改变,因此,还不是真正意义上的独立开来。
而调用setsid函数会创建一个新的会话并自任该会话的组长,调用setsid函数有下面3个作用:让进程摆脱原会话的控制,让进程摆脱原进程组的控制,让进程摆脱原控制终端的控制

进程组:是一个或多个进程的集合。进程组有进程组ID来唯一标识。除了进程号(PID)之外,进程组ID(GID)也是一个进程的必备属性。每个进程都有一个组长进程,其组长进程的进程号等于进程组ID。且该进程组ID不会因为组长进程的退出而受影响。
会话周期:会话期是一个或多个进程组的集合。通常,一个会话开始于用户登录,终止于用户退出,在此期间该用户运行的所有进程都属于这个会话期。
控制终端:由于在linux中,每一个系统与用户进行交流的界面称为终端,每一个从此终端开始运行的进程都会依赖这个控制终端。


(3)改变当前目录为根目录

使用fork函数创建的子进程继承了父进程的当前工作目录。
由于在进程运行中,当前目录所在的文件是不能卸载的,这对以后的使用会造成很多的不便。
利用chdir("/");把当前工作目录切换到根目录。


(4)重设文件权限掩码

umask(0);将文件权限掩码设为0,Deamon创建文件不会有太大麻烦


(5)关闭所有不需要的文件描述符

新进程会从父进程那里继承一些已经打开了的文件。
这些被打开的文件可能永远不会被守护进程读写,而它们一直消耗系统资源。
另外守护进程已经与所属的终端失去联系,那么从终端输入的字符不可能到达守护进程,守护进程中常规方法(如printf)输出的字符也不可能在终端上显示。
所以通常关闭从0到MAXFILE(确保全部关闭,采用Linux系统最大文件描述符值)的所有文件描述符。

for(i=0;ii++)
    close(i);

(注:有时还要处理SIGCHLD信号signal(SIGCHLD, SIG_IGN);防止僵尸进程(zombie))


创建守护进程代码实例

#include 
#include 
#include 

#define MAXFILE 64

int Daemon(void);

int Daemon()
{
    int i = 0;
    if(fork() != 0)
    {
        exit(0);  //父进程退出
    }
    setsid();       //成为新进程组组长和新会话领导,脱离控制终端
    chdir("/");     //切换工作目录为根目录
    umask(0);       //重设文件访问权限掩码
    for(; i < MAXFILE; i++)
    {
        close(i);//关闭所有从父进程继承来的文件
    }
    return 0;
}

int main()
{
    Daemon();
    while(1)
    {
        sleep(1);
    }
    return 0;
}

整个创建守护进程的过程完毕。

你可能感兴趣的:(Linux,programming,linux,终端,守护进程)