终端、守护进程、作业

3/3/2017来源:C/C++教程人气:1583

一、进程组,作业,会话 1、进程组 每个进程除了有一个进程ID之外,还属于一个进程组。进程组是多个进程的集合,同一个进程组的进程的组ID相同,每一个进程组都有一个唯一的组ID。 每个进程组都有一个组长,组长的pid与gid相同。组长可以创建一个进程组,创建该组中的进程,然后终止。只要进程组内任意一个进程存在,那么这个进程组就存在,与组长进程是否终止无关。 通常进程组都与作业相关联,可以接收来自同一终端的各种信号。 这里写图片描述

2、作业 shell分前后台来控制的不是进程,而是作业或进程组。一个作业可以由多个进程组组成,shell可以运行一个前台作业和任意多个后台作业,这就称作作业控制。 如果作业中的某个进程又创建了子进程,则子进程不属于作业,但是属于进程组。一但前台作业运行完之后,shell就把自己提到前台,如果前台的子进程还没终止,他就自动变成后台进程。 shell进程本身属于一个单独的进程组,同时他也是会话首进程(控制进程)。

作业控制相关的命令: jobs:可以查看当前shell下的作业。 fg 作业号:可以将后台作业提到前台运行,同时因为只能有一个作业在前台运行,所以shell会到后台运行。 bg 作业号:将后台中stop的作业重新运行起来。 例: 这里写图片描述

3、会话 会话是一个或多个进程组的集合。一个会话可以有一个控制终端。建立与控制终端连接的会话首进程被称为控制进程。这个控制进程通常是bash。 一个会话中的进程组可以被分为一个前台进程组和一个或多个后台进程组。所以一个会话中应该包含控制进程(会话首进程),一个前台进程和任意后台进程。内核发送的信号,会被前台进程组中的所有进程接收。 只要会话中还有任意一个进程组,则会话就存在。 这里写图片描述 可以看到这些进程属于同一个会话,会话首进程是bash。

4、进程间关系 多个进程可以构成进程组,多个进程组可以构成会话。一次只能有一个前台作业或进程组,和任意多个后台作业或进程组。

5、总结: 5.1、后台进程可以向前台写数据,但是不能从前台读数据。这是因为前台只有一个,所以写数据不存在二义性,而后台进程可以有多个,所以读的时候会产生二义性。后台进程要读数据的话会被stop。 命令:ssty tostop 可以通过这条命令,使得后台进程不能向前台写数据。 5.2、用kill命令给一个已经停止的进程发送SIGTERM信号,这个信号不会被立即处理,而是要等进程准备运行之前处理,默认动作是终止进程。但是如果给一个停止的进程发SIGKILL信号就会被立即处理。 5.3、放到前台就是和当前终端建立关联,控制终端信息都在PCB中。只有会话首进程才能打开一个终端。

二、终端 用户通过终端登录系统后得到一个shell进程,这个终端成为shell的控制终端,控制终端是保存在shell进程的PCB中的,因为fork会复制PCB中的信息,因此由shell进程启动的其他进程的控制终端也是这个终端。 这里写图片描述 可以看到,我的终端下的的设备文件在/dev/pts的目录下: 这里写图片描述 每次打开一个终端,就会出现一个终端控制文件。我们可以根据终端控制文件来实现在不同终端的通信。 要想和另一终端通信,可以直接向另一个终端的设备文件中写。

三、守护进程(daemon) 1、守护进程的基本概念 守护进程也叫作精灵进程,它是在后台运行的一种特殊进程。它独立于控制终端并且周期性的执行某种任务或等待处理某些发生的事件。 linux系统启动时会启动很多系统服务进程,这些系统服务进程没有控制终端,不能直接和用户交互。这些系统服务进程不受用户登录注销的影响,他们一直运行着,这些系统服务进程就是守护进程。

例:查看守护进程 命令:ps axj 列出所有用户的进程 这里写图片描述 由于守护进程的父进程是1号进程,所以守护进程是孤儿进程。

凡是TPGID这一栏写着-1的都是没有控制终端的进程。再COMMAND这一列用[]括起来的都是内核线程。这些内核线程在内核里面创建,没有用户空间代码,因此没有程序文件名和命令行。udevd负责维护/dev目录下的设备文件。acpid负责电源管理,syslogd负责维护/var/log下的日志文件,守护进程通常以d结尾。 例: 这里写图片描述 这里写图片描述

2、为什么要有守护进程??? 控制终端由于某些原因会发送一些信号。接受到信号的进程去执行 这些信号的默认处理动作会导致进程退出。这使得的进程不能正常的处理某些任务,所以就需要向守护进程这样接收不到信号的进程。让进程独立于控制终端,执行某些任务或处理某些事件。

3、创建守护进程 创建守护进程最关键的一步是调用setsid函数创建一个新的session,并成为会话首进程。

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

返回值:这个函数成功时返回新创建的会话的id,也就是当前进程的id,出错返回-1。 注意:调用这个函数之前,当前进程不允许是进程组的组长,否则就返回-1。所以我们一般fork出一个子进程,然后杀死父进程,则这个子进程一定不是组长,然后让这个子进程调用setsid称为守护进程。 成功的结果: 1、创建一个新的会话,当前进程自成会话首进程。 2、创建一个新的进程组,当前进程成为进程组的组长。 3、如果当前进程原本有一个控制终端,则它会失去这个控制终端,成为一个没有控制终端的进程。所谓的失去控制终端是指,原来的控制终端仍然是打开的,仍然可读可写,但只是一个普通文件了。守护进程是独立于控制终端存在的。

步骤: 1、调用umask将权限掩码设置为0。 2、调用fork,父进程退出。保证子进程不是一个进程组的组长。 3、调用setsid创建一个新的会话。这个函数会让当前进程成为新会话的首进程。而且自成一个进程组的组长。最重要的是没有控制终端。(还可以再fork一次,保证daemon进程之后不会打开控制终端)。 4、将当前创建目录更改为根目录。 5、关闭不再需要的文件描述符。 6、忽略SIGCHLD信号。 代码: 这里写图片描述

我们观察到该守护进程的pid是5934,我们可以在”/PRoc”这个目录下查看这些进程. 这里写图片描述

还可以通过调用标准函数直接创建一个守护进程。

#include<unistd.h> int daemon(int nochdir,int noclose);

nochdir:这个值为0的话,表示将当前进程的工作目录设值为”/”目录。 noclose:这个值 为0的话表示将所有的文件描述符都写入”/dev/null”中。”/dev/null”表示黑洞,写入里面的所有信息都被内核丢弃。 这里写图片描述

这里写图片描述