linux c编程:进程控制(四)进程关系
每一个进程除了有一个进程ID外,还属于一个进程组。 进程组是一个或多个进程的集合,通常情况下,他们是在同一作业中结合起来的,同一进程组的个进程接受来自同一终端的各种信号。 每一个进程组有一个唯一的进程ID。
组长进程
每个进程组都有一个组长进程,组长进程的进程组ID等于其进程ID。 进程组组长可以创建一个进程组,创建进程组中的进程然后种植。只要进程组中还有任意一个进程存在,那么这个进程组就存在。 从进程组的创建到最后一个进程离开的时间去成为进程组的生命周期
函数getpgrp返回调用进程的进程组ID。
#include<unistd.h>
pid_t getpgrp(void);
下面的代码来验证下进程组
void pgroup_func(){
pid_t pid;
pid=fork();
if (pid == 0){;
printf("child id is:%d, the group id is :%d\n",getpid(),getpgrp());
sleep(1);
}
else{
printf("parent id is:%d,the group id is:%d\n",getpid(),getpgrp());
sleep(1);
}
}
从下面的运行结果可以看出,子进程和父进程的进程组ID是一样的。说明子进程和父进程同属一个进程组。父进程是这个进程组的组长。

那么如何修改进程组呢,setpgid函数将pid进程的进程组ID设置为pgid. 如果这两个参数相等,则由pid指定的进程变成进程组组长。如果pid是0,则使用调用者的进程ID。如果pgid=0,则由pid指定的进程ID用做进程组ID。但是如果子进程一旦执行exec,父进程就无法调用setpgid函数来设置子进程的进程组ID了
代码增加一个setpgid
void pgroup_func(){
pid_t pid;
pid=fork();
if (pid == 0){
setpgid(pid,0);
printf("child id is:%d, the group id is :%d\n",getpid(),getpgrp());
sleep(1);
}
else{
printf("parent id is:%d,the group id is:%d\n",getpid(),getpgrp());
sleep(1);
}
}
可以看到父进程和子进程的进程组ID不一样了。

有了创建进程组的接口,新创建的进程组就不必继承父进程的进程组ID了。最常见的创建进程组的场景就是在shell中执行管道命令,代码如下:cmd1 | cmd2 | cmd3
下面用一个最简单的命令来说明,其进程之间的关系如下所示。
ps ax|grep nfsd

ps进程和grep进程都是bash创建的子进程,两者通过管道协同完成一项工作,它们隶属于同一个进程组,其中ps进程是进程组的组长。
进程组的概念并不难理解,可以将人与人之间的关系做类比。一起工作的同事,自然比毫不相干的路人更加亲近。shell中协同工作的进程属于同一个进程组,就如同协同工作的人属于同一个部门一样。
引入了进程组的概念,可以更方便地管理这一组进程了。比如这项工作放弃了,不必向每个进程一一发送信号,可以直接将信号发送给进程组,进程组内的所有进程都会收到该信号。
前面提到过,子进程一旦执行exec,父进程就无法调用setpgid函数来设置子进程的进程组ID了,这条规则会影响shell的作业控制。出于保险的考虑,一般父进程在调用fork创建子进程后,会调用setpgid函数设置子进程的进程组ID,同时子进程也要调用setpgid函数来设置自身的进程组ID。这两次调用有一次是多余的,但是这样做能够保证无论是父进程先执行,还是子进程先执行,子进程一定已经进入了指定的进程组中。由于fork之后,父子进程的执行顺序是不确定的,因此如果不这样做,就会造成在一定的时间窗口内,无法确定子进程是否进入了相应的进程组。
会话
会话是一个或多个进程组的集合。进程调用setsid函数建立一个新会话。
如果调用此函数的进程不是一个进程组的组长,则此函数就会创建一个新会话,该进餐变成会话的首进程,然后该进程成为一个新进程组的组长进程,该进程没有控制终端。因为会话首进程是具有唯一进程ID的单个进程,所以可以将会话首进程的进程ID视为会话Id。
#include <unistd.h>
pid_t setsid(void);
pid_t getsid(pid_t pid);
来看下面的2个例子:
void session_func(){
pid_t pid;
pid=fork();
if(pid == 0){
printf("child id is:%d, the group id is:%d,the session id is:%d\n",getpid(),getpgrp(),getsid(pid));
}
else{
printf("parent id is:%d,the group id is:%d,the session id is:%d\n",getpid(),getpgrp(),getsid(pid));
}
}

