进程间关系及守护进程

一.进程间关系

1.进程组

每个进程除了有一个进程ID之外,还属于一个进程组进程组是一个或多个进程的集合。通常,它们与同一作业相关联,可以接收来自同一终端的各种信号。每个进程组有一个唯一的进程组ID。每个进程组都可以有一个组长进程。组长进程的标识是其进程组ID等于其进程ID。 组长进程可以创建一个进程组,创建该组中的进程,然后终止只要在某个进程组中一个进程存在,则该进程组就存在,这与其组长进程是否终止无关

在这里插入图片描述

进程3055、3056属于一个进程组,4055为进程组的组长,使用kill命令杀掉组长,进程组依然存在。

2.作业

2.1 作业概念

shell分前后台控制的是作业或进程组而不是进程。一个前台或后台作业可以由多个进程组成。shell可以运行一个前台作业和任意多的后台作业

一旦作业运行结束,Shell就把自己提到前台(但是子进程还在,可是子进程不属于作业),如果原来的前台进程还存在,也就是子进程存在,它自动变为后台进程

作业和进程组的区别:如果作业中的某个进程又创建了子进程,该子进程不属于作业,但是属于进程组。

总结:,在前台新起作业,shell是无法运行,因为他被提到了后台。 但是如果前台作业退出, shell就又被提到了前台,所以可以继续接受用户输入

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

int main()
{
  pid_t id = fork();
  if(id < 0){
    perror("use fork");
    exit(1);
  }
  else if(id == 0){//child
    while(1)
    {
      printf("child:%d is running!\n",getpid());
      sleep(1);
    }
  }
  else{//parent
    int i = 5;
    while(i)
    {
      printf("parent:%d is going done...%d\n",getppid(),i--);
      sleep(1);
    }
  }
  return 0;
}

当该程序跑起来之后,在前台新起了1个作业,包含父子两个进程。 5s之内,shell无法接受任何命令!说明此时的前台作业不是shell。但是父进程退出之后,子进程还在运行,但此时输入的命令, shell可以处理的,说明此时shell变成了前台作业。换句话说,我们刚新起的作业退出了!但子进程还在,就自动被提到后台。你可以再看看,子进程所属的进程组还在!组长是父进程(已经退出) 。我们发现他还在一直打消息,只要有使用kill - 9杀掉即可

2.2 作业控制

一个前台作业可以由多个进程组成,一个后台作业也可以由多个进程组成。Shell可以同时运行一个前台作业和任意多个后台作业,这称为作业控制(Job Control)

  • &:让作业到后台运行
  • fg n:将后台作业提到前台运行,n为作业编号
  • bz:让后台的作业为运行态
  • jobs:查看后台作业
  • ctrl+z:将前台作业暂停放到后台
  • chrl+dexit:让bash退出

ctrl+c只可以退出前台作业,不能让后台作业退出。
在这里插入图片描述

3.会话

会话(Session)是一个或多个进程组的集合。一个会话可以有一个控制终端。这通常是登陆到其上的终端设备或伪终端设备(在网络登陆情况下)。建立与控制终端连接的会话首进程被称为控制进程。一个会话中的几个进程组可被分为一个前台进程组以及一个或多个后台进程组。 所以一个会话中,应该包括控制进程(会话首进程),一个前台进程组和任意后台进程组
在这里插入图片描述
ps选项:

  • a: 不仅列当前用户的进程,也列出所有其他用户的进程
  • x: 表示不仅列有控制终端的进程,也列出所有无控制终端的进程
  • j: 表示列出与作业控制相关的信息
  • u:以用户为主的格式来显示程序状况

二.守护进程(Daemon)

1.守护进程概念

守护进程也称精灵进程(Daemon),是运⾏行在后台的一种特殊进程。它独立于控制终端并且周期性地执行某种任务或等待处理某些发⽣生的事件。守护进程是一种很有用的进程。Linux的大多数服务器就是用守护进程实现的。比如,ftp服务器,ssh服务器,Web服务器httpd等。同时,守护进程可以完成许多系统任务,作业规划进程crond等Linux系统启动时会启动很多系统服务进程,这些系统服务进程没有控制终端,不能直接和用户交互。其它进程都是在用户登录或运行程序时创建,在运行结束或用户注销时终止,但系统服务进程(守护进程)不受⽤用户登录注销的影响 ,它们一直在运行着,这种进程就叫守护进程。

守护进程独立成进程组,独立成作业,不受任何影响。
使用ps -axj | more命令查看所有的进程信息:
在这里插入图片描述

  • TPGID一栏写着-1的都是没有控制终端的进程,也就是守护进程
  • COMMAND一列用[]括起来的名字表示内核线程,这些线程在内核里创建,没有用户空间代码。因此没有程序文件名和命令行, 通常采用以k开头的名字,表示Kernel
  • init进程为1号进程,是为了回收领养孤儿进程
  • 守护进程通常采用以d结尾的名字,表示Daemon
  • 所有的守护进程的父进程都是init进程,也就是说所有的守护进程都是孤儿进程

2.创建守护进程

setsid函数用来创建一个会话。

#include <unistd.h> pid_t setsid(void); 
//该函数调⽤用成功时返回新创建的Session的id(其实也就是当前进程的id),出错返回-1

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

setsid函数调用成功:

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

创建守护进程的一般步骤:

  • 父进程中执行fork

  • 子进程中调用setsid函数创建新的会话

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

  • 在子进程中调用umask函数,设置进程的umask为0

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

3.daemon函数

#include <unistd.h>
int daemon(int nochdir, int noclose);
参数说明:
1.nochdir:为零时,当前目录变为根目录,否则不变;
2.noclose:为零时,标准输入、标准输出和错误输出重导向为/dev/null,也就是不输出任何信息,否则照样输出。

返回值:
deamon()调用了fork(),如果fork成功,那么父进程就调用_exit(2)退出,所以看到的错误信息全部是子进程产生的。如果成功函数返回0,否则返回-1并设置errno。

函数作用:是当前进程切换到后台运行

#include <stdio.h>
#include <unistd.h>

int main()
{
  daemon(0,0);
  while(1);
  return 0;
}

下边为运行结果,可以看到运行两次a.out,都是两个后台进程
在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值