linux_api之进程环境(二)
本篇索引:
1、引言
2、终端登录
3、进程组
4、会话期
1、引言
通过上一篇的学习,我们已经知道了如何控制一个进程,fork函数从父进程中复制出子进程,我们可以通过exec函数让子进程运行新的程序,进程可以通过调用exit系列函数(return,_exit)终止,父进程可以利用wait或waitpid函数等待某个进程的终止状态。
在操作系统上同时运行着很多的进程,那么这些进程之间有没有什么什么关系呢?答案是肯定的,最起码这些进程之间是有父子关系的,当然只这里要除掉0、1、2这三个进程。
那么本篇的主要内容就是进一步讨论这些进程之间的相互关系。
2、终端登录
大家现在登陆一个linux操作系统已经熟练了,只要输入用户名和密码即可,但是对于这个的登陆的过程就不是很熟悉了,我们通过RS-232终端登陆的过程来窥见下这个具体的登陆过程是怎样的。
3、进程组
进程组是一个进程或多个进程的集合,每个进程还属于某个唯一的进程组,组ID就是进程组组长的进程ID,getpgrp函数可以返回当前进程所在进程组的组ID。
3.1、getpgrp函数
1)、函数原型和所需头文件
#include <unistd.h>
pid_t getpgrp(void);
2)、函数功能:获取调用该函数的当前进程所在进程组的组ID。
3)、函数参数:无参数。
4)、函数返回值:成功,返回当前进程所在进程组的组ID,失败返回-1,errno被设置。
5)、注意:
a)、即使是组长死了,但只要该组中还有一个组员进程正在运行,那么进程组依然存在。
B)、从进程组创建到该组中最后一个进程结束的这段时间,我们称为进程组的生命期。
c)、从shell终端开始运行的第一个进程担任组长,其进程ID就是组ID,随后从该进程fork 出的所有的进程全部属于该组。
d)、当然除了组长进程外任何一个组员都能够通过调用setpgid函数自成一组或则加入其 它组。
6)、测试用例
test.c
#include <stdio.h>
#include <unistd.h> int main(void)
{
int ret = ;
ret = fork();
if( == ret) //原始父进程的第一个子进程
{
ret = fork();
if( == ret){
printf("pgrp = %d\n", getpgrp());
}
else if(ret > ){
printf("pgrp = %d\n", getpgrp());
}
}
else if(ret > ){
ret = fork(); //原始父进程的第二个子进程
if( == ret){
printf("pgrp = %d\n", getpgrp());
}
else if(ret > ){ //原始父进程
sleep();//让所有子进程先运行结束
printf("pgrp = %d\n", getpgrp());
}
}
return ;
} return ;
}
上例运行结果如下:
pgrp = 4163
pgrp = 4163
pgrp = 4163
pgrp = 4163
可以看出所有进程都同属于一个组,组长由原始父进程担任,从原始父进程复制出来所有的进程都属于该组。
3.2、setpgid函数
1)、函数原型和所需头文件
#include <unistd.h>
pid_t getpgid(pid_t pid, pid_t pgid);
2)、函数功能:调用该函数以指定某个进程参加一个现存的组或者创建一个新的进程组。
3)、函数参数
pid_t pid:指定需要被修改为或设置到新进程组的进程。
pid_t pgid:进程组ID,指定某个进程组。
4)、函数返回值:成功返回进程组的组ID,失败返回-1,errno被设置。
5)、注意:
这个函数的功能是将pid进程的进程组修改为pgid对应的进程组。但是需要注意如下特殊情况。
1、当这两个参数相等时,则将pid对应的进程直接的变为进程组组长。
2、pid填0的话,pid默认使用调用者的进程ID。
3、Pgid填0的话,pgid默认使用pid指定的进程ID为进程组ID。
4、一个进程只能为它自己或者子进程设置进程组ID,但是子进程一旦exec之后就不能 再改变它的进程组ID了。
6)、例子
a)、在shell命令行执行了新的程序之时,shell会fork出子进程以运行新的程序,shell 父进程会调用该函数将子进程设置为新的进程组组长,子进程也会调用该函数将自己设 置为新的进程组组长,这两个操作有一个冗余的,但是这能够保证无论父子进程谁先运 行,子进程都能被设置为新的进程组组长,否者的话就会产生一个竟态,结果就会依赖 于哪一个进程先运行,之后从该子进程复制出的所有的子进程都属于该进程组。
b)、测试用例
#include <stdio.h>
#include <unistd.h> int main(void)
{
int ret = ;
ret = fork();
if( == ret){ //原始父进程的第一个子进程
setpgid(getpid(), getpid());
printf("in chaild pgrp = %d\n", getpgrp());
}
else if(ret > ){ //原始父进程,有shell父进程复制而来
sleep();
printf("in pareant pgrp = %d\n", getpgrp());
}
return ;
}
程序运行结果如下:
in child pgrp = 7930
in pareant pgrp = 7929
本来复制出来的子进程和原始父进程同属于一个进程组,原始父进程亲自担任组长,但是子进程由于调用setpgid(getpid(), getpid());将自己变成了一个新的进程组,自己亲自担任组长,所以我们看到这里的父子进程各自都属于自己的一组,所以此后从子进程复制出的子进程就会属于新的一组,父进程父子出的子进程属于父进程这一组。
4、会话期
我们知道一个或多个进程组合在一起就组成为了一个进程组,但当多个进程组被组合在一起的集合,这个集合就被称为会话期,如下图所示,该会话期中有三个进程组,进程组之间由shell管道线连接组成。
4.1、setsid函数
1)、函数原型和所需头文件
#include <unistd.h>
pid_t setsid(void);
2)、函数功能:将调用该函数的进程变成一个新的会话期,但是进程组组长不能被设置为一 个新的会话期。
3)、函数参数:无参数。
4)、函数返回值:成功,返回新的会话期ID,失败返回-1,errno被设置。
5)、注意
·进程组中担任组长的进程不能被设置为新的会话期,组长调用了该函数后会出错返回, 为了避免这种情况的发生,通常先fork出子进程,然后使其父进程终止,子进程继续运 行,新的子进程与父进程同属于一个进程组,但是子进程不可能是进程组组长。
·创建新的会话期的进程就是会话期的首进程,会话期ID就是该进程的进程ID。
·会话期首进程也成为了会话期中第一个进程组中的组长。
·此进程没有控制进终端,如果在调用setsid函数之前此进程有一个控制终端,那么这 种关系也会被解除了。
6)、测试用例
5、控制终端
5.1、会话期的特点
1)、一个会话期可以有一个控制终端,这个通常是我们登录的终端设备或伪终端设备。
2)、建立与控制终端连接的对话器首进程被称为控制进程。
3)、一个会话期中的多个进程组被分为一个前台进程组和一个或多个后台进程组。
4)、一个会话期可以有0个或1个前台进程组,但是至少要有一个后台进程组。
5)、如果会话期有控制终端的话,一定有一个前台进程组,前台进程组有与控制终端交互的 权利,其它所有的进程组的都是后台进程组。
6)、无论何时按下中断键(ctrl+c或ctrl+\),其参生的SIGINT或SIGQUIT信号被发送前台 进程组中的所有进程。
7)、如果中断界面已经检测到调制解调器已经脱离或则终端已经断开,则挂断信号会被 送至控制进程。
例子说明:
1、当我们打开一个终端时,这个终端本身就是一个运行的进程,该进程创建了一个新的会话期, 该shell进程就是创建此会话期的首进程,也是控制进程,这个会话期中就只有一个进程组,就 是会话期首进程所在的组,也是前台进程组,这时我们能够直接输入shell命令到shell终端,因 为前台进程组拥有与控制终端交互的权利。
2、当我们./a.out在该shell终端上运行新的程序时,不加&,不把程序放到后台运行的情况下, 该程序的第一个进程独自成立一组自己亲自担任组长,它所复制出来的进程均属于这一进程组, 该进程组就变为了前台进程组,我们拥有与前台该进程组的组长进程的终端交互权,如果我们将 新程序放到后台运行的话(如./a.out &)。新运行的程序创建出的新的一组属于后台进程组,shell 终端命令行仍然控制着终端交互权。
3、从新运行的程序所在进程组中分列出的所有的进程组全部都将变为后台进程组。
linux_api之进程环境(二)的更多相关文章
- linux_api之进程环境
本篇索引: 1.引言 2.main函数 3.进程的终止方式 4.exit和_exit函数 5.atexit函数 7.环境表 8.C程序程序空间布局 9.存储空间的手动分配 10.库文件 1.引言 一个 ...
- (五) 一起学 Unix 环境高级编程 (APUE) 之 进程环境
. . . . . 目录 (一) 一起学 Unix 环境高级编程 (APUE) 之 标准IO (二) 一起学 Unix 环境高级编程 (APUE) 之 文件 IO (三) 一起学 Unix 环境高级编 ...
- Linux进程环境
Linux下C程序都是main开始的,main函数的原型是: int main(int argc, char **argv) 其中argc是命令行参数的数目,argc是指向参数的各个指针所构成的数组. ...
- Unix环境高级编程(五)进程环境
本章主要介绍了Unix进程环境,包含main函数是如何被调用的,命令行参数如何传递,存储方式布局,分配存储空间,环境变量,进程终止方法,全局跳转longjmp和setjmp函数及进程的资源限制. ma ...
- Unix编程第7章 进程环境
准备雄心勃勃的看完APUE,但是总感觉看着看着就像进入一本字典,很多地方都是介绍函数的用法的,但是给出例子远不及函数介绍的多.而且这本书还是个大部头呢.第7章的讲的进程环境,进程是程序设计中一个比较重 ...
- Linux/UNIX之进程环境
进程环境 进程终止 有8种方式使进程终止,当中5中为正常终止,它们是 1) 从main返回 2) 调用exit 3) 调用_exit或_Exit 4) 最后一个 ...
- Qemu搭建ARM vexpress开发环境(二)----通过u-boot启动Linux内核
Qemu搭建ARM vexpress开发环境(二)----通过u-boot启动Linux内核 标签(空格分隔): Qemu ARM Linux 在上文<Qemu搭建ARM vexpress开发环 ...
- Linux进程管理(二)
目录 Linux进程管理(二) 参考 vmstat命令 top命令 Linux进程管理(二)
- 《UNIX环境高级编程》(APUE) 笔记第七章 - 进程环境
7 - 进程环境 Github 地址 1. main 函数 C 程序总是从 main 函数 开始执行: int main(int argc, char *argv[]); \(argc\) 为命令行参 ...
随机推荐
- 常用SQL性能统计代码
1 BEGIN DBMS_STATS.GATHER_TABLE_STATS(OWNNAME=> 'TELEDB', TABNAME=> 'WFGTEST', PARTNAME=> N ...
- Nginx配置 简单写了个
#user nobody;worker_processes 1; #error_log logs/error.log;#error_log logs/error.log notice;#error_l ...
- NPOI row.Cells[i] 的坑
我有A行B列的EXCEL表格: 获取一行数据: IRow row=sheet.GetRow(i); 正常是这样的 row.Cells.Count = B; 但由于会存在没有激活的单元格,导致 row. ...
- ZooKeeper的部署和测试
一背景 zookeeper是一个开源的分布式应用程序协调服务,是Apache Hadoop 的一个子项目.它是一个为分布式应用提供一致性服务的软件,提供的功能包括:配置维护.名字服务.分布式同步.组服 ...
- day02.3-元组内置方法
元组——tuple的定义:test = (11,"alex",(22,33),[44,55],True) 特点:1. 元组是可迭代对象 2. 元组中元素是有序的,但其不可修改,也不 ...
- spring 学习(三):aop 学习
spring 学习(三):aop 学习 aop 概念 1 aop:面向切面(方面)编程,扩展功能不修改源代码实现 2 AOP采取横向抽取机制,取代了传统纵向继承体系重复性代码 3 aop底层使用动态代 ...
- TypeScript入门-类
▓▓▓▓▓▓ 大致介绍 在ECMASript6中引入了类这一概念,通过class声明一个类.对于学习过C和C++的人应该不会陌生 ▓▓▓▓▓▓ 类 看一个简单的类: class Greeter { g ...
- php Tp5下mysql的增删改查
// 增 public function insert(){ $data = array( "username"=>"user121", "pa ...
- upsource代码审查
upsource 从零搭建代码审查平台,需要的不仅是把代码审查的工具搭起来,还要结合公司情况制定一系列的代码审查规范.下面是对选择的upsource web端代码审查工具的安装及介绍.详细的请看这篇文 ...
- XAF ORMDataModel构建的基础资料对象无法被调用显示的解决办法
修正,其实只要在基础资料类中加入[XafDefaultProperty("名称")]标签即可. namespace NEO.ERP.Module.BusinessObjects.B ...