进程关系

1.终端,控制台,控制终端的概念
linux

1.1控制台和控制台shell

终端和控制台都不是我的电脑的概念,而是多人共用的小型中型大型计算机上的概念。
编程

一台主机,连不少终端,终端为主机提供了人机接口,每一个人都经过终端使用主机的资源.。windows

终端有字符哑终端和图形终端两种.服务器

控制台是另外一种人机接口,,不经过终端与主机相连,,而是经过显示卡-显示器和键盘接口网络

别与主机相连, 这是人控制主机的第一人机接口。函数

回到个人计算机上,我的计算机只有控制台,没有终端. 固然愿意的话,,能够在串口上连一测试

两台字符哑终端.。可是linux偏要按POSIX标准把我的计算机当成小型机来用,那么就在控制ui

台上经过getty软件虚拟了六个字符哑终端(或者叫控制台终端tty1-tty6,数量能够在加密

/etc/inittab里本身调)和一个图型终端。在虚拟图形终端中又能够经过软件(如rxvt)再虚拟

无限多个虚拟字符哑终端pts/0....)。记住,这全是虚拟的,用起来同样,但实际上并非。

因此在我的计算机上,只有一个实际的控制台,没有终端,全部终端都是在控制台上用软件模拟的。
要把我的计算机当主机再经过串口或网卡外连真正的物理终端也能够,但因为真正的物理终端

并不比我的计算机自己便宜,通常没有人这么作。

1.2切换控制台

如同其余UNIX类系统,Linux自己也是基于命令行的。试试“Ctrl”+“Alt”+“Fx”。这就是控制台算是Linux的原本面目。至于使用方法,除了多出登陆注销外,其它操做和咱们在linux图形界面

(X—window)下的终端操做是同样的,在X-Window出问题或不运行X-Window的时候,操做

主要在这里完成。

  Linux在控制台下提供了不止一个(字符哑)终端,支持多用户同时登陆,包括在本机同时登陆。

控制台“Alt”+“Fx”可以切换到第x个(字符哑)终端。若是须要从X-Window里跳到第(字符哑)终端,

须要“Ctrl”+“Alt”+“Fx”。通常状况下若是要从控制台返回Xwindow可用““Alt”+7”来返回到Xwind的

图形界面。(Linux发行版提供7个虚拟屏幕,1~6号是控制台终端((字符哑)终端),第7个

上面跑X-Window。)

1.3控制终端(/dev/tty)

这是个在应用程序中的一个概念,前台进程有个控制终端,就对应这个。

不过它并不指任何物理意义上的终端,其实/dev/tty会映射到当前的设备(经过tty命令能够看到),

好比你若是在控制台界面下(即字符界面下)那么dev/tty就是映射到dev/tty1-6之间的一个(取决于你当

前的控制台号),可是若是你如今是在图形界面(Xwindows),那么你会发现如今的/dev/tty映射到

的是/dev/pts的伪终端上。好比你能够输入命令 #tty 那么将显示当前映射终端如:/dev/tty1或者/dev/pts/0等。

///////////////////////////////////////////////在第四条以后buchong

(1)一个会话能够有一个控制终端,这一般是终端设备(在如今的我的电脑上,一般是虚拟的tty1-tty6,而后图形界面虚拟的),或者伪终端设备,创建与控制终端链接的首进程叫作控制进程,不是前台进程组

(2)一个会话中的进程组能够被分红前台进程组(必须该会话有一个控制终端)和后台进程组(除前台进程组以外的都是后台进程组)

(3)若是键入delte或者是ctrl+c则(中断信号),ctrl+\(终端退出键)被送往前台进程组,而且当终端接口检测到调制解调器断开了链接,则将挂断信号发送到控制进程


咱们通常不须要担忧控制终端,通常控制终端在登录的时候本身进行创建

POSIX.1分配控制终端的机制

介绍,好比说在linux3.2.0中,若是在第一次调用open的时候,前提是没有O_NOCITY的时候,就将此open的控制终端分配给此会话

这是从system V派生的系统

下面的命令是相同的

有的时候不论是否标准输入,标准输出是否重定向,程序都须要与控制终端进行交互(若是重定向了,标准输入输出就不必定指向的是控制终端,所以在这里就须要万无一失准确的打开控制终端)

2终端登录

2.1终端登录::::(这里是终端登录,并非咱们日常的我的电脑直接登录,可是大致相同)

(1)系统管理者建立一个一般名为/etc/ttys的文件(这个文件原本就有),其中,每一个终端设备有一行,每一行说

明设备名和传到getty程序的参数,这些参数说明了终端的波特率等。

(2)当系统自举时,内核建立进程 ID 1,也就是init进程。init进程使系统进入多用户状态。init

读文件/etc/ttys,对每个容许登陆的终端设备, init调用一次fork(以空环境执行getty程序),它所生成的子进程则

执行程序getty。        

(3) getty对终端设备调用open函数,以读、写方式将终端打开。若是设备是调制解调器,则open可能会在设备

