linux内核之进程的基本概念(进程,进程组,会话关系)

进程是操作系统的一个核心概念。每个进程都有自己唯一的标识:进程ID,也有自己的生命周期。一个典型的进程的生命周期如图4-1所示。

进程都有父进程,父进程也有父进程,这就形成了一个以init进程为根的家族树。除此以外,进程还有其他层次关系:进程、进程组和会话。

进程组和会话在进程之间形成了两级的层次:进程组是一组相关进程的集合,会话是一组相关进程组的集合。

这样说来,一个进程会有如下ID:

·PID:进程的唯一标识。对于多线程的进程而言,所有线程调用getpid函数会返回相同的值。

·PGID:进程组ID。每个进程都会有进程组ID,表示该进程所属的进程组。默认情况下新创建的进程会继承父进程的进程组ID。

·SID:会话ID。每个进程也都有会话ID。默认情况下,新创建的进程会继承父进程的会话ID。

可以调用如下指令来查看所有进程的层次关系:

  1. ps -ejH
  2. ps axjf

进程,可以调用以下函数获取进程组ID跟会话ID.

  1. pid_t getpgrp(void);
  2. pid_t getsid(pid_t pid);

前面提到过,新进程默认继承父进程的进程组ID和会话ID,如果都是默认情况的话,那么追根溯源可知,所有的进程应该有共同的进程组ID和会话ID。但是调用ps axjf可以看到,实际情况并非如此,系统中存在很多不同的会话,每个会话下也有不同的进程组。

为何会如此呢?

就像家族企业一样,如果从创业之初,所有家族成员都墨守成规,循规蹈矩,默认情况下,就只会有一个公司、一个部门。但是也有些“叛逆”的子弟,愿意为家族公司开疆拓土,愿意成立新的部门。这些新的部门就是新创建的进程组。如果有子弟“离经叛道”,甚至不愿意呆在家族公司里,他别开天地,另创了一个公司,那这个新公司就是新创建的会话组。由此可见,系统必须要有改变和设置进程组ID和会话ID的函数接口,否则,系统中只会存在一个会话、一个进程组。

进程组和会话是为了支持shell作业控制而引入的概念。

当有新的用户登录Linux时,登录进程会为这个用户创建一个会话。用户的登录shell就是会话的首进程。会话的首进程ID会作为整个会话的ID。会话是一个或多个进程组的集合,囊括了登录用户的所有活动。

在登录shell时,用户可能会使用管道,让多个进程互相配合完成一项工作,这一组进程属于同一个进程组。

当用户通过SSH客户端工具(putty、xshell等)连入Linux时,与上述登录的情景是类似的。

4.2.1 进程组

修改进程组ID的接口如下

  1. int setpgid(pid_t pid, pid_t pgid);

这个函数的含义是,找到进程ID为pid的进程,将其进程组ID修改为pgid,如果pid的值为0,则表示要修改调用进程的进程组ID。该接口一般用来创建一个新的进程组。

下面三个接口含义一致,都是创立新的进程组,并且指定的进程会成为进程组的首进程。如果参数pid和pgid的值不匹配,那么setpgid函数会将一个进程从原来所属的进程组迁移到pgid对应的进程组。

  1. setpgid(0,0)
  2. setpgid(getpid(),0)
  3. setpgid(getpid(),getpid())

setpgid函数有很多限制:

·pid参数必须指定为调用setpgid函数的进程或其子进程,不能随意修改不相关进程的进程组ID,如果违反这条规则,则返回-1,并置errno为ESRCH。

·pid参数可以指定调用进程的子进程,但是子进程如果已经执行了exec函数,则不能修改子进程的进程组ID。如果违反这条规则,则返回-1,并置errno为EACCESS。

·在进程组间移动,调用进程,pid指定的进程及目标进程组必须在同一个会话之内。这个比较好理解,不加入公司(会话),就无法加入公司下属的部门(进程组),否则就是部门要造反的节奏。如果违反这条规则,则返回-1,并置errno为EPERM。

·pid指定的进程,不能是会话首进程。如果违反这条规则,则返回-1,并置errno为EPERM。

有了创建进程组的接口,新创建的进程组就不必继承父进程的进程组ID了。最常见的创建进程组的场景就是在shell中执行管道命令,代码如下:cmd1 | cmd2 | cmd3

下面用一个最简单的命令来说明,其进程之间的关系如图4-2所示。

ps ax|grep nfsd 

