- 进程组
进程组是一个或多个进程的集合。每个进程除了有一个进程ID之外,还属于一个进程组。每个进程组有一个唯一的进程组ID。每一个进程组都可以有一个组长进程。(一般来说,第一个进程为组长进程)组长进程的标识是,其进程组ID等于其进程的ID。
只要某个进程中一个进程还存在,则进程组就存在,与其组长进程是否终止无关。
- 作业
Shell分前后台控制的不是进程而是作业或者进程组。在命令行上运行一个二进制程序,则是开启了一个作业。一个前台作业可以由多个进程组成,一个后台作业也可以由很多进程组成。
作业控制:Shell可以运行一个前台作业和任意多个后台作业
作业与进程组的区别:如果作业中的某个进程创建了子进程,则子进程不属于作业而是属于一个进 程组。一旦作业运行结束,Shell就把自己提到前台,若原来的前台进程还存 在,它自动变为后台进程。
- 会话
会话是一个或多个进程组的集合。一个会话可以有一个控制终端。
打开一个终端就相当于打开一个会话。
控制进程:建立与控制终端连接的会话首进程
一次会话中应该包括:控制进程(会话首进程),一个前台进程组和任意后台进程组
4. 守护进程
守护进程也称为精灵进程。是运行在后台的一种特殊进程。它独立于控制终端并且周期性地执行某种任务或等待处理某些发生的进程。
创建守护进程最关键的一步是调用setsid函数创建一个新的会话。
pid_t setsid(void);
返回值:成功时,返回新创建的会话的id(其实也是当前进程的id)
出错返回-1.
注:在调用这个函数之前,当前进程不允许是进程组的Leader,否则该函数返回-1.
成功调用该函数的结果为:
<1> 创建一个新的会话,当前进程称为会话Leader,当前进程的id就是会话的id
<2> 创建一个新的进程组,当前进程成为进程组的Leader,当前进程的id就是进程组的id
<3> 如果当前进程原本有一个控制终端,则它失去这个控制终端,成为一个没有控制终端的进程。所谓失去控制终端是指,原来的控制终端仍然是打开的,仍然可以读写,但只是一个普通的打开文件而不是控制终端。
创建守护进程:
<1> 调用umask将文件模式创建屏蔽字设置为0
<2> 调用fork,父进程退出(exit)。
原因:一般来说,进程组的第一个进程为进程组长,fork出子进程,父进程退出,保证子进程不 是一个进程组的组长进程。
<3> 调用setsid创建一个新会话。setsid会导致(1)调用进程成为新会话的首进程(2)调用进程成 为一个进程组的组长进程(3)调用进程没有控制终端
<4> 将当前工作目录更改为根目录
<5> 关闭不在需要的文件描述符
<6> 忽略SIGCHLD信号
创建守护进程:
int daemon(int nochdir,int noclose)
当 nochdir为零时,当前目录变为根目录,否则不变;
当 noclose为零时,标准输入、标准输出和错误输出重导向为/dev/null,也就是不输出任何信 息,否则照样输出。
返回值:
deamon()调用了fork(),如果fork成功,那么父进程就调用_exit(2)退出,所以看到的错误信息 全部是子进程产生的。如果成功函数返回0,否则返回-1并设置errno。
假如运行成功,父进程在daemon函数运行完毕后自杀,以后的休眠和打印全部是子进程来运行。
在看到有些资料上,创建守护进程时,首先fork子进程,让父进程终止,然后子进程fork出孙子进程,让孙子进程setsid。为什么要fork两次呢?
分析:首先第二次fork也不是必须的。第二次fork的主要目的是:防止进程再次打开一个终端。因为打开一个终端是条件是该进程必须为会话组长,在fork一次,孙子进程id != sid(sid为子进程的sid)。所以无法打开新的控制终端。