精华内容
下载资源
问答
  • 创建守护进程的步骤

    万次阅读 多人点赞 2018-08-03 21:30:55
    实现一个守护进程,其实就是将普通进程按照上述特性改造为守护进程的过程。 需要注意的一点是,不同版本的 Unix 系统其实现机制不同,BSD 和 Linux 下的实现细节就不同。 根据上述的特性,我们便可以创建一个简单的...

    什么是守护进程?

    答:守护进程是后台运行的、系统启动是就存在的、不予任何终端关联的,用于处理一些系统级别任务的特殊进程。

    实现思路:

    实现一个守护进程,其实就是将普通进程按照上述特性改造为守护进程的过程。
    需要注意的一点是,不同版本的 Unix 系统其实现机制不同,BSD 和 Linux 下的实现细节就不同。

    根据上述的特性,我们便可以创建一个简单的守护进程,这里以 Linux 系统下从终端 Shell 来启动为例。

    在此有必要说一下两个概念:会话和进程组。

    参考 https://www.cnblogs.com/zengyiwen/p/5755191.html

    进程都有父进程,父进程也有父进程,这就形成了一个以init进程为根的家族树。除此以外,进程还有其他层次关系:进程、进程组和会话。进程组和会话在进程之间形成了两级的层次:进程组是一组相关进程的集合,会话是一组相关进程组的集合。

    这样说来,一个进程会有如下ID:

    ·PID:进程的唯一标识。对于多线程的进程而言,所有线程调用getpid函数会返回相同的值。

    ·PGID:进程组ID。每个进程都会有进程组ID,表示该进程所属的进程组。默认情况下新创建的进程会继承父进程的进程组ID。

    ·SID:会话ID。每个进程也都有会话ID。默认情况下,新创建的进程会继承父进程的会话ID。

    前面提到过,新进程默认继承父进程的进程组ID和会话ID,如果都是默认情况的话,那么追根溯源可知,所有的进程应该有共同的进程组ID和会话ID。但是调用ps axjf可以看到,实际情况并非如此,系统中存在很多不同的会话,每个会话下也有不同的进程组。

    为何会如此呢?

    就像家族企业一样,如果从创业之初,所有家族成员都墨守成规,循规蹈矩,默认情况下,就只会有一个公司、一个部门。但是也有些“叛逆”的子弟,愿意为家族公司开疆拓土,愿意成立新的部门。这些新的部门就是新创建的进程组。如果有子弟“离经叛道”,甚至不愿意呆在家族公司里,他别开天地,另创了一个公司,那这个新公司就是新创建的会话组。由此可见,系统必须要有改变和设置进程组ID和会话ID的函数接口,否则,系统中只会存在一个会话、一个进程组。

    进程组和会话是为了支持shell作业控制而引入的概念。

    当有新的用户登录Linux时,登录进程会为这个用户创建一个会话。用户的登录shell就是会话的首进程。会话的首进程ID会作为整个会话的ID。会话是一个或多个进程组的集合,囊括了登录用户的所有活动。在登录shell时,用户可能会使用管道,让多个进程互相配合完成一项工作,这一组进程属于同一个进程组。

    当用户通过SSH客户端工具(putty、xshell等)连入Linux时,与上述登录的情景是类似的。

    通常,会话开始于用户登录,终止于用户退出,期间的所有进程都属于这个会话。一个会话一般包含一个会话首进程、一个前台进程组和一个后台进程组,控制终端可有可无;此外,前台进程组只有一个,后台进程组可以有多个,这些进程组共享一个控制终端。

    • 前台进程组:
      该进程组中的进程可以向终端设备进行读、写操作(属于该组的进程可以从终端获得输入)。该进程组的 ID 等于控制终端进程组 ID,通常据此来判断前台进程组。

    • 后台进程组:
      会话中除了会话首进程和前台进程组以外的所有进程,都属于后台进程组。该进程组中的进程只能向终端设备进行写操作。

    下图为会话、进程组、进程和控制终端之间的关系(登录 shell 进程本身属于一个单独的进程组)。

    Linux 守护进程的实现

    想了解更多关于会话 Sessions 内容,可以认真读一下 APUE 这本书。

    如果调用进程非组长进程,那么就能创建一个新会话:

    • 该进程变成新会话的首进程
    • 该进程成为一个新进程组的组长进程
    • 该进程没有控制终端,如果之前有,则会被中断(会话过程对控制终端的独占性)

    也就是说:组长进程不能成为新会话首进程,新会话首进程必定成为组长进程。

     

    1、fork()创建子进程,父进程exit()退出;

    这是创建守护进程的第一步。由于守护进程是脱离控制终端的,完成这一步后就会在Shell终端里造成程序已经运行完毕的假象。之后的所有工作都在子进程中完成,而用户在Shell终端里则可以执行其他命令,从而在形式上做到了与控制终端的脱离,在后台工作。

    由于父进程先于子进程退出,子进程就变为孤儿进程,并由 init 进程作为其父进程收养。

    2、在子进程调用setsid()创建新会话;

    在调用了 fork() 函数后,子进程全盘拷贝了父进程的会话期、进程组、控制终端等,虽然父进程退出了,但会话期、进程组、控制终端等并没有改变。这还不是真正意义上的独立开来,而 setsid() 函数能够使进程完全独立出来

     setsid()创建一个新会话,调用进程担任新会话的首进程,其作用有:

    • 使当前进程脱离原会话的控制
    • 使当前进程脱离原进程组的控制
    • 使当前进程脱离原控制终端的控制

    这样,当前进程才能实现真正意义上完全独立出来,摆脱其他进程的控制。

    3、再次 fork() 一个子进程,父进程exit退出;

    现在,进程已经成为无终端的会话组长,但它可以重新申请打开一个控制终端,可以通过 fork() 一个子进程,该子进程不是会话首进程,该进程将不能重新打开控制终端。退出父进程。

    也就是说通过再次创建子进程结束当前进程,使进程不再是会话首进程来禁止进程重新打开控制终端

    4、在子进程中调用chdir()让根目录“/”成为子进程的工作目录;

    这一步也是必要的步骤。使用fork创建的子进程继承了父进程的当前工作目录。由于在进程运行中,当前目录所在的文件系统(如“/mnt/usb”)是不能卸载的,这对以后的使用会造成诸多的麻烦(比如系统由于某种原因要进入单用户模式)。因此,通常的做法是让"/"作为守护进程的当前工作目录,这样就可以避免上述的问题,当然,如有特殊需要,也可以把当前工作目录换成其他的路径,如/tmp。改变工作目录的常见函数是chdir。(避免原父进程当前目录带来的一些麻烦)

    5、在子进程中调用umask()重设文件权限掩码为0;

    文件权限掩码是指屏蔽掉文件权限中的对应位。比如,有个文件权限掩码是050,它就屏蔽了文件组拥有者的可读与可执行权限(就是说可读可执行权限均变为7)。由于使用fork函数新建的子进程继承了父进程的文件权限掩码,这就给该子进程使用文件带来了诸多的麻烦。因此把文件权限掩码重设为0即清除掩码(权限为777),这样可以大大增强该守护进程的灵活性。通常的使用方法为umask(0)。(相当于把权限开发)

    6、在子进程中close()不需要的文件描述符;

    同文件权限码一样,用fork函数新建的子进程会从父进程那里继承一些已经打开了的文件。这些被打开的文件可能永远不会被守护进程读写,但它们一样消耗系统资源,而且可能导致所在的文件系统无法卸下。其实在上面的第二步之后,守护进程已经与所属的控制终端失去了联系。因此从终端输入的字符不可能达到守护进程,守护进程中用常规方法(如printf)输出的字符也不可能在终端上显示出来。所以,文件描述符为0、1和2 的3个文件(常说的输入、输出和报错)已经失去了存在的价值,也应被关闭。(关闭失去价值的输入、输出、报错等对应的文件描述符

    for (i=0; i < MAXFILE; i++)
        close(i); 

    7、守护进程退出处理

    当用户需要外部停止守护进程运行时,往往会使用 kill 命令停止该守护进程。所以,守护进程中需要编码来实现 kill 发出的signal信号处理,达到进程的正常退出。

     

    一张简单的图可以完美诠释之前几个步骤:

     

    至此为止,一个简单的守护进程就建立起来了。

    注意守护进程一般需要在 root 权限下运行。

    通过

    ps -ef | grep 'daemon'

    对比执行前后确实可以看到多了一个进程:

    并且产生了 daemon.log,里面是这样的时间标签

    1. Thu Dec 8 14:35:11 2016

    2. Thu Dec 8 14:36:11 2016

    3. Thu Dec 8 14:37:11 2016

    最后我们想退出守护进程,只需给守护进程发送 SIGQUIT 信号即可

    sudo kill -3 26454 

    再次使用 ps 会发现进程已经退出。

    程序如下:

    #include <unistd.h>
    #include <signal.h>
    #include <stdlib.h>
    #include <string.h>
    #include <fcntl.h>
    #include <sys/stat.h>
    #include <time.h>
    #include <stdio.h>
     
    static bool flag = true;
    void create_daemon();
    void handler(int);
     
    int main()
    {
    	time_t t;
    	int fd;
    	create_daemon();
    	struct sigaction act;
    	act.sa_handler = handler;
    	sigemptyset(&act.sa_mask);
    	act.sa_flags = 0;
    	if(sigaction(SIGQUIT, &act, NULL))
    	{
    		printf("sigaction error.\n");
    		exit(0);
    	}
    	while(flag)
    	{
    		fd = open("/home/mick/daemon.log", O_WRONLY | O_CREAT | O_APPEND, 0644);
    		if(fd == -1)
    		{
    			printf("open error\n");
    		}
    		t = time(0);
    		char *buf = asctime(localtime(&t));
    		write(fd, buf, strlen(buf));
    		close(fd);
    		sleep(60);
    	}
    	return 0;
    }
    void handler(int sig)
    {
    	printf("I got a signal %d\nI'm quitting.\n", sig);
    	flag = false;
    }
    void create_daemon()
    {
    	pid_t pid;
    	/*(1)-----创建一个进程来用作守护进程-----*/
    	pid = fork();
    	
    	if(pid == -1)
    	{
    		printf("fork error\n");
    		exit(1);
    	}
    	/*(1.1)-----------原父进程退出-------------*/
    	else if(pid)
    	{
    		exit(0);
    	}
     	/*(2)---setsid使子进程独立。摆脱会话控制、摆脱原进程组控制、摆脱终端控制----*/
    	if(-1 == setsid())
    	{
    		printf("setsid error\n");
    		exit(1);
    	}
      	/*(3)---通过再次创建子进程结束当前进程,使进程不再是会话首进程来禁止进程重新打开控制终端----*/
    	pid = fork();
    	if(pid == -1)
    	{
    		printf("fork error\n");
    		exit(1);
    	}
    	else if(pid)
    	{
    		exit(0);
    	}
      	/*(4)---子进程中调用chdir()让根目录成为子进程工作目录----*/
    	chdir("/");
    	int i;
    	/*(6)---关闭文件描述符(常说的输入,输出,报错3个文件)----*/
    	for(i = 0; i < 3; ++i)
    	{
    		close(i);
    	}
    	/*(5)---重设文件掩码为0(将权限全部开放)----*/
    	umask(0);
    	return;
    }
    

     

    以下为其他的两种实现,大同小异:

     C++实现 :

    #include<stdio.h>
    #include<fcntl.h>
    #include<sys/stat.h>
    #include<string.h>
    #include<sys/types.h>
    #include<unistd.h>
    #include<stdlib.h>
    
    int main(){
        pid_t pid;
        char buf[]="this is a dameon\n";
        pid=fork();//创建一个进程用来做守护进程
        if(pid>0){ //父进程退出,此时 子进程变为孤儿进程。
            exit(0);
        }
        setsid();   //使子进程独立1.摆脱原会话控制 2.摆脱原进程组的控制 3.摆脱控制终端的控制
        chdir("/"); //改变当前工作目录,这也是为了摆脱父进程的影响
        umask(0);   //重设文件权限掩码
        int i;
        for(i=0;i<1024;i++){    //关闭文件描述符(常说的输入,输出,报错3个文件),
            close(i);            //因为守护进程要失去了对所属的控制终端的联系,这三个文件要关闭
        }
    
        int fd=open("dameon.txt",O_CREAT|O_WRONLY|O_APPEND,0777);
        if(fd<0){
            perror("open");
            return -1;
        }
        while(1){
            write(fd,buf,strlen(buf)+1);
    		printf("This is a deamon!!\n");
            sleep(5);
        }
    
        close(fd);
    }

     

     

     

     

    C语言实现(有时间再研究):

    test.c文件

    #include <unistd.h>
    #include <stdio.h> 
    #include <time.h>
    #include <stdlib.h>
    
    void init_daemon(void);//守护进程初始化函数
    main()
    {
    	FILE *fp;
    	time_t t;
    	init_daemon();//初始化为Daemon
    	while (1)//每隔一分钟向test.log报告运行状态 
    	{
    		sleep(10);//睡眠一分钟 
    		if ((fp = fopen("test.log", "a")) >= 0)
    		{
    			t = time(0);
    			fprintf(fp, "I'm here at %sn",asctime(localtime(&t)) );
    			fclose(fp);
    		}
    	}
    }

    init.c文件

    #include<unistd.h>
    #include <signal.h> 
    #include <sys/param.h> 
    #include <sys/types.h> 
    #include <sys/stat.h>
    #include <stdlib.h>
    void init_daemon(void)
    {
    	int pid;
    	int i;
    
    	if (pid = fork())
    		exit(0);//是父进程,结束父进程 
    	else if (pid< 0)
    		exit(1);//fork失败,退出 
    				//是第一子进程,后台继续执行
    
    	setsid();//第一子进程成为新的会话组长和进程组长 
    			 //并与控制终端分离 
    	if (pid = fork())
    		exit(0);//是第一子进程,结束第一子进程 
    	else if (pid< 0)
    		exit(1);//fork失败,退出 
    				//是第二子进程,继续 
    				//第二子进程不再是会话组长
    
    	for (i = 0; i< NOFILE; ++i)//关闭打开的文件描述符 
    		close(i);
    	chdir("/tmp");//改变工作目录到/tmp 
    	umask(0);//重设文件创建掩模 
    	return;
    }

    编译:gcc –g –o test init.c test.c 
    执行:./test 
    查看进程:ps –ef 
    从输出可以发现test守护进程的各种特性满足上面的要求。

    展开全文
  • 本篇文章主要介绍了linux下如何创建守护进程的步骤,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
  • 实现思路:实现一个守护进程,其实就是将普通进程按照上述特性改造为守护进程的过程。需要注意的一点是,不同版本的 Unix 系统其实现机制不同,BSD 和 Linux 下的实现细节就不同。根据上述的特性,我们便可以创建一...

    7c923df1c4357ba958cba9c6e825f046.png

    什么是守护进程?

    答:守护进程是后台运行的、系统启动是就存在的、不予任何终端关联的,用于处理一些系统级别任务的特殊进程。

    实现思路:

    实现一个守护进程,其实就是将普通进程按照上述特性改造为守护进程的过程。
    需要注意的一点是,不同版本的 Unix 系统其实现机制不同,BSD 和 Linux 下的实现细节就不同。

    根据上述的特性,我们便可以创建一个简单的守护进程,这里以 Linux 系统下从终端 Shell 来启动为例。

    在此有必要说一下两个概念:会话和进程组。

    参考 https://www.cnblogs.com/zengyiwen/p/5755191.html

    进程都有父进程,父进程也有父进程,这就形成了一个以init进程为根的家族树。除此以外,进程还有其他层次关系:进程、进程组和会话。进程组和会话在进程之间形成了两级的层次:进程组是一组相关进程的集合,会话是一组相关进程组的集合。

    这样说来,一个进程会有如下ID:

    ·PID:进程的唯一标识。对于多线程的进程而言,所有线程调用getpid函数会返回相同的值。

    ·PGID:进程组ID。每个进程都会有进程组ID,表示该进程所属的进程组。默认情况下新创建的进程会继承父进程的进程组ID。

    ·SID:会话ID。每个进程也都有会话ID。默认情况下,新创建的进程会继承父进程的会话ID。

    前面提到过,新进程默认继承父进程的进程组ID和会话ID,如果都是默认情况的话,那么追根溯源可知,所有的进程应该有共同的进程组ID和会话ID。但是调用ps axjf可以看到,实际情况并非如此,系统中存在很多不同的会话,每个会话下也有不同的进程组。

    为何会如此呢?

    就像家族企业一样,如果从创业之初,所有家族成员都墨守成规,循规蹈矩,默认情况下,就只会有一个公司、一个部门。但是也有些“叛逆”的子弟,愿意为家族公司开疆拓土,愿意成立新的部门。这些新的部门就是新创建的进程组。如果有子弟“离经叛道”,甚至不愿意呆在家族公司里,他别开天地,另创了一个公司,那这个新公司就是新创建的会话组。由此可见,系统必须要有改变和设置进程组ID和会话ID的函数接口,否则,系统中只会存在一个会话、一个进程组。

    进程组和会话是为了支持shell作业控制而引入的概念。

    当有新的用户登录Linux时,登录进程会为这个用户创建一个会话。用户的登录shell就是会话的首进程。会话的首进程ID会作为整个会话的ID。会话是一个或多个进程组的集合,囊括了登录用户的所有活动。在登录shell时,用户可能会使用管道,让多个进程互相配合完成一项工作,这一组进程属于同一个进程组。

    当用户通过SSH客户端工具(putty、xshell等)连入Linux时,与上述登录的情景是类似的。

    通常,会话开始于用户登录,终止于用户退出,期间的所有进程都属于这个会话。一个会话一般包含一个会话首进程、一个前台进程组和一个后台进程组,控制终端可有可无;此外,前台进程组只有一个,后台进程组可以有多个,这些进程组共享一个控制终端。

    • 前台进程组:
      该进程组中的进程可以向终端设备进行读、写操作(属于该组的进程可以从终端获得输入)。该进程组的 ID 等于控制终端进程组 ID,通常据此来判断前台进程组。
    • 后台进程组:
      会话中除了会话首进程和前台进程组以外的所有进程,都属于后台进程组。该进程组中的进程只能向终端设备进行写操作。

    下图为会话、进程组、进程和控制终端之间的关系(登录 shell 进程本身属于一个单独的进程组)。

    aed637cb36d2c2cb3be46a353b5a60d9.png

    想了解更多关于会话 Sessions 内容,可以认真读一下 APUE 这本书。

    如果调用进程非组长进程,那么就能创建一个新会话:

    • 该进程变成新会话的首进程
    • 该进程成为一个新进程组的组长进程
    • 该进程没有控制终端,如果之前有,则会被中断(会话过程对控制终端的独占性)

    也就是说:组长进程不能成为新会话首进程,新会话首进程必定成为组长进程。

    1、fork()创建子进程,父进程exit()退出;

    这是创建守护进程的第一步。由于守护进程是脱离控制终端的,完成这一步后就会在Shell终端里造成程序已经运行完毕的假象。之后的所有工作都在子进程中完成,而用户在Shell终端里则可以执行其他命令,从而在形式上做到了与控制终端的脱离,在后台工作。

    由于父进程先于子进程退出,子进程就变为孤儿进程,并由 init 进程作为其父进程收养。

    2、在子进程调用setsid()创建新会话;

    在调用了 fork() 函数后,子进程全盘拷贝了父进程的会话期、进程组、控制终端等,虽然父进程退出了,但会话期、进程组、控制终端等并没有改变。这还不是真正意义上的独立开来,而 setsid() 函数能够使进程完全独立出来。

    setsid()创建一个新会话,调用进程担任新会话的首进程,其作用有:

    • 使当前进程脱离原会话的控制
    • 使当前进程脱离原进程组的控制
    • 使当前进程脱离原控制终端的控制

    这样,当前进程才能实现真正意义上完全独立出来,摆脱其他进程的控制。

    3、再次 fork() 一个子进程,父进程exit退出;

    现在,进程已经成为无终端的会话组长,但它可以重新申请打开一个控制终端,可以通过 fork() 一个子进程,该子进程不是会话首进程,该进程将不能重新打开控制终端。退出父进程。

    也就是说通过再次创建子进程结束当前进程,使进程不再是会话首进程来禁止进程重新打开控制终端。

    4、在子进程中调用chdir()让根目录“/”成为子进程的工作目录;

    这一步也是必要的步骤。使用fork创建的子进程继承了父进程的当前工作目录。由于在进程运行中,当前目录所在的文件系统(如“/mnt/usb”)是不能卸载的,这对以后的使用会造成诸多的麻烦(比如系统由于某种原因要进入单用户模式)。因此,通常的做法是让"/"作为守护进程的当前工作目录,这样就可以避免上述的问题,当然,如有特殊需要,也可以把当前工作目录换成其他的路径,如/tmp。改变工作目录的常见函数是chdir。(避免原父进程当前目录带来的一些麻烦)

    5、在子进程中调用umask()重设文件权限掩码为0;

    文件权限掩码是指屏蔽掉文件权限中的对应位。比如,有个文件权限掩码是050,它就屏蔽了文件组拥有者的可读与可执行权限(就是说可读可执行权限均变为7)。由于使用fork函数新建的子进程继承了父进程的文件权限掩码,这就给该子进程使用文件带来了诸多的麻烦。因此把文件权限掩码重设为0即清除掩码(权限为777),这样可以大大增强该守护进程的灵活性。通常的使用方法为umask(0)。(相当于把权限开发)

    6、在子进程中close()不需要的文件描述符;

    同文件权限码一样,用fork函数新建的子进程会从父进程那里继承一些已经打开了的文件。这些被打开的文件可能永远不会被守护进程读写,但它们一样消耗系统资源,而且可能导致所在的文件系统无法卸下。其实在上面的第二步之后,守护进程已经与所属的控制终端失去了联系。因此从终端输入的字符不可能达到守护进程,守护进程中用常规方法(如printf)输出的字符也不可能在终端上显示出来。所以,文件描述符为0、1和2 的3个文件(常说的输入、输出和报错)已经失去了存在的价值,也应被关闭。(关闭失去价值的输入、输出、报错等对应的文件描述符)

    7、守护进程退出处理

    当用户需要外部停止守护进程运行时,往往会使用 kill 命令停止该守护进程。所以,守护进程中需要编码来实现 kill 发出的signal信号处理,达到进程的正常退出。

    一张简单的图可以完美诠释之前几个步骤:

    bfde1d5f8604a5f125ac4426ee2ed36f.png

    至此为止,一个简单的守护进程就建立起来了。

    注意守护进程一般需要在 root 权限下运行。

    展开全文
  • 今天小编就来给大家科普一下什么是守护进程以及linux系统如何创建守护进程。一、守护进程是什么?Linux Daemon(守护进程)是运行在后台一种特殊进程。它独立于控制终端并且周期性地执行某种任务或等待处理某些发生...

    守护进程是什么?可能很多伙伴对于守护进程都不怎么了解吧?守护进程是操作系统后台的一种特殊进程,像Linux系统的大多数服务器都是通过守护进程实现的。今天小编就来给大家科普一下什么是守护进程以及linux系统如何创建守护进程。

    一、守护进程是什么?

    Linux Daemon(守护进程)是运行在后台的一种特殊进程。它独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。它不需要用户输入就能运行而且提供某种服务,不是对整个系统就是对某个用户程序提供服务。Linux系统的大多数服务器就是通过守护进程实现的。常见的守护进程包括系统日志进程syslogd、 web服务器httpd、邮件服务器sendmail和数据库服务器mysqld等。

    守护进程一般在系统启动时开始运行,除非强行终止,否则直到系统关机都保持运行。守护进程经常以超级用户(root)权限运行,因为它们要使用特殊的端口(1-1024)或访问某些特殊的资源。

    一个守护进程的父进程是init进程,因为它真正的父进程在fork出子进程后就先于子进程exit退出了,所以它是一个由init继承的孤儿进程。守护进程是非交互式程序,没有控制终端,所以任何输出,无论是向标准输出设备stdout还是标准出错设备stderr的输出都需要特殊处理。

    守护进程的名称通常以d结尾,比如sshd、xinetd、crond等

    二、创建守护进程的步骤

    首先我们要了解一些基本概念:

    1、进程组 :

    每个进程也属于一个进程组

    每个进程主都有一个进程组号,该号等于该进程组组长的PID号 。

    一个进程只能为它自己或子进程设置进程组ID号

    2、会话期:

    会话期(session)是一个或多个进程组的集合。

    setsid()函数可以建立一个对话期:

    如果,调用setsid的进程不是一个进程组的组长,此函数创建一个新的会话期。

    (1)此进程变成该对话期的首进程

    (2)此进程变成一个新进程组的组长进程。

    (3)此进程没有控制终端,如果在调用setsid前,该进程有控制终端,那么与该终端的联系被解除。 如果该进程是一个进程组的组长,此函数返回错误。

    (4)为了保证这一点,我们先调用fork()然后exit(),此时只有子进程在运行

    现在我们来给出创建守护进程的所需步骤:

    编写守护进程的一般步骤:

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

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

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

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

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

    说明:

    (1)在后台运行。

    为避免挂起控制终端将Daemon放入后台执行。方法是在进程中调用fork使父进程终止,让Daemon在子进程中后台执行。

    if(pid=fork())

    exit(0);//是父进程,结束父进程,子进程继续

    (2)脱离控制终端,登录会话和进程组

    有必要先介绍一下Linux中的进程与控制终端,登录会话和进程组之间的关系:进程属于一个进程组,进程组号(GID)就是进程组长的进程号(PID)。登录会话可以包含多个进程组。这些进程组共享一个控制终端。这个控制终端通常是创建进程的登录终端。

    控制终端,登录会话和进程组通常是从父进程继承下来的。我们的目的就是要摆脱它们,使之不受它们的影响。方法是在第1点的基础上,调用setsid()使进程成为会话组长:

    setsid();

    说明:当进程是会话组长时setsid()调用失败。但第一点已经保证进程不是会话组长。setsid()调用成功后,进程成为新的会话组长和新的进程组长,并与原来的登录会话和进程组脱离。由于会话过程对控制终端的独占性,进程同时与控制终端脱离。

    (3) 禁止进程重新打开控制终端

    现在,进程已经成为无终端的会话组长。但它可以重新申请打开一个控制终端。可以通过使进程不再成为会话组长来禁止进程重新打开控制终端:

    if(pid=fork())

    exit(0);//结束第一子进程,第二子进程继续(第二子进程不再是会话组长)

    (4)关闭打开的文件描述符

    进程从创建它的父进程那里继承了打开的文件描述符。如不关闭,将会浪费系统资源,造成进程所在的文件系统无法卸下以及引起无法预料的错误。按如下方法关闭它们:

    for(i=0;i 关闭打开的文件描述符close(i);>

    (5) 改变当前工作目录

    进程活动时,其工作目录所在的文件系统不能卸下。一般需要将工作目录改变到根目录。对于需要转储核心,写运行日志的进程将工作目录改变到特定目录如/tmpchdir("/")

    (6)重设文件创建掩模

    进程从创建它的父进程那里继承了文件创建掩模。它可能修改守护进程所创建的文件的存取位。为防止这一点,将文件创建掩模清除:umask(0);

    (7)处理SIGCHLD信号

    处理SIGCHLD信号并不是必须的。但对于某些进程,特别是服务器进程往往在请求到来时生成子进程处理请求。如果父进程不等待子进程结束,子进程将成为僵尸进程(zombie)从而占用系统资源。如果父进程等待子进程结束,将增加父进程的负担,影响服务器进程的并发性能。在Linux下可以简单地将SIGCHLD信号的操作设为SIG_IGN。

    signal(SIGCHLD,SIG_IGN);

    这样,内核在子进程结束时不会产生僵尸进程。这一点与BSD4不同,BSD4下必须显式等待子进程结束才能释放僵尸进程。

    三、创建守护进程

    在创建之前我们先了解setsid()使用:

    #include

    pid_t setsid(void);

    DESCRIPTION

    setsid()  creates a new session if the calling process is not a process

    group leader.  The calling process is the leader of  the  new  session,

    the  process group leader of the new process group, and has no control-

    ling tty.  The process group ID and session ID of the  calling  process

    are set to the PID of the calling process.  The calling process will be

    the only process in this new process group and in this new session.

    //调用进程必须是非当前进程组组长,调用后,产生一个新的会话期,且该会话期中只有一个进程组,且该进程组组长为调用进程,没有控制终端,新产生的group ID 和 session ID 被设置成调用进程的PID

    RETURN VALUE

    On success, the (new) session ID of the calling  process  is  returned.

    On  error,  (pid_t) -1  is  returned,  and errno is set to indicate the

    error.

    现在根据上述步骤创建一个守护进程:

    以下程序是创建一个守护进程,然后利用这个守护进程每个一分钟向daemon.log文件中写入当前时间

    01#include

    02#include

    03#include

    04#include

    05#include

    06#include

    07#include

    08#define ERR_EXIT(m) \

    09do\

    10{\

    11perror(m);\

    12exit(EXIT_FAILURE);\

    13}\

    14while (0);\

    15void creat_daemon(void);

    16int main(void)

    17{

    18time_t t;

    19int fd;

    20creat_daemon();

    21while(1){

    22fd = open("daemon.log",O_WRONLY|O_CREAT|O_APPEND,0644);

    23if(fd == -1)

    24ERR_EXIT("open error");

    25t = time(0);

    26char *buf = asctime(localtime(&t));

    27write(fd,buf,strlen(buf));

    28close(fd);

    29sleep(60);

    30}

    31return 0;

    32}

    33void creat_daemon(void)

    34{

    35pid_t pid;

    36pid = fork();

    37if( pid == -1)

    38ERR_EXIT("fork error");

    39if(pid > 0 )

    40exit(EXIT_SUCCESS);

    41if(setsid() == -1)

    42ERR_EXIT("SETSID ERROR");

    43chdir("/");

    44int i;

    45for( i = 0; i < 3; ++i)

    46{

    47close(i);

    48open("/dev/null", O_RDWR);

    49dup(0);

    50dup(0);

    51}

    52umask(0);

    53return;

    54}

    复制代码

    #include

    #include

    #include

    #include

    #include

    #include

    #include

    #define ERR_EXIT(m) \

    do\

    {\

    perror(m);\

    exit(EXIT_FAILURE);\

    }\

    while (0);\

    void creat_daemon(void);

    int main(void)

    {

    time_t t;

    int fd;

    creat_daemon();

    while(1){

    fd = open("daemon.log",O_WRONLY|O_CREAT|O_APPEND,0644);

    if(fd == -1)

    ERR_EXIT("open error");

    t = time(0);

    char *buf = asctime(localtime(&t));

    write(fd,buf,strlen(buf));

    close(fd);

    sleep(60);

    }

    return 0;

    }

    void creat_daemon(void)

    {

    pid_t pid;

    pid = fork();

    if( pid == -1)

    ERR_EXIT("fork error");

    if(pid > 0 )

    exit(EXIT_SUCCESS);

    if(setsid() == -1)

    ERR_EXIT("SETSID ERROR");

    chdir("/");

    int i;

    for( i = 0; i < 3; ++i)

    {

    close(i);

    open("/dev/null", O_RDWR);

    dup(0);

    dup(0);

    }

    umask(0);

    return;

    }

    结果:

    结果显示:当我一普通用户执行a.out时,进程表中并没有出现新创建的守护进程,但当我以root用户执行时,成功了,并在/目录下创建了daemon.log文件,cat查看后确实每个一分钟写入一次。为什么只能root执行,那是因为当我们创建守护进程时,已经将当前目录切换我/目录,所以当我之后创建daemon.log文件是其实是在/目录下,那肯定不行,因为普通用户没有权限,或许你会问那为啥没报错呢?其实是有出错,只不过我们在创建守护进程时已经将标准输入关闭并重定向到/dev/null,所以看不到错误信息。

    四、利用库函数daemon()创建守护进程

    其实我们完全可以利用daemon()函数创建守护进程,其函数原型:

    #include

    int daemon(int nochdir, int noclose);

    DESCRIPTION

    The daemon() function is for programs wishing to detach themselves from

    the controlling terminal and run in the background as system daemons.

    If nochdir is zero, daemon()  changes  the  process’s  current  working

    directory to the root directory ("/"); otherwise,

    If  noclose is zero, daemon() redirects standard input, standard output

    and standard error to /dev/null; otherwise,  no  changes  are  made  to

    these file descriptors.

    功能:创建一个守护进程

    参数:

    nochdir:=0将当前目录更改至“/”

    noclose:=0将标准输入、标准输出、标准错误重定向至“/dev/null”

    返回值:

    成功:0

    失败:-1

    现在我们利用daemon()改写刚才那个程序:

    01#include

    02#include

    03#include

    04#include

    05#include

    06#include

    07#include

    08#define ERR_EXIT(m) \

    09do\

    10{\

    11perror(m);\

    12exit(EXIT_FAILURE);\

    13}\

    14while (0);\

    15void creat_daemon(void);

    16int main(void)

    17{

    18time_t t;

    19int fd;

    20if(daemon(0,0) == -1)

    21ERR_EXIT("daemon error");

    22while(1){

    23fd = open("daemon.log",O_WRONLY|O_CREAT|O_APPEND,0644);

    24if(fd == -1)

    25ERR_EXIT("open error");

    26t = time(0);

    27char *buf = asctime(localtime(&t));

    28write(fd,buf,strlen(buf));

    29close(fd);

    30sleep(60);

    31}

    32return 0;

    33}

    复制代码

    #include

    #include

    #include

    #include

    #include

    #include

    #include

    #define ERR_EXIT(m) \

    do\

    {\

    perror(m);\

    exit(EXIT_FAILURE);\

    }\

    while (0);\

    void creat_daemon(void);

    int main(void)

    {

    time_t t;

    int fd;

    if(daemon(0,0) == -1)

    ERR_EXIT("daemon error");

    while(1){

    fd = open("daemon.log",O_WRONLY|O_CREAT|O_APPEND,0644);

    if(fd == -1)

    ERR_EXIT("open error");

    t = time(0);

    char *buf = asctime(localtime(&t));

    write(fd,buf,strlen(buf));

    close(fd);

    sleep(60);

    }

    return 0;

    }

    当daemon(0,0)时:

    结果同刚才一样,也是只有root才能成功,普通用户执行时看不到错误信息

    现在让daemon(0,1),就是不关闭标准输入输出结果:

    可以看到错误信息

    现在让daemon(1,0),就是不重定向,结果如下:

    这次普通用户执行成功了,以为没有切换到/目录下,有权限

    其实我们可以利用我们刚才写的创建守护进程程序默认daemon()实现:

    代码如下:

    01#include

    02#include

    03#include

    04#include

    05#include

    06#include

    07#include

    08#define ERR_EXIT(m) \

    09do\

    10{\

    11perror(m);\

    12exit(EXIT_FAILURE);\

    13}\

    14while (0);\

    15void creat_daemon(int nochdir, int noclose);

    16int main(void)

    17{

    18time_t t;

    19int fd;

    20creat_daemon(0,0);

    21while(1){

    22fd = open("daemon.log",O_WRONLY|O_CREAT|O_APPEND,0644);

    23if(fd == -1)

    24ERR_EXIT("open error");

    25t = time(0);

    26char *buf = asctime(localtime(&t));

    27write(fd,buf,strlen(buf));

    28close(fd);

    29sleep(60);

    30}

    31return 0;

    32}

    33void creat_daemon(int nochdir, int noclose)

    34{

    35pid_t pid;

    36pid = fork();

    37if( pid == -1)

    38ERR_EXIT("fork error");

    39if(pid > 0 )

    40exit(EXIT_SUCCESS);

    41if(setsid() == -1)

    42ERR_EXIT("SETSID ERROR");

    43if(nochdir == 0)

    44chdir("/");

    45if(noclose == 0){

    46int i;

    47for( i = 0; i < 3; ++i)

    48{

    49close(i);

    50open("/dev/null", O_RDWR);

    51dup(0);

    52dup(0);

    53}

    54umask(0);

    55return;

    56}

    复制代码

    #include

    #include

    #include

    #include

    #include

    #include

    #include

    #define ERR_EXIT(m) \

    do\

    {\

    perror(m);\

    exit(EXIT_FAILURE);\

    }\

    while (0);\

    void creat_daemon(int nochdir, int noclose);

    int main(void)

    {

    time_t t;

    int fd;

    creat_daemon(0,0);

    while(1){

    fd = open("daemon.log",O_WRONLY|O_CREAT|O_APPEND,0644);

    if(fd == -1)

    ERR_EXIT("open error");

    t = time(0);

    char *buf = asctime(localtime(&t));

    write(fd,buf,strlen(buf));

    close(fd);

    sleep(60);

    }

    return 0;

    }

    void creat_daemon(int nochdir, int noclose)

    {

    pid_t pid;

    pid = fork();

    if( pid == -1)

    ERR_EXIT("fork error");

    if(pid > 0 )

    exit(EXIT_SUCCESS);

    if(setsid() == -1)

    ERR_EXIT("SETSID ERROR");

    if(nochdir == 0)

    chdir("/");

    if(noclose == 0){

    int i;

    for( i = 0; i < 3; ++i)

    {

    close(i);

    open("/dev/null", O_RDWR);

    dup(0);

    dup(0);

    }

    umask(0);

    return;

    }

    关于linux系统创建守护进程的操作步骤就给大家讲解到这里了,有此工作需求的伙伴,可以按照小编的步骤进行创建。

    展开全文
  • linux下如何创建守护进程的步骤发布时间:2020-08-26 03:21:32来源:脚本之家阅读:151作者:xld_hung栏目:服务器这两天学习了linux 守护进程这个地方知识点挺多的,而且很重要,所以,今天添加一点小笔记。...

    linux下如何创建守护进程的步骤

    发布时间:2020-08-26 03:21:32

    来源:脚本之家

    阅读:151

    作者:xld_hung

    栏目:服务器

    这两天学习了linux 守护进程这个地方知识点挺多的,而且很重要,所以,今天添加一点小笔记。

    1,进程的概念:程序的一次动态执行过程。

    进程存在于内存当中,存在着  创建,调度,执行和消亡,进程号是进程的唯一标志,每一个进程都有自己独立的内存空间,在32位操作系统中,进程拥有0-4G的内存空间,其中0-3G属于用户,3G-4G属于内核,所以就出现了进程的执行模式:用户模式和内核模式。

    2,进程的类型:交互进程,批处理进程,守护进程

    3,守护进程:Linux中的后台服务进程,daemon进程

    4,守护进程的创建步骤:

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

    经过这步以后,子进程就会成为孤儿进程(父进程先于子进程退出, 此时的子进程,成为孤儿进程,会被init进程收养)。使用fork()函数,如果返回值大于0,表示为父进程,exit(0),父进程退出,子进程继续。

    (2)在子进程中创建新会话,使当前进程成为新会话组的组长。

    使用setsid()函数,如果当前进程不是进程组的组长,则为当前进程创建一个新的会话期,使当前进程成为这个会话组的首进程,成为这个进程组的组长。

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

    由于守护进程在后台运行,开始于系统开启,终止于系统关闭,所以要将其目录改为系统的根目录下。进程在执行时,其文件系统不能被卸下。

    (4)重新设置文件权限掩码。

    进程从父进程那里继承了文件创建掩码,所以可能会修改守护进程存取权限位,所以要将文件创建掩码清除,umask(0);

    (5)关闭文件描述符。

    子进程从父进程那里继承了打开文件描述符。所以使用close即可关闭。

    在这里要用到getdtablesize()函数得到这个进程打开文件的数目。

    按照上面的步骤即可完成守护进程的创建。

    具体的代码如下,守护进程的功能写的较为简单,往文件里写东西:

    #include

    #include

    #include

    #include

    #include

    #include

    int main()

    { // 1创建子进程 ,父进程退出

    pid_t pid = fork();

    if(pid<0)

    {

    perror("fork error");

    return -1;

    }

    else if(pid>0)

    {

    exit(0);

    }

    else

    {

    while(1)

    {

    //2 组长

    setsid();

    // 改变路径至根目录

    chdir("/tmp");

    //重设文件掩码

    umask(0);

    //关闭文件描述符

    int des=getdtablesize();

    int i=0;

    for(i=0;i

    {

    close(i);

    }

    }

    char buf[]="bat xld come!\n";

    int fd=open("xld.txt",O_WRONLY|O_CREAT |O_APPEND,0666);

    write(fd,buf,sizeof(buf));

    sleep(2);

    }

    return 0;

    }

    以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持亿速云。

    展开全文
  • 1,进程的概念:程序的一次动态执行过程。 进程存在于内存当中,存在着 创建,调度,执行和消亡,进程号是进程... 2,进程的类型:交互进程,批处理进程,守护进程 3,守护进程:Linux中的后台服务进程,daemon进程 ...
  • 进程存在于内存当中,存在着 创建,调度,执行和消亡,进程号是进程的唯一标志,每一个进程都有自己独立的内存空间,在32位操作系统中,进程拥有0-4G的内存空间,其中0-3G属于用户,3G-4G属于内核,所以就出现了进程...
  • 实现思路:实现一个守护进程,其实就是将普通进程按照上述特性改造为守护进程的过程。需要注意的一点是,不同版本的 Unix 系统其实现机制不同,BSD 和 Linux 下的实现细节就不同。根据上述的特性,我们便可以创建一...
  • 进程存在于内存当中,存在着 创建,调度,执行和消亡,进程号是进程的唯一标志,每一个进程都有自己独立的内存空间,在32位操作系统中,进程拥有0-4G的内存空间,其中0-3G属于用户,3G-4G属于内核,所以就出现了进程...
  • 守护进程的概念 Ssh、打印、ftp等均属于守护进程。进程类型为d。 特点:始终在后台运行,独立于任何终端,周期性的执行某种任务或等待处理特定事件。 会话和控制终端:linux以会话、进程组的方式管理进程,每个进程...
  • 创建守护进程关键步骤

    千次阅读 2020-08-25 22:22:45
    创建一个守护进程,有几个关键的步骤,也有几个地方需要注意,几个关键的步骤有:1:清除文件创建权限2:调用fork,然后使父进程退出3:调用setsid以创建一个新的会话4:切换工作目录5:关闭不需要的文件描述符 ...
  • 02守护进程学习之创建守护进程的步骤及其分析 1 守护进程的概念 1)脱离终端,运行于inux中的后台服务进程,不能直接和用户交互。不受用户登录、注销的影响。 2)在后台周期执行任务或等待处理某些事件。一般采用...
  • 何为守护进程 、进程组 / 组长进程 / 会话 / 会话首进程 、守护进程的创建步骤创建守护进程程序示例
  • 创建守护进程步骤

    2008-12-18 22:53:00
    8.2.1 实现守护进程的步骤在Linux系统中,要编程实现一个守护进程必须遵守如下的步骤。1.让init进程成为新产生进程的父进程。调用fork函数创建子进程后,使父进程立即退出。这样,产生的子进程将变成孤儿进程,并...
  • 创建守护进程

    2015-09-25 23:41:17
    创建守护进程的步骤: 1、让init进程成为新产生进程的父进程  具体方法:调用fork()函数创建子进程后,使父进程立即退出。 2、调用setsid函数 调用setsid函数,使得新创建的进程脱离控制终端,同时创建新的进程...
  • 守护进程的创建步骤

    千次阅读 2019-04-21 15:08:23
    转自 ...什么是守护进程? 答:守护进程是后台运行的、系统启动是就存在的、...实现一个守护进程,其实就是将普通进程按照上述特性改造为守护进程的过程。 需要注意的一点是,不同版本的 Unix 系统其实现机制不同,BSD ...
  • 守护进程创建步骤

    2013-10-02 10:45:19
    daemon进程步骤 1. 调用fork产生一个子进程,同时父进程退出,所有后续工作都在子进程中完成。 如果我们是从命令行执行该程序,这可以造成程序执行完毕假象,shell会回去等待下一条命令; 刚刚通过fork...
  • linux创建守护进程

    2017-03-15 19:16:24
    创建守护进程的步骤: 1、创建子进程,父进程退出此时,子进程为孤儿进程,与终端脱离的部分关系 2、在子进程中创建新会话 => setsid()此时,子进程为新建会话组的组长,  彻底脱离于原bash会话组的关系 3、...
  • 一,守护进程概述 Linux Daemon(守护进程)是运行在后台一种特殊进程。它独立于控制终端并且周期性地执行某种任务或等待处理某些发生事件。...Linux系统大多数服务器就是通过守护进程实现。常见守护进...
  • LinuxDaemon(守护进程)是运行在后台一种特殊进程。它独立于控制终端并且周期性地执行某种任务或等待处理某些发生事件。它不需要用户输入就能运行而且提供某种服务,不是对整个系统就是对某个用户程序提供服务...
  • 创建子进程,父进程退出 这是编写守护进程的第一步。... 在子进程中创建新会话 这个步骤创建守护进程中最重要的一步,虽然它的实现非常简单,但它的意义却非常重大。在这里使用的是系统函数setsid,在具体

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 382
精华内容 152
关键字:

创建守护进程的步骤