良许Linux教程网 干货合集 Linux系统守护进程详解

Linux系统守护进程详解

什么是守护进程?例如淘宝、支付宝等必须7*24小时不停的运行,这就是一个非常典型的守护进程。下面为大家详解讲解一下Linux系统中守护进程的具体内容。

Linux系统守护进程详解

1、查看系统中的进程

 ps axj1

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

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

2、setsid函数 1>创建守护进程最关键的一步是调用setsid函数创建一个新的Session,并成为Session Leader。

 #include
 pid_t setsid(void);12

返回值:该函数调用成功时返回新创建的Session的id(其实也就是当前进程的id),出错返回-1。

2>需要注意的是,,调用这个函数之前,当前进程不允许是进程组的Leader,否则该函数返回-1。 解决办法:先fork再调用setsid,fork创建的子进程和父进程在同一个进 程组中,进程组的Leader必然是该组的第一个进程,所以子进程不可能是该组的第一个进程,在子 进程中调用setsid就不会有问题了。

3>成功调用该函数的结果是: \1. 创建一个新的Session,当前进程成为Session Leader,当前进程的id就是Session的id。 \2. 创建一个新的进程组,当前进程成为进程组的Leader,当前进程的id就是进程组的id。 \3. 如果当前进程原本有一个控制终端,则它失去这个控制终端,成为一个没有控制终端的进程。所谓失去控制终端是指,原来的控制终端仍然是打开的,仍然可以读写,但只是一个普通的打开文件而不是控制终端了。

3、创建守护进程的步骤 1>调用umask将文件模式创建屏蔽字设置为0.

 umask(0);//umask必须清0,否则创建文件受系统默认权限的影响1

文件权限掩码是屏蔽掉文件权限中的对应位。由于使用fork()函数新创建的子进程继承了父进程的文件权限掩码,这就给该子进程使用文件带了很多的麻烦(比如父进程中的文件没有执行文件的权限,然而在子进程中希望执行相应的文件这个时候就会出问题)。因此在子进程中要把文件的权限掩码设置成为0,即在此时有最大的权限,这样可以大大增强该守护进程的灵活性。

2>调用fork,父进程退出(exit)。 原因: 1)如果该守护进程是作为一条简单的shell命令启动的,那么⽗父进程终止使得shell认为该命令已经执行完毕。 2)保证子进程不是一个进程组的组长进程。

3>调用setsid创建一个新会话。 setsid会导致: 1)调用进程成为新会话的首进程。 2)调用进程成为一个进程组的组长进程 。 3)调用进程没有控制终端。(再次fork一次,保证daemon进程,之后不会打开tty设备)

调用setsid的原因: 由于创建守护进程的第一步是调用fork()函数来创建子进程,再将父进程退出。由于在调用了fork()函数的时候,子进程拷贝了父进程的会话期、进程组、控制终端等资源、虽然父进程退出了,但是会话期、进程组、控制终端等并没有改变,因此,需要用setsid()韩式来时该子进程完全独立出来,从而摆脱其他进程的控制。

4>将当前工作目录更改为根目录。 防止当前目录有一个目录被删除,导致守护进程无效。 使用fork()创建的子进程是继承了父进程的当前工作目录,由于在进程运行中,当前目录所在的文件系统是不能卸载的,这对以后使用会造成很多的麻烦。因此通常的做法是让“/”作为守护进程的当前目录,当然也可以指定其他的别的目录来作为守护进程的工作目录。

5>关闭不再需要的文件描述符。 同文件权限码一样,用fork()函数新建的子进程会从父进程那里继承一些已经打开了的文件。这些文件被打开的文件可能永远不会被守护进程读写,如果不进行关闭的话将会浪费系统的资源,造成进程所在的文件系统无法卸下以及引起预料的错误。

如:关闭标准输入流、标准输出流、标准错误流。

 close(0);
 close(1);
 close(2);123

6>其他:忽略SIGCHLD信号。

 signal(SIGCHLD,SIG_IGN);1

