APUE学习笔记:第九章 进程关系

9.1 引言

本章将更详尽地说明进程组以及POSIX.1引入的会话的概念。还将介绍登陆shell(登录时所调用的)和所有从登陆shell启动的进程之间的关系。

9.1 终端登陆

系统管理员创建通常名为/etc/ttys的文件,其中每个终端设备都有一行,每一行说明设备名传递给getty程序的参数。当系统自举时,内核创建进程ID为1的进程,依旧是init进程。init进程使系统进入多用户状态。init进程读文件/etc/ttys,对每一个允许登陆的终端设备,init调用一次fork,所生成的子进程则执行(exec)getty程序。(init以空环境执行getty程序)

getty为终端设备调用open函数,以读、写方式将终端打开。如果设备是调制解调器,则open可能会在设备驱动程序中滞留,知道用户拨号调制解调器,并且呼叫被应答。一旦设备被打开,则文件描述符0、1、2就被设置到该设备。然后getty输出“login:”之类的信息,并等待用户键入用户名。如果终端支持多种速度,则getty可以测试特殊字符以便适当地更改终端速度(波特率)

当用户键入了用户名后,getty的工作就完成了。然后调用login程序。init以一个空环境调用getty。getty以终端名和在gettytab中说明的环境字符串为login创建一个环境。

login能执行多项工作。因为它得到了用户名,所以能调用getpwnam取得相应用户的口令文件登陆项。然后调用getpass以显示提示“password:”,接着读用户键入的口令。它调用crypt将用户键入的口令加密,并与该用户在阴影口令文件中登陆想的pw_passwd字段相比较。如果用户几次键入的口令都无效,则login以参数1调用exit表示登陆过程失败。父进程了解到子进程终止情况后,将在此调用fork,其后接着执行getty,对此终端重复上述过程。

如果用户正确登陆,login就将执行如下工作:

-将当前工作目录更改为该用户的起始目录

-调用chown改变该终端的所有权,是登陆用户成为它的所有者

-将对该终端设备的访问权限改变成用户读和写

-调用setgid和initgroups设置进程的组ID

-用login所得到的所有信息初始化环境:起始目录、shell、用户名,以及一个系统默认路径

-login进程改变为登陆用户的用户ID并调用该用户的登陆shell

到此为止,登录用户的登陆shell开始运行。

9.3 网络登陆

通过网络登陆时,终端和计算机之间的连接不是点对点链接。在这种情况下,login只是一种可用的服务,这与其他网络服务的性质相同。

在网络登陆情况下,所有登陆都经由内核的网络接口驱动程序,实现并不知道将会有多少这样的登陆。我们不是使进程等待每个可能的登陆,而是必须等待一个网络连接请求的到达。

为使同一个软件既能处理终端login,又能处理网络login,系统使用了一种称为伪终端的软件驱动程序,它仿真串行终端的运行行为,并将终端操作映射为网络操作

作为系统启动的一部分,init调用一个shell,使其执行shell脚本/etc/rc.由此shell脚本启动一个守护进程inetd。一旦此shell脚本终止,inetd的父进程就变成init。inetd等待TCP/IP连接请求到达主机,而当一个连接请求到达时,它执行一次fork,然后生成的子进程执行适当的程序。

我们假定到达了一个针对TELNET服务的TCP连接请求。TELNET是使用TCP协议的远程登陆应用程序。在另一台主机(它通过某种形式的网络与服务进程的主机相连接)上的用户,或者同一台主机上的用户启动TELNET客户进程,由此启动登陆过程:

telnet hostname

该客户进程打开一个到hostname主机的TCP连接,在hostname主机上启动的程序被称为TELNET服务进程。然后,客户进程和服务进程之间使用TELNET应用协议通过TCP连接交换数据。所发生的是启动客户进程的用户现在登录到了服务进程所在的主机。

然后,telnetd进程打开一个伪终端设备,并用fork分成两个进程。父进程处理通过网络连接的通信,子进程则执行login程序。父子进程通过伪终端相连接。在调用exec之前,子进程使其文件描述符0,1,2与伪终端相连。如果登陆正确,login就执行:更改当前工作目录为起始目录,设置登陆的组ID和用户ID,以及登陆用户的初始环境。然后login调用exec将自身替换为登陆用户的登陆shell

