🔥博客主页: 我要成为C++领域大神
🎥系列专栏:【C++核心编程】 【计算机网络】 【Linux编程】 【操作系统】
❤️感谢大家点赞👍收藏⭐评论✍️本博客致力于知识分享,与更多的人进行学习交流
亲缘关系
Linux操作系统下,进程间的关系是强亲缘(父子),弱亲缘(爷孙)
爷爷进程与孙子进程最低限度亲缘(继承关系)
强亲缘关系(父子进程),子进程被父进程创建,子进程的任务被父进程指定,继承父进程数据,子进程结束后,父进程负责回收避免僵尸问题,整个子进程的生命周期父进程要全程参与。(“中国式家长”)
在shell下执行的所有命令创建的进程都是shell的子进程。
我们来验证一下:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <string.h>
#include <sys/fcntl.h>
int main()
{
printf("Child Process PID:%d\n",getpid());
printf("Parent Process PID:%d\n",getppid());
while(1){
sleep(1);
}
return 0;
}
getppid
即get parent pid,获取父进程的pid
在操作系统中,fork
创建进程+execl
重载进程是广泛使用
Process Group
进程组
一个进程组一般由一个组长和若干组员组成。
终端进程被创建,默认就是进程组的组长进程(系统会分配一个进程组)。
组长进程的唯一标志:组id==进程id
getpid()
,返回进程pidgetppid()
,返回进程父进程的pidgetpgrp()
,返回进程进程组的id
使用这三个函数查看一个进程组之间的关系:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <string.h>
#include <sys/fcntl.h>
int main()
{
int i;
pid_t pid;
for(i=0;i<3;++i){
pid=fork();
if(pid==0) break;
}
if(pid>0){
printf("Parent PID:%d,PPID:%d,GroupID:%d\n",getpid(),getppid(),getpgrp());
}
else if(pid==0){
printf("Child PID:%d,PPID:%d,GroupID:%d\n",getpid(),getppid(),getpgrp());
}else{
perror("fork call failed");
exit(0);
}
return 0;
}
进程组的销毁:组中最后一个进程,结束或转移,进程组中没有进程时系统会释放进程。
就近原则:子进程被创建后默认归纳到父进程同组的组员
组长是不允许转移的。
组长进程无法创建进程组,组员进程可以创建新组,脱离原有进程组,成为新的组长
子进程成立新组后,亲缘关系不变,子进程依然由父进程回收(子进程脱离原有进程组,父进程可以通过waitpid跨组回收)
setpgid(pid_t pid,pid_t gid)
可以让组员创建新组,也可以将一个组员转移到其他组。
转移时目标组必须存在,需要对目标组有访问权限才可以完成转移
创建:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <string.h>
#include <sys/fcntl.h>
int main()
{
int i;
pid_t pid;
for(i=0;i<3;++i){
pid=fork();
if(pid==0) break;
}
if(pid>0){
printf("Parent %d PID:%d,PPID:%d,GroupID:%d\n",i,getpid(),getppid(),getpgrp());
}
else if(pid==0){
if(i==2)//对创建的一个子进程脱离当前组创建一个新的组
{
printf("Child %d PID:%d,PPID:%d,GroupID:%d\n",i,getpid(),getppid(),getpgrp());
printf("Child %d PID:%d is Creating Group\n",i,getpid());
setpgid(getpid(),getpid());
printf("Child %d PID:%d,GroupID:%d\n",i,getpid(),getpgrp());
}
printf("Child %d PID:%d,PPID:%d,GroupID:%d\n",i,getpid(),getppid(),getpgrp());
}else{
perror("fork call failed");
exit(0);
}
return 0;
}
转移:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main() {
pid_t pid1, pid2;
pid_t gpid = -1; // 用于存储新进程组的组ID
// 创建第一个子进程
if ((pid1 = fork()) < 0) {
perror("fork error");
exit(1);
} else if (pid1 == 0) {
// 第一个子进程
printf("Child 1 PID:%d, PPID:%d, GroupID:%d\n", getpid(), getppid(), getpgrp());
// 创建新的进程组
if (setpgid(getpid(), getpid()) < 0) {
perror("setpgid error for Child 1");
exit(1);
}
printf("Child 1 PID:%d is Creating Group with GroupID:%d\n", getpid(), getpgrp());
printf("Child 1 PID:%d, GroupID:%d\n", getpid(), getpgrp());
// 子进程1循环等待
while (1) {
sleep(1);
}
}
// 父进程
printf("Parent PID:%d, GroupID:%d\n", getpid(), getpgrp());
// 延时等待第一个子进程完成创建新进程组
sleep(1); // 等待1秒,确保第一个子进程已经执行完setpgid
// 获取第一个子进程的组ID
gpid = pid1;
// 创建第二个子进程
if ((pid2 = fork()) < 0) {
perror("fork error");
exit(1);
} else if (pid2 == 0) {
// 第二个子进程
printf("Child 2 PID:%d, PPID:%d, GroupID:%d\n", getpid(), getppid(), getpgrp());
// 将第一个子进程移动到新的进程组中
if (setpgid(getpid(), gpid) < 0) {
perror("setpgid error for Child 2");
exit(1);
}
printf("Child 2 PID:%d is Changing Group for Child 1\n", getpid());
printf("Child 2 PID:%d, GroupID:%d\n", getpid(), getpgrp());
// 子进程2循环等待
while (1) {
sleep(1);
}
}
// 父进程等待所有子进程结束
wait(NULL);
wait(NULL);
return 0;
}
Process Session会话关系
会话是一种终端进程管理的组织结构。
在一个终端下创建的一组会话。会话发起者杀死进程是按组杀死的。
关闭终端,在这个会话组创建的其他进程也被杀死了。
如果子进程创建新组了可以存活,但是这个子进程并没有脱离会话。
如果子进程脱离了会话,创建新的会话,也可以存活。(脱离控制终端)
如何脱离控制终端会话?
当一个终端关闭时,那么该终端下创建的其它会话参与者也会被杀死。那么有没有方法避免会话参与者被杀死呢?
1、新建组
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <string.h>
#include <sys/fcntl.h>
int main()
{
int i;
pid_t pid;
for(i=0;i<3;++i){
pid=fork();
if(pid==0) break;
}
if(pid>0){
printf("Parent %d PID:%d,PPID:%d,GroupID:%d\n",i,getpid(),getppid(),getpgrp());
while(1) sleep(1);
}
else if(pid==0){
if(i==1)
{
printf("Child %d PID:%d,PPID:%d,GroupID:%d\n",i,getpid(),getppid(),getpgrp());
printf("Child %d PID:%d is Creating Group\n",i,getpid());
setpgid(getpid(),getpid());
printf("Child %d PID:%d GroupId:%d SessionID:%d\n",i,getpid(),getpgrp(),getsid(getpid()));
while(1) sleep(1);
}
printf("Child %d PID:%d,PPID:%d,GroupID:%d\n",i,getpid(),getppid(),getpgrp());
}else{
perror("fork call failed");
exit(0);
}
return 0;
}
创建了新组的子进程仍然存活。
2、新建会话
使用setsid()
函数创建新的会话。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <string.h>
#include <sys/fcntl.h>
int main()
{
int i;
pid_t pid;
for(i=0;i<3;++i){
pid=fork();
if(pid==0) break;
}
if(pid>0){
printf("Parent %d PID:%d,PPID:%d,GroupID:%d\n",i,getpid(),getppid(),getpgrp());
while(1) sleep(1);
}
else if(pid==0){
if(i==1)
{
printf("Child %d PID:%d,PPID:%d,GroupID:%d\n",i,getpid(),getppid(),getpgrp());
printf("Child %d PID:%d is Changing Sessin\n",i,getpid());
setsid();
printf("Child %d PID:%d GroupId:%d SessionID:%d\n",i,getpid(),getpgrp(),getsid(getpid()));
while(1) sleep(1);
}
printf("Child %d PID:%d,PPID:%d,GroupID:%d\n",i,getpid(),getppid(),getpgrp());
}else{
perror("fork call failed");
exit(0);
}
return 0;
}
创建了新会话的进程仍然存活。