4、自己创建守护进程:

   1 /**************************************
   2 *文件说明:mydaemon.c
   3 *作者:段晓雪
   4 *创建时间:2024年11月20日 星期三 12时26分43秒
   5 *开发环境:Kali Linux/g++ v6.3.0
   6 ****************************************/
   7
   8 #include
   9 #include
  10 #include
  11 #include
  12 #include
  13 #include
  14
  15 void mydaemon()
  16 {
  17     umask(0);//将文件模式创建屏蔽字设为0
  18     pid_t pid = fork();//创建子进程
  19     if(pid == -1)
  20         perror("fork error");
  21     else if(pid == 0)//child
  22     {
  23         setsid();//创建新会话
  24         if(chdir("/") 

ps axj | grep mydaemon查看精灵进程: 这里写图片描述

5、调用系统函数daemon创建守护进程

 #include 
 int daemon(int nochdir, int noclose);12

1>daemon函数主要用于希望脱离控制台,以守护进程的形式在后台运行的程序。 2>当nochdir为0时,daemon将更改当前进程的目录为root(“/”)目录。 3> 当noclose为0时,daemon将进程的STDIN,STDOUT,STDERR都重定向到/dev/null。 /dev/null:linux下的黑洞,写入的所有数据会直接丢弃。

用daemon函数创建守护进程:

   1 /**************************************
   2 *文件说明:daemon.c
   3 *作者:段晓雪
   4 *创建时间:2024年11月22日 星期五 06时53分52秒
   5 *开发环境:Kali Linux/g++ v6.3.0
   6 ****************************************/
   7
   8 #include
   9 #include
  10
  11 int main()
  12 {
  13     daemon(1,1);//创建守护进程
  14     while(1);
  15     return 0;
  16 }12345678910111213141516

这里写图片描述

三、如何杀死守护进程?

1> 利用ps axj | grep 守护进程名字找到相应的守护进程,然后用kill -9 进程号将对应进程杀死。 这里写图片描述

2>利用ps -ef命令查找相应的守护进程,再用kill命令将其杀死。 这里写图片描述 …… 这里写图片描述

3>也可创建shell脚本对进程的启动、关闭、重启进行自动管理。

四、为什么有人创建守护进程会fork两次?

一个daemon函数常见的实现:

 int daemon(void)  
 {  
     pid_t pid = fork();  //第一次fork
 
     if( pid != 0 )
         exit(0);//parent  
 
     //first children  
     if(setsid() == -1)  
     {  
        printf("setsid failed\n");  
        assert(0);  
        exit(-1);  
     }  
 
     umask(0);  
 
     pid = fork();  //第二次fork
 
     if( pid != 0)
         exit(0);  
 
     //second children  
     chdir ("/");  
 
     for (int i = 0; i 

可以看到上面的代码里我fork了两次,虽然说这并不是必须的,但是这的确是对守护进程做出了一些更优化的操作。

首先第一次fork:这里第一次fork的作用就是让shell认为这条命令已经终止,不用挂在终端输入上;再一个是为了后面的setsid服务,因为调用setsid函数的进程不能是进程组组长(会报错Operation not permitted),如果不fork子进程,那么此时的父进程是进程组组长,无法调用setsid。所以到这里子进程便成为了一个新会话组的组长。

第二次fork:第二次fork是为了避免后期进程误操作而再次打开终端。因为打开一个控制终端的前提条件是该进程必须为会话组组长,而我们通过第二次fork,确保了第二次fork出来的子进程不会是会话组组长。

下面罗列一下控制终端会产生哪些信号。程序中只要处理好这些信号,同样能达到上面函数实现的目的。

//后台进程读取/写入终端输入产生下面两个信号,或者控制终端不存在情况读取和写入会产生

   signal(SIGTTOU, SIG_IGN);
    signal(SIGTTIN, SIG_IGN);12

//按CTRL-C ,CTRL-\ CTRL-Z会向前台进程组发送下面这些信号

    signal(SIGINT,  SIG_IGN );
    signal(SIGQUIT, SIG_IGN );
    signal(SIGTSTP, SIG_IGN );123

//终端断开,会给会话组长或孤儿进程组所有成员发送下面信号

    signal(SIGHUP,  SIG_IGN );1

还有有些信号也可以由终端shell产生,需要关注

 signal(SIGCONT, SIG_IGN );
 signal(SIGSTOP, SIG_IGN );12

上面这些信号,应该有些程序缺省处理(SIG_DFL)本身动作就是忽略(SIG_IGN),不是退出进程。不过按照上面写也不会造成什么问题。

至此关于Linux系统守护进程的具体 内容分享完毕,欢迎大家在评论区留言。

以上就是良许教程网为各位朋友分享的Linux系统相关内容。想要了解更多Linux相关知识记得关注公众号“良许Linux”,或扫描下方二维码进行关注,更多干货等着你!

137e00002230ad9f26e78-265x300

本文由 良许Linux教程网 发布,可自由转载、引用,但需署名作者且注明文章出处。如转载至微信公众号,请在文末添加作者公众号二维码。
良许

作者: 良许

良许,世界500强企业Linux开发工程师,公众号【良许Linux】的作者,全网拥有超30W粉丝。个人标签:创业者,CSDN学院讲师,副业达人,流量玩家,摄影爱好者。
上一篇
下一篇

发表评论

联系我们

联系我们

公众号:良许Linux

在线咨询: QQ交谈

邮箱: yychuyu@163.com

关注微信
微信扫一扫关注我们

微信扫一扫关注我们

关注微博
返回顶部