驱动程序中滞留,直到用户拨号调制解调器,而且线路被接通。一旦设备被打开,则文件描述符 0、一、2就被

设置到该设备。而后getty输出“login:”之类的信息,并等待用户键入用户名。若是终端支持多种速度,则

getty能够测试特殊字符以便适当地更改终端速度 (波特率)(综上来看getty主要是用来打开终端,分配文件标识

符,而且最后调用login登录程序)

(4)当用户键入了用户名后,getty就完成了。而后它以相似于下列的方式调用login程序:

execle("/usr/bin/login", "login", "-p", username, (char *) 0, envp);

(在gettytab文件中可能会有一些选择项使其调用其余程序,但系统默认是login程序)。init以一个空环境调用getty

。getty以终端名(例如TERM=foo, 其中终端foo的类型取自gettytab文件-->绿色的都放在envp中)和在gettytab中

的环境字符串为 login建立一个环境( envp参数)。-p标志通知login保留传给它的环境,也可将其余环境字符串加

到该环境中,可是不要替换它。图 9 - 2显示了login刚被调用后这些进程的状态。

(5)login 能处理多项工做。由于它获得了用户名,因此能调用 getpwnam 取得相应用户的口令文件登陆项。而后

调用 getpass(3)以显示提示“ Password:”接着读用户键入的口令(天然,禁止回送用户键入的口令)。它调用 

crypt(3)将用户键入的口令加密,并与该用户口令文件中登陆项的pw_passwd字段相比较。若是用户几回键入

口令都无效,则 login 以参数1调用 exit 表示登陆过程失败。父进程(init)了解到子进程的终止状况后,将再次

调用fork其后又跟随着执行getty,对此终端重复上述过程。若是用户正确登陆,login就将当前工做目录更改

为该用户的起始目录 (chdir)。它也调用chown改变该终端的全部权,使该用户成为全部者和组全部者。将对该

终端设备的存取许可权改变成:用户读、写和组写。调用setgid及initgroups设置进程的组ID。而后用login所得

到的全部信息初始化环境:起始目录 (HOME )、shell ( SHELL)、用户名(USER和LOGNAME),以及一

