进程间关系和守护进程
一、进程组(Process Group)
每个进程除了进程ID以外还属于一个进程组,进程组是一个或者多个进程的集合,通常与同一作业相关联,可以接受同一个终端的各种信号,每个进程组有唯一的进程组ID,每个进程都有一个组长进程,组长进程的标识符就是进程组ID等于进程ID,组长进程可以创建一个进程组,创建该组进程中的进程,然后终止,只要进程组中某个进程存在,那么这个进程就存在,与其组长进程是否终止无关。
二、作业(Job)
shell分前后台来控制的,不是进程,而是作业或者进程组,一个前台作业可以有多个进程组成,一个后台作业可以由多个进程组成,shell可以运行一个前台作业和多个后台作业,这成为作业控制。
作业在后台的时候不允许从前台读取数据,一旦试图读取数据,则该进程被终止。后台进程不允许从stdout读,但是可以往stdout上写。
作业和进程组的区别:
如果作业中的某个进程创建了子进程,但是这个子进程是不属于作业的,一旦作业运行结束,shell就把自己提到前台,如果前台作业还存在,那么他将自动变成后台进程组,在前台新起的作业,shell是无法运行的,因为他被提到了后台,如果前台进程退出,shell就又提到了前台,所以可以继续接收用户的输入。
代码:
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
int main(){
if(fork() == 0){
while(1){
printf("i am child:%d\n",getpid());
sleep(1);
}
}else{
int i = 5;
while(i>0){
printf("i am father:%d,i will dead:%d\n",getppid(),i);
i--;
sleep(1);
}
}
return 0;
}
代码中,我们用fork创建出子进程,让父进程在5s后退出,但shell运行这个程序的时候,shell就被提到了后台,此时父子进程开始打印各自的数据,但是5s之后,父进程退出,也就是说我们的前台会话结束,此时shell从后台被提到了前台,且还是可以输入是命令的,但是我们可以发现,子进程还是在运行,所以表明子进程是不属于这个作业的,同时也证明后台作业可以往stdout写,但是不能从stdout读。
三、会话(Session)
会话是指一个或者多个进程组集合,一个会话可以有一个控制终端,这通常是登录到其上的终端设备或者是伪终端设备,建立与控制会话的首进程,被称之为控制进程(会话首进程),一个会话中的几个进程组可被分为一个前台进程和任意多个后台进程,所以会话中包括控制进程,一个前台进程,和任意多个后台进程,和新打开的一个终端。
四、作业控制
shell可以运行一个前台作业和任意个后台作业,其实是不太全面的,事实上shell分前后台来运行是不是进程,而是作业或者是进程组,一个前台作业可以由多个进程组成,一个后台作业也可以由多个进程组成,shell可以运行一个前台作业和多个后台作业,这成为作业控制。
和作业控制有关的信号:
在cat指令的后面加上“&”,表示让指令在后台运行,但是cat指令需要从stdout读取数据,此时内核发送SIGTTIN信号给cat,该信号的默认处理动作是使进程终止。
指令:jobs,可以查看当前有哪些作业,fg命令可以将某个命令提到前台来进行,如果该作业的进程组在后台进行,则提到前台运行,如果作业处于停止,则发送SIGCONT使其继续运行,如果输入:ctrl+z则向所有前台进程发送SIGTSTP信号,该信号默认使得所有前台进程停止,cat继续以后台作业的形式存在。
以上过程是:先把cat放在后台运行,此时cat被stop,然后向cat发送SIGTERM(15)信号,默认进程终止,但是此时cat已经被stop了,所以此时不会立即的处理信号,得等到我们把cat运行起来的时候,再去执行这个信号,此时我们使用fg 1命令,代表将第一个后台作业提到前台来,如上图所示。
五、守护进程
守护进程也称作精灵进程,是运行在后台,独立于控制终端,并且周期性的执行某种任务,或者的等待处理某些发生的事件,Linux的 大多数服务器就是用守护进程实现的,比如Linux的SSH、Web服务器的http。
Linux启动的时候,就会启动很多系统服务进程,这些系统服务进程没有服务终端,不能和用户直接交互。其他的进程都是在用户登录或者程序运行的时候启动,在用户注销或者程序结束的时候终止,但是守护进程(系统服务进程)不受用户注销的影响,一直在运行。
命令:ps axj 查看当前系统中的命令,参数a表示:表示把所有用户的进程都列出来,参数x表示把有无终端控制的进程都列出来,参数j表示列出所有与作业控制相关的信息
守护进程的创建
创建守护进程最关键的地方就是调用setsid函数创建一个新的Session,并成为Session Leader。
头文件:#include <unistd.h> 函数:pid_t setsid(void)
成功返回创建的Session的Id,也就是当前进程的ID,出错返回-1;
Tip:调用该函数之前,当前进程是不允许成为进程组的Leader,否则出错返回-1,所以要保证当前进程不是进程组的Leader,
只要调用fork(),再调用setsid就可以保证。
成功调用该函数的结果:
①创建一个新的Session命名,当前进程成为进程组的Leader,当前进程ID就是进程组的ID
②创建一个新的进程组,当前进程就是进程组的Leader,当前ID就是进程组ID
③如果当前进程原本有一个控制终端,则它失去这个控制终端,成为一个没有控制终端的进程,所谓失去终端是指,原来的终端
还是可以打开,仍然可以读写,但是只是一个普通的文件,而不是终端控制了。
如果使我们自己创建的话,应该是这样:
第一步:调用umask将文件模式创建屏蔽字设置为0,
第二步:调用fork,然后使得父进程退出,
第三步:调用setsid创建新的Session,
第四步:将当前工作目录改成根目录
第五步:关闭不再需要的文件描述符
第六步:忽略:SIGCHLD信号
代码如下:
#include <stdio.h>
#include <unistd.h>
void mydaemon(){
umask(0);
if(fork()>0){
exit(1);
}
setsid();
chdir("/");//改变工作目录到指定位置,“/”代表根目录
close(0);
close(1);
close(2);
signal(SIGCHLD,SIG_IGN);
}
int main(){
mydaemon();
while(1);
return 0;
}
但是系统为我们提供了一个创建守护进程接口可以代替我们的工作:int daemon(int nochdir, int noclose);
参数:nochdir:是否把当前工作目录改成根目录,“0”:改;“1”:不改
noclose:是否关闭当前的文件指针,“0”:关闭;“1”:不关
限于编者水平,文章难免有缺漏之处,欢迎指正
如需转载,请注明出处~!