在子进程中创建会话
void session_func(){
pid_t pid;
pid=fork();
if(pid == 0){
setsid();
printf("child id is:%d, the group id is:%d,the session id is:%d\n",getpid(),getpgrp(),getsid(pid));
}
else{
printf("parent id is:%d,the group id is:%d,the session id is:%d\n",getpid(),getpgrp(),getsid(pid));
}
}
}

我们可以看到在子进程没用setsid函数建立一个会话之前,子进程是和父进程在同一会话里的,当子进程用setsid函数建立一个会话,会话的首进程ID就是子进程ID也就是会话ID。
一个会话可以有一个控制终端。这通常是登陆到其上的终端设备(在终端登陆情况下)或伪终端设备(在网络登陆情况下)。建立与控制终端连接的会话首进程被称为控制进程。
一个会话中的几个进程组可被分为一个前台进程组以及一个或多个后台进程组。所以一个会话中,应该包括控制进程(会话首进程),一个前台进程组和任意后台进程组。
无论何时键入终端的终端键,都会将中断信号发送到前台进程组的所有进程
还是来看之前的例子,
void session_func(){
pid_t pid;
pid_t pid1;
pid=fork();
pid1=tcgetpgrp(0);
printf("The pid1 is %d\n",pid1);
if(pid == 0){
setsid();
printf("child id is:%d, the group id is:%d,the session id is:%d\n",getpid(),getpgrp(),getsid(pid));
}
else{
printf("parent id is:%d,the group id is:%d,the session id is:%d\n",getpid(),getpgrp(),getsid(pid));
}
}
tcgetpgrp的原型如下,通过终端的文件描述符fd返回前台进程组pid.
#include<unistd.h>
pid_t tcgetpgrp(int fd)
通过tcgetpgrp(0)得到终端1的pid,可以看到和父进程的pid是一样的。

linux c编程:进程控制(四)进程关系的更多相关文章
- Linux系统编程之进程控制(进程创建、终止、等待及替换)
		
进程创建 在上一节讲解进程概念时,我们提到fork函数是从已经存在的进程中创建一个新进程.那么,系统是如何创建一个新进程的呢?这就需要我们更深入的剖析fork函数. 1.1 fork函数的返回值 调用 ...
 - 【Linux程序设计】之进程控制&守护进程
		
这个系列的博客贴的都是我大二的时候学习Linux系统高级编程时的一些实验程序,都挺简单的. 实验题目:Linux环境下的进程控制 实验目的:熟悉并掌握Linux环境下进程的相关函数的应用:守护进程的概 ...
 - Linux系统编程(7)—— 进程之进程概述
		
我们知道,每个进程在内核中都有一个进程控制块(PCB)来维护进程相关的信息,Linux内核的进程控制块是task_struct结构体.现在我们全面了解一下其中都有哪些信息. 进程id.系统中每个进程有 ...
 - linux c编程:进程控制(四)进程调度
		
当系统中有多个进程到时候,哪个进程先执行,哪个进程后执行是由进程的优先级决定的.进程的优先级是由nice值决定的.nice值越小,优先级越高.可以看做越友好那么调度优先级越低.进程可以通过nice函数 ...
 - Linux系统编程(9)—— 进程之进程控制函数exec系列函数
		