(当通过终端或网络登陆时,我们得到一个登陆shell,其标准输入、输出和标准出错连接到一个终端设备或者伪终端设备上。

9.4 进程组

函数getpgrp返回调用进程的进程组ID

#include<unistd.h>

pid_t getpgrp(void);

            //返回值:调用进程的进程组ID

进程可以通过调用setpgid来加入一个现有的组或者创建一个新进程组

#include<unistd.h>

int setpgid(pid_t pid,pid_t pgid);

            //返回值:若成功则返回0,若出错则返回-1

9.5 会话

会话是一个或多个进程组的集合。

通常是shell的管道线将几个进程编程一组的。进程调用setsid函数建立一个新会话

#include<unistd.h>

pid_t setsid(void);

        //返回值:若成功则返回进程组ID,若出错则返回-1

如果调用此函数的进程不是一个进程组的组长,则此函数就会创建一个新会话,结果将发生下面3件事:

(1)该进程编程新会话首进程(会话首进程是创建该会话的进程),此时该进程是新会话中的惟一进程

(2)该进程称为一个新进程组的组长进程。新进程组ID是该调用进程的进程ID

(3)该进程没有控制终端。如果在调用setsid之前该进程有一个控制终端,那么这种联系也会被中断

如果该调用进程已经是一个进程组的组长,则此函数返回出错。为了保证不发生这种情况,通常先调用fork,然后使其父进程终止,而子进程继续。因为子进程继承了父进程的进程组ID,而其进程ID则是新分配的,两者不可能相等,所以这就保证子进程不会是一个进程组的组长。

9.6 控制终端

会话和进程组有一些其他特性:

-一个会话可以有一个控制终端。这通常是登录到其上的终端设备(在终端登录情况下)或伪终端设备(在网络登陆情况下)

-建立与控制终端链接的会话首进程被称为控制进程

-一个会话中的几个进程组可被分成一个前台进程组以及一个或几个后台进程组

-如果一个会话有一个控制终端,则它有一个前台进程组,会话中的其他进程组则为后台进程组

-无论何时键入终端的中断键,就会将中断信号发送给前台进程组的所有进程

-无论何时键入终端的退出键,就会将退出信号发送给前台进程组的所有进程

-如果终端接口检测到调制解调器已经断开连接,则将挂断信号发送给控制进程

9.7 tcgetpgrp、tcsetpgrp和tcgetsid函数

需要有一种方法来通知内核哪一个进程组是前台进程组,这样,终端设备驱动程序就能了解将终端输入和终端产生的信号送到何处

#include<unistd.h>

pid_t tcgetpgrp(int filedes);
        //返回值:若成功则返回前台进程组的进程ID,若出错则返回-1

int tcsetpgrp(int filedes,pid_t pgrpid);

                //返回值:若成功则返回0,若出错则返回-1

9.8 作业控制

作业控制要求下面三种形式的支持:

(1)支持作业控制的shell

(2)内核中的终端驱动程序必须支持作业控制

(3)内核必须提供对某些作业控制信号的支持

9.9 shell执行程序

管道线中最后一个进程是shell的子进程,该管道线中的第一个进程则是最后一个进程的子进程。当最后一个进程终止时,shell得到通知

9.10 孤儿进程组

我们曾提及,一个其父进程已经终止的进程称为孤儿进程,这种进程由init进程“收养”。现在我们要说明整个进程组也可称为“孤儿”

实例: 9_1 创建一个孤儿进程组

 1 #include"apue.h"
 2 #include<errno.h>
 3
 4 static void sig_hup(int signo)
 5 {
 6     printf("SIGHUP received,pid=%d\n",getpid());
 7 }
 8 static void pr_ids(char *name)
 9 {
10     printf("%s:pid=%d,ppid=%d,pgrp=%d,tpgrp=%d\n",name,getpid(),getppid(),
11         getpgrp(),tcgetpgrp(STDIN_FILENO));
12     fflush(stdout);
13 }
14 int main()
15 {
16     char c;
17     pid_t pid;
18     pr_ids("parent");
19     if((pid=fork())<0){
20     err_sys("fork error");
21     }else if(pid>0){
22     sleep(5);
23     exit(0);
24     }
25     else{
26     pr_ids("child");
27     signal(SIGHUP,sig_hup);
28     kill(getpid(),SIGTSTP);
29     pr_ids("child");
30     if(read(STDIN_FILENO,&c,1)!=1)
31     printf("read error from controlling TTY,errno=%d\n",errno);
32     exit(0);
33 }}

APUE学习笔记:第九章 进程关系,布布扣,bubuko.com

时间: 2024-12-24 11:53:00

APUE学习笔记:第九章 进程关系的相关文章

《Python基础教程(第二版)》学习笔记 -&gt; 第九章 魔法方法、属性和迭代器

准备工作 >>> class NewStyle(object): more_code_here >>> class OldStyle: more_code_here 在这两个类中,NewStyle是新式的类,OldStyle是旧式的类,如果文件以__metaclass__ = type 开始,那么两个类都是新式类. 构造方法 构造方法,当一个对象被创建后,会立即调用构造方法.Python中创建一个构造方法,只要把init方法的名字从简单的init修改成__init__

Java学习笔记—第九章 字符串String

第九章 字符串String Java中使用String类来创建一个字符串变量,字符串变量是类类型变量,是一个对象.声明字符串的语法格式如下:String s; 创建字符串:通过String类提供的构造方法可创建字符串,有以下几种方式: (1)创建字符串对象时直接赋值,例如: String s1 = "hello"; String s2 = new String("hello"); (2)由一个字符串创建另一个字符串,例如: String s1 = "hel

Android学习笔记—第九章 Activity的加载模式

第九章 Activity的加载模式 task:类似于栈,每次打开界面会创建一个task,然后将这开启的界面放入到该task中. (1)standard:默认模式 每次都会创建一个新的界面,将该界面加入task中 (2)singleTop:栈顶单实例模式 a. 如果目标Activity不存在,创建一个新的Activity,存入到task中 b. 如果目标Activity已经存在,并且处于栈顶,不会再创建新的Activity c. 如果目标Activity已经存在,但不处于栈顶,创建一个新的Acti

apue学习笔记(第九章 进程关系)

本章将详细地说明进程组以及POSIX.1引入的会话的概念.还将介绍登录shell和所有从登录shell启动的进程之间的关系 终端登录 BSD终端登录.系统管理者创建通常名为/etc/ttys的文件,其中每个终端设备都有一行,用来说明设备名和传到getty程序的参数. 当系统自举时,内核创建进程ID为1的进程(init进程).init进程读取文件/etc/ttys,对每一个允许登录的终端设备调用一次fork,它所生成的子进程则exec getty程序,如下图所示: getty对终端设备调用open

《DOM Scripting》学习笔记-——第九章 CSS-DOM

本章内容: 一.style属性 二.如何检索样式信息 三.如何改变样式 属性: 包含位置信息:parentNode , nextSibling , previousSibling , childNodes , firstChild , lastChild 包含元素本身信息:nodeType,nodeName 包含元素样式信息:style 举例: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w

《metasploit渗透测试魔鬼训练营》学习笔记第九章--meterpreter

七.强大的meterpreter  7.1再探metasploit的攻击载荷模块     7.1.1典型的攻击载荷模块     metasploit涵盖了各大主流操作系统和平台,其中绝大部分是远程漏洞利用所使用的攻击载荷模块,功能一般是开启远程shell,远程执行命令.     metasploit支持用户将自己的shellcode导入框架中,只需将payload替换成自己的shellcode代码,修改一下描述等基础信息即可.     7.1.2使用攻击载荷模块     search 搜索,查询

JavaScript高级程序设计学习笔记第九章--客户端检测

1.能力检测:能力检测的目标不是识别特定的浏览器,而是识别浏览器的能力.(我的理解就是识别浏览器能做什么不能做什么) 2.怪癖检测:目标是识别浏览器的特殊行为.但与能力检测确认浏览器支持什么能力不同,怪癖检测是想要知道浏览器存在什么缺陷(“怪癖”也就是 bug). 3.用户代理检测:争议最大的一种客户端检测技术.用户代理检测通过检测用户代理字符串来确定实际使用的浏览器.在每一次 HTTP 请求过程中,用户代理字符串是作为响应首部发送的,而且该字符串可以通过 JavaScript 的 naviga

matlab学习笔记第九章——变换

1.拉普拉斯变换:时间函数f(t)的拉普拉斯变换用下面的积分式定义: L {f(t)} = ??0∞f(t)e-stdt,我们通常把f(t)的拉普拉斯变换写F(s).在MATLAB计算拉普拉斯变换,我们要调用laplace(f(t)),它做的是符号计算. L (tn) = n!/sn+1 要计算拉普拉斯逆变换,我们输入ilaplace 2.一个函数f(t)的傅立叶变换被定义为: F(ω) = ??-∞∞f(t)e-iωtdt 在MATLAB中我们可以输入fourier命令计算一个函数的傅立叶变换

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

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