Linux-进程间关系与守护进程

这里写图片描述

进程组

概念

进程组是一个或多个进程的集合

查看命令

 ps axj  | grep sleep | grep -v grep


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


grep sleep:查看只含sleep的信息
grep -v grep:不看grep进程信息

当我们输入命令,“回车键一按”,一个进程组就产生了,来看例子

这里写图片描述

sleep命令执行,进程组成立。

进程:9768 9769 9770
进程组组长(PGID):9768

kill -9 杀掉进程组长,进程组依然存在

这里写图片描述

特性
  • 有唯一的组ID
  • 可以有一个进程组组长,一般父进程为组长
  • 组长进程可以创建进程,终止进程
  • 只要进程组有一个进程在,则该进程组就存在,这与其组长进程是否存在无关
  • 通常他们与同一个作业相关联,可以收到来自同一个终端的信号

例子:学校里,每个班就是一个进程组,每个同学就是一个进程

作业

概念:作业是用户需要计算机完成的某项任务,是要求计算机所做工作的集合。一个作业的完成要经过作业提交、作业收容、作业执行和作业完成4个阶段。一个作业需要一个或多个进程组配合完成

简单来讲作业就是系统中一项任务
进程组执行任务即执行作业。为了完成作业,往往需要多个进程组相互协作。

Shell分前后台来控制的不是进程是作业(Job)或者进程组(Process Group)。
1个前台作业可以由多个进程组成,1个后台也可以由多个进程组成,Shell可以运1个前台作业和任意多个后台作业,这称为作业控制。

作业与进程组的区别:如果作业中的某个进程创建了子进程,则进程不属于作业。
一旦作业运行结束,Shell就把自己提到前台(子进程还在,可是子进程不属于作业),如果原来的前台进程还存在(如果这个子进程还没终止),它自动变为后台进程组。

我们在重新理解下,在前台新起作业,shell是法运行,因为他被提到了后台。 但是如果前台进程退出,shell就被提到了前台,所以可以继续接受用户输入,。 来看例子证明这句话

#include <stdio.h>
#include <unistd.h>
int main(  )
{
      pid_t pid=fork(  );
      if(pid<0)
      {
            perror(  "  fork");
            return 1;
      }
      else if(  pid==0)
      {
            while(  1)
            {
                  printf("i am child!,id:%d\n",getpid(  ));
                  sleep(  1);
            }
      }
      else
      {

          int i=10;
          while(  i--)
          {

            printf("i am father,id is:%d\n",getpid(  ));
            sleep(1);
          }

      }
      return 0;
}

这里写图片描述

在前台新起作业 ./a.out,shell是法运行,因为他被提到了后台。 但是如果前台进程退出,shell就被提到了前台,所以可以继续接受用户输入,

但此时子进程并没有退出,而是退到后台,继续执行,往显示器上输出内容

查看进程信息如下

这里写图片描述

此时,用kill -9杀掉即可。

这里注意:后台进程可以往显示器输出内容,但是不能从显示器读取内容。

作业控制

Shell分前后台来控制的不是进程是作业(Job)或者进程组(Process Group)。
1个前台作业可以由多个进程组成,1个后台也可以由多个进程组成,Shell可以运1个前台作业和任意多个后台作业,这称为作业控制。

查看作业命令:jobs
将作业提到后台:ctrl +z
将作业提到前台:fg (作业编号)
杀掉前台作业:ctrl+c

kill -9 前台后台作业均有效

来看下面例子:新起五个作业

这里写图片描述
这里写图片描述

先用kill -9 杀掉2976,成功杀掉
然后用ctrl +c 杀掉后台进程,失败,因为非kill信号杀不掉后信号,只有当进程被提到前台(从内核态切换到用户态时)才会有效

然后用jobs查看作业,执行fg 1,将作业1提到前台,然互用ctrl +c 杀掉,成功。

与作业控制相关的信号:
信号编号默认动作对应事件
SIGTTIN21停止知道下一个SIGCCHLD后台进程从终端读
SIGCHLD17忽略一个子进程停止或终止
SIGCONT18忽略继续进程如果该进程已停止
SIGTSTP20停止知道下一个SIGCCHLD来自终端的停止信号
SIGKILL9终止杀死进程
SIGTERM15终止软件终止信号

来看例子
执在前台行cat命令结果
这里写图片描述

你在显示器输入什么,直接有给你显示在显示器上
现在,我们将cat命令放到后台
这里写图片描述

  • jobs命令可以查看当前有哪些作业。
  • fg命令可以将某个作业提到前台运行,如果该作业的进程组正在后台运行则提到前台运行,如果该作业处于停止状态,则给进程组的每个进程SIGCONT信号使它继续运行。参数%1表示将第1个作业提到前台运行。cat提到前台运行后,挂起等待终端输入,当输入hello并回车后,cat打印出同样的内容,然后继续挂起等待输入。如果输按下Ctrl-Z则向所有前台进程发SIGTSTP信号,该信号的默认动作是使进程
    停止,cat继续以后台作业的形式存在。

  • bg命令可以让某个停止的作业在后台继续运⾏,也需要给该作业的进程组的每个进程发SIGCONT信号。cat进程继续运行,要读终端输入,然而它在后台不能读终端输人,所以又收到SIGTTIN信号停止。