个系统默认路径 (PATH)。最后login进程改变为登陆用户的用户 I D (setuid )并调用该用户的登录shell,其方式相似于:execl("/bin/sh", "-sh", (char *) 0);(login主要是用来在getty获得用户名以后,进行登录

argv[0]的第一个字符-是一个标志,表示该shell被调用为登陆shell.shell能够查看此字符,并相应地修改其起动过程。

最后一步setuid改变一下3个用户ID:实际用户ID,有效用户ID,和保存的用户ID!!(登录成功sh用来启动一个shell,叫作登录shell

3网络登录

3.1网络登录与终端登录的区别

终端登陆中 ,init知道哪些终端设备可用来进行登陆,并为每一个设备生成一个getty进程。可是,对网络登陆则状况

有所不一样,全部登陆都经由内核的网络界面驱动程序(例如:以太网驱动程序),事先并不知道将会有多少这样的登

录。不是使一个进程等待每个可能的登陆,而是必须等待一个网络链接请求的到达。在4.3+BSD中,有一个称

为 i n e t d的进程(有时称为Internet superserver),它等待大多数网络链接

3.2网络登录

(1)做为系统起动的一部分,init调用一个shell,使其执行shell脚本etc/rc。由此shell脚本起动一个精灵进程 inted。一

旦此shell脚本终止,inted的父进程就变成init。inted等待TCP/IP链接请求到达主机,而当一个链接请求到达时,它

执行一次fork,而后该子进程执行适当的程序。(在这里执行了一次fork)

(2)假定到达了一个对于TELNET服务器的TCP链接请求.TELNET是使用TCP协议的远程登陆应用程序。在另

一个主机 (它经过某种形式的网络,链接到服务器主机上 )上的用户,或在同一个主机上的一个用户起动 

TELNET客户进程( c l i e n t )起动登陆过程:

                                                telnet hostname

该客户进程打开一个到名为hostname的主机的TCP链接,在hostname主机上起动的程序被称为TELNET服务器

。而后,客户进程和服务器进程之间使用TELNET应用协议经过TCP链接交换数据。所发生的是起动客户进程的

用户如今登陆到了服务器进程所在的主机。(天然,用户须要在服务器进程主机上有一个有效的帐号)。

图 9 - 4显示了在执行 TELNET服务器进程 (称为telnetd )中所涉及的进程序列。而后telnetd进程打开一个

伪终端设备,并用fork生成一个子进程。父进程处理经过网络链接的通讯,子进程则执行login程序。父、子

进程经过伪终端相链接。在调用exec以前,子进程使其文件描述符 0 , 1 , 2与伪终端相连。若是登陆正确

, login就执行9.2节中所述的一样步骤 — 更改当前工做目录为起始目录,设置登陆用户的组ID和用户ID,

以及登陆用户的初始环境。而后login用exec将其自身替换为登陆用户的登陆shell。图9 - 5显示了到达这一点

时的进程安排。

须要理解的重点是:当经过终端(见图 9 - 3)或网络(见图 9 - 5)登陆时,咱们获得一个登录shell,其标准输入、输出

和标准出错链接到一个终端设备或者伪终端设备上。在下一节中咱们会了解到这一登陆shell是一个PISXO.1对

话期的开始,而此终端或伪终端则是会话期的控制终端。


4进程组

每个进程都有一个进程组ID,进程组是一个或多个进程的集合,每个进程组都有一个进程组长,进程组组长的pid等于组id,进程组组长能够建立一个进程组,只要本进程组有一个进程存在,这个进程组就是存在的

4.1取得进程组ID

#include<unistd.h>

pid_t getpgrp(void);

获得调用进程的进程组ID,若是成功则返回进程组ID,出错,返回-1

pid_t getpgid(pid_t pid);

获得进程为pid的进程组ID,若是pid是0,则获得调用进程的进程组id

4.2设置进程组ID

#include <unistd.h>

int setpgid(pid_t pid,pid_t pgid);将pid的进程组ID设置成pgid

(1)若是pid不等于pgid:将指定pid的进程组的组ID设置成pgid

(2)pid=pgid:由pid指定的进程变成组长进程

(3)pid=0:使用调用者的pid,将其进程组的id设置成pgid

(4)pgid=0:则pid指定的进程ID,变成组长进程

一个进程只能给本身或者子进程设置组ID,而且在子进程调用了exec以后,就不能再进行进程组的设置

5会话

会话的定义,就是一个或者多个进程组

5.1建立一个新的会话

pid_t setsid(void);

建立一个新的会话

注意点

(1)应该保证调用此函数的进程不是进程组的组长,为了保证不是进程组长ID,一般进行fork,而后终止其父进程,由于进程组是继承的,因此不可能与新的ID重合

(2)该进程编程新会话的会话首进程

(3)该进程的ID成为第一个进程组的ID,即新进程组的ID是该进程ID,第一个进程的ID也是会话ID

(4)该进程事没有控制终端的,若是有,也进行切断

pid_t tcgetpgrp(int fd);

函数返回前台进程组的ID,这个进程组ID与fd相关连

int tcsetpgrp(int fd,pid_t pgrpid);

从fd找到前台进程组ID,将前台进程组ID设置成pgrpid,就这样!!

pid_t getsid(void);

获得会话首进程的进程组ID!!!

一个会话能够没有控制终端,没有控制终端证实着没有与终端进行交互的功能

pid_t tcgetsid(int fd);

给出控制TTY的文件描述符,经过此函数就能获得会话首进程的进程组ID

6做业控制

有做业控制特性能够在终端上连续启动不少个做业(多个进程组)可是要具有做业控制必须

(1)支持做业控制的shell

(2)内核中的终端驱动程序必须支持做业控制(这体现子在当用户输入一个退出,中断的时候,驱动程序应该接受)

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

列如在setsid的时候已经创建了会话,在登录shell指向终端驱动的时候,其实在这里创建了前台进程组(即肯定了前台进程组ID),在做业控制的时候,后台进程组写终端的时候会产生SIGTTOU信号,而终端产生的信号应该送往前台进程组,setpgid是设置进程组ID,这样就能够把那些做业设置成前台进程组,完美的解决了!!!!这是在有做业控制的时候的一些过程

4:shell执行程序

在没有做业控制的shell上执行

则不论是前台进程仍是后台进程,进程组ID都是不变的,下面是后台进程组,发现并无变化,进程组ID

若是,在后台进程组中试图读控制终端(再有做业控制的时候,会产生开心好sigtin),在没有做业控制的时候,没有重定向标准输入,则将标准输入重定向到dev/null

若是经过打开/dev/tty的时候,则状况发生改变,好比说

crypt < salaries | lpr&

这个命令,crypt是经过打开tty进行的读写,这样子是无效的

这张图解释了为何会出现这种状况,如上图,由于当执行ps的时候,其实cat1,cat2并无exec好 因此都是sh

5孤儿进程组

问题的提出:考虑一个进程,当他fork了一个子进程的时候,子进程中止,而后父进程终止,子进程应该如何继续,子进程是否知道了他目前是一个孤儿进程?

5.1定义:该组中的每一个成员要么是该组的一个成员,要么不是该组所属会话的成员

5.2解释如图所示的程序编程了孤儿进程组

当进行fork的时候,首先让父进程进行睡眠,而后等待子进程运行,子进程中止本身,父进程因为睡眠时间已到,因此exit进行退出,此时父进程退出发出sighup信号,子进程收到以后进行pr_ids信息打印,此时因为父进程已死,因此子进程的父进程编程init,此时子进程所属的进程组变成一个孤儿进程组,因为向标准输入读一个数据,此时本进程变成了一个后台进程,后台进程在进程组是孤儿进程租的时候读标准输入,此时read应该返回一个errno,而且进行打印



相关文章
相关标签/搜索