- 进程组
- 作业
- 会话
进程组
每一个进程都是属于一个进程组的。显然,它们的PCB中除了有进程ID之外,还有所属组的进程组ID。那进程组ID又是怎么来的呢?每一个进程组都有一个组长进程(该组长进程是进程组的第一个进程),组长进程ID就为进程组ID。组长进程可以创建进程组,创建该组中的进程。进程组的存在与否并不与组长进程是否终止相关,即进程组中只要有一个进程存在,那么进程组就存在。
作业
Shell分前后台来控制的并不是进程而是作业或是进程组。一个前台作业可以由多个进程组成,一个后台也可以由多个进程组成。
Shell可以运行一个前台作业和任意多个后台作业。
作业和进程组的关系与区别:
- 通常,每一个进程组都与一个作业关联,可以接受来自控制终端的各种信号。可以这么理解,进程组完成同一个作业。举个例子,一个小组做任务,进程组就是这个小组,作业就是小组要完成的任务。
- 作业中的某个进程有创建了子进程,该子进程不属于作业,属于进程组。
做个实例:
由于Shell只能运行一个前台作业,当运行这个作业时,Shell把自己提到后台。
当完成作业,Shell把自己提到前台,如果原来的前台作业中创建了子进程,子进程不属于作业,而属于进程组。子进程自动变成后台进程组。
Shell提到前台之后,才可以接受用户输入的各种命令。
1 #include<stdio.h>
2 #include<unistd.h>
3 #include<stdlib.h>
4 #include<string.h>
5
6 int main()
7 {
8 pid_t pid = fork();//创建子进程
9 if(pid<0){perror("fork");exit(1);}
10 if(pid == 0)
11 {
12 while(1)
13 {
14 printf("i am child\n");
15 sleep(1);
16 }
17 }else
18 {
19 int count = 5;
20 while(count--)//父进程5秒之后退出
21 {
22 printf("i am parent\n");
23 sleep(1);
24 }
25 }
26 return 0;
27 }
可以观察到Shell可以接受命令,子进程仍在打印。这是因为当作业结束后,子进程变为后台进程组,Shell将自己提到前台。
会话
会话是一个或多个进程组的集合。
一个会话可以有一个控制终端。
一个会话中的几个进程组可以被分为一个前台进程组以及一个或多个后台进程组。
一个会话中包括控制进程(会话首进程)、一个前台进程组和任意后台进程组。
控制终端
会话首进程打开一个终端之后, 该终端就成为该会话的控制终端。
每打开一个终端,/dev/pts
会创建对应文件,命令ps axj
中TTY
表示与该会话相连的终端。
会话创建
#inlcude<unistd.h>
pid_t setsid(void);//调用成功返回新创建的Session的id,出错-1.
注意:调用该函数的进程不能是进程组组长。
守护进程
守护进程自成进程组、自成会话、不受用户登录注销影响。独立于控制端并且周期性执行某种任务或者处理某些发生的事件。
守护进程创建:
2 #include<stdlib.h>
3 #include<string.h>
4 #include<stdio.h>
5
6 void mydam()
7 {
8 pid_t pid = fork();
9 if(pid< 0){perror("fork");exit(1);}
10
11 if(pid >0)
12 exit(0);
13 pid_t sid = setsid();
14 printf("%d\n",sid);
15 close(0);
16 close(1);
17 close(2);
18 }
19
20 int main()
21 {
22 mydam();
23
23
24 while(1)
25 {
26 sleep(1);
27 }
28 }
守护进程特点:父进程为init(1号)进程,守护进程通常以d结尾,TPGID一栏为-1