ps进程和grep进程都是bash创建的子进程,两者通过管道协同完成一项工作,它们隶属于同一个进程组,其中ps进程是进程组的组长。

进程组的概念并不难理解,可以将人与人之间的关系做类比。一起工作的同事,自然比毫不相干的路人更加亲近。shell中协同工作的进程属于同一个进程组,就如同协同工作的人属于同一个部门一样。

引入了进程组的概念,可以更方便地管理这一组进程了。比如这项工作放弃了,不必向每个进程一一发送信号,可以直接将信号发送给进程组,进程组内的所有进程都会收到该信号。

前文曾提到过,子进程一旦执行exec,父进程就无法调用setpgid函数来设置子进程的进程组ID了,这条规则会影响shell的作业控制。出于保险的考虑,一般父进程在调用fork创建子进程后,会调用setpgid函数设置子进程的进程组ID,同时子进程也要调用setpgid函数来设置自身的进程组ID。这两次调用有一次是多余的,但是这样做能够保证无论是父进程先执行,还是子进程先执行,子进程一定已经进入了指定的进程组中。由于fork之后,父子进程的执行顺序是不确定的,因此如果不这样做,就会造成在一定的时间窗口内,无法确定子进程是否进入了相应的进程组。

用户在shell中可以同时执行多个命令。对于耗时很久的命令(如编译大型工程),用户不必傻傻等待命令运行完毕才执行下一个命令。用户在执行命令时,可以在命令的结尾添加“&”符号,表示将命令放入后台执行。这样该命令对应的进程组即为后台进程组。在任意时刻,可能同时存在多个后台进程组,但是不管什么时候都只能有一个前台进程组。只有在前台进程组中进程才能在控制终端读取输入。当用户在终端输入信号生成终端字符(如ctrl+c、ctrl+z、ctr+\等)时,对应的信号只会发送给前台进程组。

shell中可以存在多个进程组,无论是前台进程组还是后台进程组,它们或多或少存在一定的联系,为了更好地控制这些进程组(或者称为作业),系统引入了会话的概念。会话的意义在于将很多的工作囊括在一个终端,选取其中一个作为前台来直接接收终端的输入及信号,其他的工作则放在后台执行。

会话

会话是一个或多个进程组的集合,以用户登录系统为例,可能存在如图4-3所示的情况。



 系统提供setsid函数来创建会话,其接口定义如下:

  1. #include <unistd.h>
  2. pid_t setsid(void);

如果这个函数的调用进程不是进程组组长,那么调用该函数会发生以下事情:

1)创建一个新会话,会话ID等于进程ID,调用进程成为会话的首进程。

2)创建一个进程组,进程组ID等于进程ID,调用进程成为进程组的组长。

3)该进程没有控制终端,如果调用setsid前,该进程有控制终端,这种联系就会断掉。

调用setsid函数的进程不能是进程组的组长,否则调用会失败,返回-1,并置errno为EPERM。

这个限制是比较合理的。如果允许进程组组长迁移到新的会话,而进程组的其他成员仍然在老的会话中,那么,就会出现同一个进程组的进程分属不同的会话之中的情况,这就破坏了进程组和会话的严格的层次关系了。

来自为知笔记(Wiz)

时间: 2024-10-09 00:17:36

linux内核之进程的基本概念(进程,进程组,会话关系)的相关文章

LINUX内核分析第八周学习总结——进程的切换和系统的一般执行过程

LINUX内核分析第八周学习总结——进程的切换和系统的一般执行过程 黄韧(原创作品转载请注明出处) <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 一.知识概要 Linux中进程调度的基本概念与相关知识 schedule函数如何实现进程调度 Linux进程的执行过程(一般情况与特殊情况) 宏观描述Linux系统执行 二.学习笔记 (一)进程切换的关键代码switch_to分析 进程进度与进程调度的时机分析 1.

LINUX内核分析第六周学习总结——进程的描述和进程的创建

LINUX内核分析第六周学习总结——进程的描述和进程的创建 张忻(原创作品转载请注明出处) <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 一.知识概要 进程的描述 进程描述符task_struct数据结构(一) 进程描述符task_struct数据结构(二) 进程的创建 进程的创建概览及fork一个进程的用户态代码 理解进程创建过程复杂代码的方法 浏览进程创建过程相关的关键代码 创建的新进程是从哪里开始执行的

【学习笔记】 深入理解Linux内核第三版 —— 第三章 进程