# kill -15 4872
# jobs
[1]+ Stopped cat
# fg 1
cat
Terminated

用kill命令给一个停止的进程发SIGTERM(15)信号,这个信号并不会立刻处理,而要等进程准备继续运行之前处理,默认动作是终止进程。但如果给一个停止的进程发SIGKILL信号就不同了。

总结:后台进程不能从终端下读取数据,但能写

这里写图片描述

守护进程

守护进程概念

守护进程也称精灵进程(Daemon),是运⾏在后台的⼀种特殊进程。它独⽴于控制终端并且周期性地执⾏

某种任务或等待处理某些发生的事件。守护进程是一种很有用的进程。Linux的很多数服务器就是用守护进程实现的。比如,ftp服务器,ssh服务器,Web服务器httpd等。同时,守护进程完成许多系统任务。比如,作业规划进程crond等。

Linux系统启动时会启动很多系统服务进程,这些系统服务进程没有控制终端,不能直接和用户交互。

其它进程都是在用户登录或运行程序时创建,在运行结束或用户注销时终至,但系统服务进程(守护进程)不受用户登录
注销的影响,它们一直在运行着。这种进程有一个名称叫守护进程(Daemon)**

下面我们用ps axj命令查看系统中的进程。参数a表示不仅列当前用户的进程,也列出所有其他用户的进程,参数x表示不仅列有控制终端的进程,也列出所有⽆控制终端的进程,参数j表示列出与 作业控制相关的信息。

这里写图片描述

  • 凡是TPGID一栏写着-1的都是没有控制终端的进程,也就是守护进程

  • 在COMMAND一列用[]括起来的名字表示内核线程,这些线程在内核创建,没有用户空间代码,因此没有程序文件名和命令, 通常采用以k开头的名字,表示Kernel。

  • init进程我们已经很熟悉了,udevd负责维护/dev目录下的 设备下件,acpid负责电源管理,syslogd负责维护/var/log下的⽇志⽂件

  • 可以看出,守护进程通常采以d结尾的名字,表示Daemon。

创建守护进程

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

#include <unistd.h>
pid_t setsid(void);

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

成功调用该函数的结果是:

  • 创建一个新的Session,当前进程成为Session Leader,当前进程的id就是Session的id。
  • 创建一个新的进程组,当前进程成为进程组的Leader,当前进程的id就是进程组的id。
  • 如果当前进程原本有一个控制终端,则它失去这个控制终端,成为⼀个没有控制终端的进程。所谓失去控制终端是指,原来的控制终端仍然是打开的,仍然可以读写,但只是一个普通的打开⽂件⽽不是控制终端

注意,调用这个函数之前,当前进程不允许是进程组的Leader,否则该函数返回-1。要保证当前进程不是进 程组的Leader也很容易,只要先fork再调⽤setsid就可了。fork创建的父进程和子进程在同一个进程组中,进程组的
Leader必然是该组的第一个进程,所以子进程不可能是该组的第一个进程,在子进程中调用setsid就不会有问题了。

守护进程代码
  • 手动实现
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>

void mydaemon(  )
{

      umask(  0);
      pid_t pid;
      struct sigaction sa;
      //调用fork,父进程退出
      //保证子进程不是进程组组长

      pid==fork(  );
      if(  pid<0)
      {
            perror(  "  fork");
            exit(  1);
      }
      else if(  pid>0)
      {
            printf(" i am father!i will exit...\n");

            sleep(  1);
            exit(  0);
      }
      //调用setsid,创建一个新会话
      setsid(  );
      //调用sigaction函数
      //当子进程退出后自动回收子进程资源
      sa.sa_handler=SIG_IGN;
      sigemptyset(&sa.sa_mask);
      sa.sa_flags=0;
      if((sigaction(SIGCHLD,&sa,NULL))<0 )
      {
            return ;
      }
      //使当前目录更改为根目录
      if(chdir("/")<0)
      {
            printf("child dir error\n");
            return ;
      }
      //关闭不需要的文件描述符
      close(0);
      close(1);
      close(2);
}
int main(  )
{
      mydaemon(  );
      while(  1)
      {
            sleep(  1);
      }
}

这里写图片描述

  • 调用库函数实现
#include <unistd.h>

int daemon(int nochdir, int noclose);

参数:0:代表关闭对应文件描述符
     1:代表打开对应文件描述符

返回值
(此函数为fork,如果fork(2)成功,父类调用_exit(2),从而使
其他错误只能由孩子看到。成功守护进程()返回零。如果一个错误
发生时,守护进程()返回-1,并将errno设置为fork(2)指定的任何错误。
和setsid(2)。

代码实现

#include <unistd.h>
#include <stdio.h>
in main()
{
    daemon(0,0);
    while(1);
}

现在新起会话,对比守护进程和一般进程的区别
这里写图片描述

注销用户,重新登陆
这里写图片描述

验证了

  • 其它进程都是在用户登录或运行程序时创建,在运行结束或用户注销时终至,但系统服务进程(守护进程)不受用户登录
    注销的影响,它们一直在运行着。

  • 守护进程自称进程组,自称作业,自称会话,不受用户注销影响

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值