会话,进程组,守护进程的特点,守护进程的步骤

一,守护进程的相关概念

守护进程特点:运行周期长(开机直至你关机或者系统关闭),在后台运行,不用和用户交互(脱离终端)

会话:每打开一个终端就建立了一个会话打开两个就建立了两个会话

会话首进程:该会话运行的第一个进程(bash)(用该bash的PID来标识该会话(id))哪怕该进程结束了会话id也不变

进程组:在会话中每运行一个命令,就是一个进程组同时会创建一个进程(该进程组中只有一个进程)以该进程的PID来命名整个进程组(也将这唯一的一个进程命名为进程组长)(进程组的id是该命令的PID或父进程的PID(父子进程))

组长进程:父子进程中父进程为组长进程(组长进程结束,并不会影响进程组,等所有组员结束进程组才会消失)
在这里插入图片描述
在这里插入图片描述

getsid(0):会话id

getpgrp():进程组id

setsid():创建一个新会话(从别的会话中拿出的子进程).此时子进程的PID就是该进程组id,也是新会话的组员(无终端)

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<assert.h>
int main()
{
   printf("pid=%d,sid=%d,gid=%d\n",getpid(),getsid(0),getpgrp());
                              //   进程id    会话id    进程组id(同进程id或父进程id)
}


同一终端中会话id相同

在这里插入图片描述

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<assert.h>
int main()
{
   fork();
   printf("pid=%d,sid=%d,gid=%d\n",getpid(),getsid(0),getpgrp());
                              //   进程id    会话id    进程组id(同进程id或父进程id)
}

在这里插入图片描述

行办法让程序长久运行,哪怕关闭了终端也能让那个其运行,这样就有了一个要求守护进程必须与终端脱离,重新创建一个会话,该会话与原来的终端不在关联了,将原来会话中的进程(不想使之关闭的那个进程)挪到新的会话中;改新会话和原来的终端没有联系,这样一来可以使之长久运行,及时关闭终端也不会导致进程关闭

二,守护进程步骤

守护进程编程的一个要求:将自己从当前会话中脱离出来放到一个新的会话中;

步骤:(重要)

1.fork()

1.fork()并退出父进程,留下子进程(子进程是一个普通的组员进程)
这是创建守护进程的第一步。由于守护进程是脱离控制终端的,完成这一步后就会在
Shell终端里造成程序已经运行完毕的假象。之后的所有工作都在子进程中完成,而用户在Shell终端里则可以执行其他命令,从而在形式上做到了与控制终端的脱离,在后台工作。
由于父进程先于子进程退出,子进程就变为孤儿进程,并由 init 进程作为其父进程收养。

2.setsid()

2.setsid()创建了一个新会话,脱离出来的进程pid标识该会话的会话id,只有你一个进程同时你也是进程组的组长;所以调用setsid()创建新会话的那个程序不能是进程组组长,也不能是会话首进程(因为将该进程脱离出原会话,会变成新的进程组组长同时会变成新会话的会话首进程,否则导致进程组id相同(不能让一个组长担当两个进程组组长否则会发生混乱))如果是一个进程组组长调用setsid()就会失败,所以要用普通组员来调用setsid();
在调用了 fork() 函数后,子进程全盘拷贝了父进程的会话期、进程组、控制终端等,虽然父进程退出了,但会话期、进程组、控制终端等并没有改变。这还不是真正意义上的独立开来,而 setsid() 函数能够使进程完全独立出来。

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

使当前进程脱离原会话的控制 使当前进程脱离原进程组的控制 使当前进程脱离原控制终端的控制
这样,当前进程才能实现真正意义上完全独立出来,摆脱其他进程的控制。

3.fork()

3.fork()并退出父进程(现在的父进程就是刚才的子进程)该步骤起一个保险的作用
现在,进程已经成为无终端的会话组长,但它可以重新申请打开一个控制终端,可以通过 fork() 一个子进程,该子进程不是会话首进程,该进程将不能重新打开控制终端。退出父进程。
也就是说通过再次创建子进程结束当前进程,使进程不再是会话首进程来禁止进程重新打开控制终端。

4.chdir(“/”)

4.chdir(“/”)将当前的路径改到根目录下。非必须(只是比较安全,怕我们处在一个可以被卸载的应用系统上)

5.umask()

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

6.close()

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

7.处理僵死进程

7.处理僵死进程

在这里插入图片描述
每个五秒给日志文件中写入当前的时间

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<assert.h>
#include<time.h>
#include<sys/stat.h>umask的头文件

int main()
{
   pid_t pid=fork();
   if(pid!=0)
   {
      exit(0);退出父进程
   }
    
    setsid();创建新会话
    pid=fork();
    if(pid!=0)
    {
       exit();
    }
    chdir("/");
    umask(0);
    int maxfd=getdtablesize();//文件描述符的多少
    for(int i=0;i<maxfd;++i)
    {
       close(i);//全部关闭
    }

   while(1)//周期性的打开一个文件
   {
      FILE* fd=fopen("/tmp/c219d.log","a");//打开文件
      if(fd==NULL)
      {
         break;
      }
      time_t tv;
      time(&tv);
      fprintf(fp,"Time is %s\n",asctime(localtime(&tv)));
              //先转为结构体,再将结构体转为字符串最后打印在文件里
      fclose(fp);
      sleep(5);
   }
}

tail -f c219d.log:查看日志文件(查看末尾几行,一旦有新数据自动刷新)

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值