目录
亲缘关系
进程组关系
会话关系
孤儿态进程
亲缘关系
亲缘关系主要体现于父子进程,子进程父进程创建,代码继承于父进程,父进程负责回收,子进程诞生至结束父进程全程参与,这种称为强亲缘关系。
系统开机后(字符Linux系统无UI)如何创建第一个终端:
终端子进程:
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
int main()
{
printf("son %d ,parent %d\n",getpid(),getppid());
while(1)
sleep(1);
}
前台才能用命令,当前这个zhong是前台所以终端不能用命令了,终端子进程默认情况下也是唯一的前台进程。
或者是有加号的是前台。
进程组关系
进程组是一种系统管理单位,进程管理器进行组划分,组转化转义,便于系统管理多进程。
一个进程组是由一个组长进程N个组员进程构成。进程组长的唯一标识,pid=pgid。
终端子进程被创建均为组长进程。
普通进程的生命周期随着使用时长持续。
进程组中直到最后一个进程终止或转移,进程组为空,系统会释放进程组。
就近原则,组长进程创建子进程,都会默认归纳到同组,成为组员进程。
进程组的成员可以转移,变为其他组成员,进程组概念与亲缘概念没有必然联系。
大多数系统不允许组长变更。
getpgrp();返回进程组id
setpgid(pid_t pid,pid_t pid);//创建进程组或转移进程组。
创建进程,组长不允许使用,组员进程可以set(getpid(),getpid()),组员申请组,组id是自己id。
转移进程,组长无法转移,setpid(3000,5000);//3000转移到5000这个组。目标组得存在,并且对目标组有权限才能转移成功。
转移到别的组代码:
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/wait.h>
int main()
{
pid_t pid;
int i;
for(i=0;i<3;i++)
{
pid=fork();
if(pid==0)break;
}
if(pid>0){
printf("parent %d,group %d\n",getpid(),getpgrp());
while(wait(NULL)>0);
}
else if(pid==0){
printf("child %d,group %d,i %d\n",getpid(),getpgrp(),i);
if(i==2){
sleep(5);
printf("create group\n");
setpgid(getpid(),getpid());
printf("child pid %d,group %d,i %d\n",getpid(),getpid(),i);
}
while(1)sleep(1);
}
else{
printf("fork fail\n");
exit(0);
}
return 0;
}
会话关系
一个终端下可能有终端子进程和其他终端进程构成,为了便于管理这些终端进程,使用会话关系管理。
会话由一个会话发起者和若干个会话参与者构成。会话发起者标志pid=gid=sid;
会话发起者结束后按组杀死参与者,杀死终端子进程的那一组。
getsid(getpid())//返回当前进程会话id
setsid()//创建新会话(创建组->创建会话)
终端子进程无法脱离终端必然被杀死。
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/wait.h>
int main()
{
pid_t pid;
int i;
for(i=0;i<3;i++){
pid=fork();
if(pid==0)break;
}
if(pid>0){
printf("parent %d,group %d\n",getpid(),getpgrp());
while(wait(NULL)>0);
}
else if(pid==0){
printf("child %d,group %d,i %d,sid %d\n",getpid(),getpgrp(),i,getsid(getpid()));
while(1)sleep(1);
}
else{
printf("fork fail\n");
exit(0);
}
return 0;
}
一组都杀没了。
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/wait.h>
int main()
{
pid_t pid;
int i;
for(i=0;i<3;i++){
pid=fork();
if(pid==0)break;
}
if(pid>0){
printf("parent %d,group %d\n",getpid(),getpgrp());
while(wait(NULL)>0);
}
else if(pid==0){
printf("child %d,group %d,i %d,sid %d\n",getpid(),getpgrp(),i,getsid(getpid()));
if(i==2){
sleep(5);
printf("create group\n");
setsid();
printf("child pid %d,group %d,i %d,sid %d\n",getpid(),getpid(),i,getsid(getpid()));
}
while(1)sleep(1);
}
else{
printf("fork fail\n");
exit(0);
}
return 0;
}
关闭后6258躲过一劫。
孤儿态进程
父进程先于子进程退出,子进程失去管理,变为孤儿进程。
进程变为孤儿,父进程变更变为托管进程。所有孤儿进程结束后托管进程(upstart user可视化进程,在ubuntu14.04的版本为1init进程后续版本是init的子进程)负责处理这些僵尸进程,避免内存泄漏。
托管进程不干预孤儿进程执行,只是负责回收。
孤儿进程残留影响新进程的创建。孤儿进程的危害是弹性的,取决于孤儿进程的工作,如果孤儿进程持续申请系统资源,危害较大。
(错误信息的抛出:假如fork()失败返回-1,系统会有个errno记录,假如error=3,perror("fork error")找这个3在系统这个错误文件里找到对应错误的原因->STDERR_FIENO->使用用户自定义语句和错误信息进行拼接。)
孤儿进程是后台进程,由于父进程也没有了所以也不会出现像上面被一组杀死这种情况。
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
int main()
{
pid_t pid;
pid=fork();
if(pid>0){
exit(0);
}
else if(pid==0){
while(1){
sleep(6);
printf("家人们谁懂啊,孤儿了\n");
}
}
else{
exit(0);
}
return 0;
}
这里能看出是后台进程。
这里能看见由于我们是老版本所以1号进程是托管进程。
怎么早发现早处理孤儿进程:
1.利用管道的特性处置孤儿进程,读端关闭,写端杀死。
2.kill(parent_pid,0)//测试进程是否存活。pthread_kill(tid,0)//测试线程是否存活。
要是多组的话那就很麻烦。