何为进程 进程(分享资源 单位)/线程(执行的单位)/轻量级进程(共享部分资源) Linux中线程通过pthead 标准库实现,其中存在实现轻量级进程的方法,方法也可针对线程组执行. 静态:进程如何描述 进程描述符:task_struct数据类型 进程与进程描述符一一对应.进程描述符指针指向进程描述符地址,内核由此来引用进程. PID(process id)可用来标识进程(linux维护pidbitmap-array位图来管理分配PID),同一个线程组的线程使用相同的PID(与第一个线程的值相同

《Linux内核分析》第七周笔记 进程的切换和系统的一般执行过程

20135132陈雨鑫 + 原创作品转载请注明出处 + <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 ” 一.进程调度与进程调度的时机分析 1.进程调度 不同类型的进程有不同的调度需求 第一种分类:       I/O-bound            频繁的进行I/O           通常会花费很多时间等待I/O操作的完成     CPU-bound            计算密集型          

《Linux内核分析》第六周笔记 进程的描述和进程的创建

进程的描述和进程的创建 一.进程的描述 1.进程描述符task_struct数据结构(一) 操作系统的三大功能:进程管理(核心).内存管理.文件系统. 进程控制块PCB——task_struct(进程描述符):为了管理进程,内核必须对每个进程进行清晰的描述,进程描述符提供了内核所需了解的进程信息. struct task_struct数据结构很庞大,共有约400行代码 Linux进程的状态与操作系统原理中的描述的进程状态似乎有所不同,比如就绪状态和运行状态都是TASK_RUNNING,为什么呢?

lab6:分析Linux内核创建一个新进程的过程

李俊锋 + 原创作品转载请注明出处 + <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 一.实验原理 1.进程的定义 进程是操作系统的概念,每当我们执行一个程序时,对于操作系统来讲就创建了一个进程,在这个过程中,伴随着资源的分配和释放.可以认为进程是一个程序的一次执行过程. 2.进程与程序的区别 程序时静态的,它是一些保存 在磁盘上得指令的有序集合,没有任何执行的概念. 进程是一个动态的概念,它是程序执行的过程

【读书笔记】《Linux内核设计与实现》进程管理与调度

大学跟老师做嵌入式项目,写过I2C的设备驱动,但对Linux内核的了解也仅限于此.Android系统许多导致root的漏洞都是内核中的,研究起来很有趣,但看相关的分析文章总感觉隔着一层窗户纸,不能完全理会.所以打算系统的学习一下Linux内核.买了两本书<Linux内核设计与实现(第3版)>和<深入理解Linux内核(第3版)> 0x00 一些废话 面向对象思想. Linux内核虽然是C和汇编语言写的,没有使用面向对象的语言,但里面却包含了大量面向对象的设计.比如可以把内核中的进程

Linux 进程的基础概念

什么是进程? 运行中的程序,是将程序脚本放入内核执行. 进程创建 内核会首先创建Init进程,再由Init进程管理其他进程.进程都是由其父进程创建的. 进程优先级 0-139: 1-99 实时优先级 数字越大 优先级越高 100-139 静态优先级 数字越小 优先级越高 nice值:-20~19 算是微调自己的优先级,但是优先级只能调低,不能调高. 虚拟内存 使用Page frame实现,page frame就是分页.将不连续的物理内存(物理地址空间)以虚拟(线性地址)的方式提供给进程使用.每个

Linux核心调度器之周期性调度器scheduler_tick--Linux进程的管理与调度(十八)

日期 内核版本 架构 作者 GitHub CSDN 2016-6-29 Linux-4.6 X86 & arm gatieme LinuxDeviceDrivers Linux进程管理与调度 我们前面提到linux有两种方法激活调度器:核心调度器和 一种是直接的, 比如进程打算睡眠或出于其他原因放弃CPU 另一种是通过周期性的机制, 以固定的频率运行, 不时的检测是否有必要 因而内核提供了两个调度器主调度器,周期性调度器,分别实现如上工作, 两者合在一起就组成了核心调度器(core schedu

嵌入式Linux内核制作【转】

本文转载自:http://blog.csdn.net/coding__madman/article/details/51291316 1. Linux体系结构 从整体上来分,linux可以分为User Space和Kernel Space构成,即用户空间和内核空间 用户空间:应用程序 C库 系统配置文件等 内核空间:在内存中,用户一般不能直接接触到(系统调用接口,体系结构相关的一些代码等等) 这样划分的的好处: 其实内核空间与用户空间是程序执行的两种不同的状态,通过系统调用和硬件中断能够完成从用