在Linux中,并不存在exec()函数,exec指的是一组函数,一共有6个,分别是: #include <unistd.h> extern char **environ; int exe ...
 - Linux系统编程(8)—— 进程之进程控制函数fork
		
fork()函数通过系统调用创建一个与原来进程几乎完全相同的进程,也就是两个进程可以做完全相同的事,但如果初始参数或者传入的变量不同,两个进程也可以做不同的事. 一个进程调用fork()函数后,系统先 ...
 - Linux Shell编程(28)——进程替换
		
进程替换与命令替换很相似. 命令替换把一个命令的结果赋给一个变量,例如 dir_contents=`ls -al`或xref=$. 进程替换则是把一个进程的输出回馈给另一个进程 (换句话说,它把一个命 ...
 - C语言 进程控制---创建进程fork()函数
		
#include "sys/types.h" #include "stdio.h" #include "stdlib.h" #include ...
 - Linux环境编程之同步(四):Posix信号量
		
信号量是一种用于提供不同进程间或一个给定进程的不同线程间同步手段的原语.有三种类型:Posix有名信号量,使用Posix IPC名字标识.Posix基于内存的信号量,存放在共享内存区中:System ...
 
随机推荐
- VM虚拟机 Windows虚拟机中linux鼠标不能动怎么办
			
有一次vmware安装red hat linux后,进入x-windows界面,鼠标不能用,百思不得其解,因为自己的安装linux的过程中设置绝对是没有问题的啊,鼠标设置肯定是usb带滑轮,这个肯定没 ...
 - C++11之function模板和bind函数适配器
			
在C++98中,可以使用函数指针,调用函数,可以参考之前的一篇文章:类的成员函数指针和mem_fun适配器的用法. 简单的函数调用 对于函数: void foo(const string &a ...
 - 【我们都爱Paul Hegarty】斯坦福IOS8公开课个人笔记30 ScrollView Demo实战
			
在上一话中我们创建了一个通过URL读取图片的Demo,这个Demo是不能拖动和缩放的.如今给它添加选项让它能够手动切换URL,并把图片加入到ScrollView中. 向Storyboard中拖入一个s ...
 - Ubuntu升级出现/boot空间不足解决(转)
			
经常升级Linux内核,导致更新时警告/boot分区空间不足.这是以为多次升级内核后,导致内核版本太多,清理一下没用的内核文件就行了.命令如下: zht@zht-Ubuntu:~$ dpkg -l ' ...
 - 对于刚開始学习的人Xcode最经常使用的快捷键
			
对于刚開始学习的人而言,好多人都是直接使用鼠标进行操作.差点儿非常少使用快捷键,从而再练习编程时比别人慢那么一点,今天就把刚開始学习的人最经常使用的几个快捷键给大家总结下,当然欢迎大家补充. (1)c ...
 - js中的string.format函数代码
			
String.prototype.format = function(args) { if (arguments.length > 0) { var result = this; if (arg ...
 - NoSQL之Redis学习小结
			
大数据时代要求: 三V:Volume海量.Velocity实时.Variety多样: 三高:高并发.高可扩.高性能 高并发操作不建议使用关联查询,而使用冗余数据,分布式系统支持不了太多的并发. 横向 ...
 - PHP面试题及答案解析(7)—Linux系统命令
			
1.请解释下列10个shell命令的用途.top.ps.mv.find.df.cat.chmod.chgrp.grep.wc top:该命令提供了实时对系统处理器状态的监控,它能够实时显示系统中各个进 ...
 - mysql负载飙高原因分析
			
某些进程/服务消耗更多CPU资源(服务响应更多请求或存在某些应用瓶颈):发生比较严重的swap(可用物理内存不足):发生比较严重的中断(因为SSD或网络的原因发生中断):磁盘I/O比较慢(会导致CPU ...
 - shell 判断问题总结
			
#!/bin/bash #比如需要判断一个变量是否含有值: if [[ -z $1 ]] ; thenecho "Something like empty!"exit 0;fi # ...