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 = 0;
        ret = fork();
        if(0 == ret)          //原始父进程的第一个子进程
        {
                ret = fork();
                if(0 == ret){
                        printf("pgrp = %d\n", getpgrp());
                }
                else if(ret > 0){
                        printf("pgrp = %d\n", getpgrp());
                }
        }
        else if(ret > 0){
                ret = fork();           //原始父进程的第二个子进程
                if(0 == ret){
                        printf("pgrp = %d\n", getpgrp());
                }
                else if(ret > 0){       //原始父进程
                        sleep(1);//让所有子进程先运行结束
                        printf("pgrp = %d\n", getpgrp());
                }
        }
        return 0;
} 

        return 0;
}
    

上例运行结果如下:

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 = 0;
        ret = fork();
        if(0 == ret){   //原始父进程的第一个子进程
                setpgid(getpid(), getpid());
                printf("in chaild pgrp = %d\n", getpgrp());
        }
        else if(ret > 0){  //原始父进程,有shell父进程复制而来
                sleep(1);
                printf("in pareant pgrp = %d\n", getpgrp());
        }
        return 0;
} 

程序运行结果如下:

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、从新运行的程序所在进程组中分列出的所有的进程组全部都将变为后台进程组。

时间: 2024-10-12 05:09:09

linux_api之进程环境(二)的相关文章

windows - 进程 - 进程环境变量

原文链接1 原文链接2 1.进程的环境变量 每个进程都有一个与它关联的环境块(environment block),这是在进程地址空间内分配的一块内存,其中包含字符串类似于: =::=::\... VarName=VarValue\0... \0 除第一个=::=::\外,块中可能还有其他字符串是以等号开头的,这种字符串不作为环境变量使用. 访问环境块的两种方式: 1)调用GetEnvironmentStrings获取完整的环境块 2)CUI程序专用,通过应用程序main入口点函数所接收的TCH

《Unix环境高级编程》读书笔记 第7章-进程环境

1. main函数 int main( int argc, char *argv[] ); argc是命令行参数的数目,包括程序名在内 argv是指向参数的各个指针所构成的数组,即指针数组 当内核执行C程序时(使用exec函数),在调用main前先调用一个特殊的启动例程.可执行程序文件将此启动例程指定为程序的起始地址——这是由连接器设置的,而连接器则是由C编译器调用.启动例程从内核取得命令行参数和环境变量值,然后按上述方式调用main函数做好安排. 2. 进程终止 有8种方式使进程终止,其中5种

读书笔记-APUE第三版-(7)进程环境

本章关注单进程运行环境:启动&终止.参数传递和内存布局等. 进程启动终止 如图所示: 启动:内核通过exec函数执行程序,在main函数运行之前,会调用启动例程(start-up routine),取得命令行参数和环境变量.可以把启动例程理解为exit(main(argc,argv)). 终止:五种正常终止方式(从main方法返回/exit/_exit/最后一个线程返回/最后一个线程退出):三种异常终止方式(abort/接收到信号/最后一个线程接收到取消请求). exit与_exit关系:exi

Unix编程第7章 进程环境

准备雄心勃勃的看完APUE,但是总感觉看着看着就像进入一本字典,很多地方都是介绍函数的用法的,但是给出例子远不及函数介绍的多.而且这本书还是个大部头呢.第7章的讲的进程环境,进程是程序设计中一个比较重要的概念,知道倒是知道它的大概意思,但是其实还是有很多的细节其实都没有深究,这章呢APUE就带着我们逛了一下如下的几个主题(尼玛,学C语言的话,学那点语法其实不是很重要,反而经常把时间浪费在语法的蹩脚处): 1.程序执行的时候main函数是如何被调用的 2.命令行参数是如何传递给新程序的: 3.典型

APUE学习笔记:第七章 进程环境

7.1 引言 本章将学习:当执行程序时,其main函数是如何被调用的:命令行参数是如何传送给执行程序的:典型的存储器布局是什么样式:如何分配另外的存储空间:进程如何使用环境变量:各种不同的进程终止方式等:另外还将说明longjmp和setjmp函数以及它们与栈的交互作用:还将介绍研究进程的资源限制 7.2 main函数 C程序总是从main函数开始执行.当内核执行C程序时,在调用main前先调用一个特殊的启动例程.可执行程序文件将此启动例程指定为程序的起始地址——这是由连接编辑器设置的,而连接编

通过IsDebuggerPesent解说windows PEB进程环境块结构

首先介绍PEB和TEB概念: PEB(Process Environment Block.进程环境块)存放进程信息.每一个进程都有自己的PEB信息.位于用户地址空间. TEB(Thread Environment Block.线程环境块)系统在此TEB中保存频繁使用的线程相关的数据.位于用户地址空间.在比 PEB 所在地址低的地方. 进程中的每一个线程都有自己的一个TEB. 写一个使用IsDebuggerPesent函数的windows程序.用OD打开,断点IsDebuggerPesent函数

通过IsDebuggerPesent讲解windows PEB进程环境块结构

首先介绍PEB和TEB概念: PEB(Process Environment Block,进程环境块)存放进程信息,每个进程都有自己的PEB信息.位于用户地址空间. TEB(Thread Environment Block,线程环境块)系统在此TEB中保存频繁使用的线程相关的数据.位于用户地址空间,在比 PEB 所在地址低的地方.进程中的每个线程都有自己的一个TEB. 写一个使用IsDebuggerPesent函数的windows程序,用OD打开,断点IsDebuggerPesent函数 OD进

Linux/UNIX之进程环境

进程环境 进程终止 有8种方式使进程终止,当中5中为正常终止,它们是 1)      从main返回 2)      调用exit 3)      调用_exit或_Exit 4)      最后一个线程从其启动例程返回 5)      最后一个线程调用pthread_exit 异常终止有3中方式 6)      调用abort 7)      接到一个信号并终止 8)      最后一个线程对取消请求做出对应 exit函数 #include <stdlib.h> void exit(int

Unix环境高级编程(五)进程环境

本章主要介绍了Unix进程环境,包含main函数是如何被调用的,命令行参数如何传递,存储方式布局,分配存储空间,环境变量,进程终止方法,全局跳转longjmp和setjmp函数及进程的资源限制. main函数的原型为int main(int argc,char *argv[]);其中argc是命令行参数的数目,argv是指向参数的各个指针构成的数组.当内核执行C程序时,使用一个exec函数,在调用main函数前线调用一个特殊的启动例程,从内核获取命令行参数和环境变量. 进程终止分为正常终止和异常