unix进程间通信_UNIX进程

在最近的一次街头交易会上,我被单人乐队迷住了。 是的,我很容易被逗乐,但仍然给我留下了深刻的印象。 真正的独奏交响曲结合了口琴,班卓琴,和a鼓-分别在嘴,膝,膝盖和脚上–真正的独奏交响乐使齐柏林飞船(Led Zeppelin)的经典作品《通往天堂的阶梯》表现出色,并生动诠释了贝多芬的第五交响曲。 相比之下,我很幸运能拍拍头并串联揉肚子。 (或者是拍拍我的肚子然后擦我的头?)

幸运的是,UNIX®操作系统比笨拙的专栏作家更像单人乐队。 UNIX在一次兼顾许多任务的同时,还特别协调了对系统有限资源(内存,设备和CPU)的访问。 换而言之,UNIX可以轻松地同时走路和嚼口香糖。

本月,让我们比平常更深入地研究一下UNIX如何同时完成许多工作。 在摸索过程中,我们还瞥了一眼外壳的内部结构,以了解如何实现作业控制命令,例如Control-C(终止)和Control-Z(挂起)。 大灯亮! 到蝙蝠洞!

真正的多任务处理程序

在UNIX(以及大多数现代操作系统,包括Microsoft®Windows®,Mac OS X,FreeBSD和Linux®)上,每个计算任务都由一个进程表示。 UNIX似乎在同一时间运行许多任务,因为每个进程以(概念上)循环方式获得一小部分CPU时间。

流程就是一个容器,它捆绑了一个正在运行的应用程序,其环境变量,应用程序输入和输出的状态以及流程的状态,包括其优先级和累积的资源使用情况。 图1描绘了一个过程。

图1. UNIX流程的概念模型
UNIX进程的表示

如果有帮助,您可以将流程视为具有边界,资源和国内生产总值的自己的主权国家。

每个过程也都有一个所有者 。 您启动的任务(例如,shell和命令)通常归您所有。 系统服务可能由特殊用户或超级用户root拥有。 例如,为了增强安全性,Apache HTTP Server通常由名为www的专用用户拥有,该用户可以访问Web服务器所需的文件,但不能访问其他文件。

进程的所有权可能会更改,但在其他方面严格排他。 一个过程在任何给定时间只能有一个所有者。

最后(简化本介绍),每个进程都具有特权 。 通常,进程的特权与其所有者的特权相对应。 (例如,如果您无法从命令行外壳程序访问特定文件,则从外壳程序启动的程序会继承相同的限制。)此继承规则的例外,在该例外情况下,进程可能会获得比其所有者更大的特权,是启用了特殊setuid或setgid位的应用程序,如ls所示。

可以使用chmod u+s设置setuid位。 setuid权限如下所示:

$ ls -l /usr/bin/top
-rwsr-xr-x     1 root  wheel     83088 Mar 20  2005 top

可以使用chmod g+s设置setgid位:

$ ls -l /usr/bin/top
-r-xr-sr-x   1 root  tty  19388 Mar 20  2005 /usr/bin/wall

setuid进程(例如launching top )将以拥有该文件的用户的特权运行。 因此,当您运行top时,您的特权将提升为root的特权。 同样,setgid进程将以与文件的组所有者关联的特权运行。

例如,在Mac OS X上, wall实用程序(即“全部写入”的缩写,因为它会将消息写入每个物理或虚拟终端设备)是setgid tty (如上所示)。 登录并分配了要键入的终端设备后(终端成为Shell的标准输入),您将成为设备的所有者,而tty成为组所有者。 由于wall以tty组的特权运行,因此它可以打开并写入每个终端。

盘点

与所有其他系统资源一样,您的UNIX系统具有有限的进程池。 (实际上,系统几乎永远不会耗尽进程。)每个新任务(例如启动vi或运行xclock)都会立即从池中分配一个进程。 在UNIX系统上,可以使用ps命令查看一个或多个进程。

例如,如果要查看自己拥有的所有进程,请输入ps -w --user username

$ ps -w --user mstreicher

您可以使用ps -a -w -x查看整个进程列表。 ( ps命令的格式和特定​​标志因UNIX风格而异,请参见UNIX风格。有关详细信息,请参见系统的联机文档。) -a选择在tty设备上运行的所有进程; -x进一步选择与tty无关的所有进程,这些进程通常包括所有永久性系统服务,例如Apache HTTP服务器, cron作业调度程序等; -w显示宽格式,对于查看与每个进程关联的应用程序的命令行或完整路径名很有用。

ps具有大量功能,有些ps版本甚至允许您自定义输出。 例如,这是一个有用的自定义过程清单:

$ ps --user mstreicher -o pid,uname,command,state,stime,time 
  PID USER     COMMAND          S STIME     TIME
14138 mstreic  sshd: mstreicher S 09:57 00:00:00
14139 mstreic  -bash            S 09:57 00:00:00
14937 mstreic  ps --user mstrei R 10:23 00:00:00

-o根据命名列的顺序格式化输出。 pidunamecommand分别是进程ID,用户名和命令。 state反映了进程状态,例如正在Hibernate( S )或正在运行( R )。 (稍后将详细介绍进程状态。) stime显示命令何时启动,而time显示进程已消耗多少CPU时间。

爸爸,流程从何而来?

在UNIX上,某些进程从系统启动到关闭运行,但是随着任务的开始和完成,大多数进程来来往往。 有时,一个过程可能会过早地死亡,甚至导致可怕的死亡(例如由于崩溃)。 新流程从何而来?

每个新的UNIX进程都是现有进程的衍生 。 此外,每个新进程(我们称其为“子”进程)都是其“父”进程的克隆,至少是暂时的,直到子进程继续独立执行。 (如果每个新流程都是现有流程的后代,那将产生如下困惑:“第一个流程来自何处?”请参见下面的边栏以获取答案。)

图1-4详细说明了生成过程,嗯:

  1. 在图23中 ,进程A正在运行一个由蓝色框表示的程序。 它运行编号为10、11、12等的指令。 进程A具有自己的数据,程序的副本,打开的文件集以及环境变量的集合,它们最初是在进程A出现时捕获的。
    图2.流程A运行代码
    在流程中运行代码
  2. 在UNIX中,使用fork()系统调用(之所以命名是因为它是对操作系统帮助的调用或请求),用于产生新进程。 当程序A在指令13中执行fork()时,系统会立即创建一个精确的进程A的克隆,名为进程Z。Z具有与A相同的环境变量,相同的内存内容,相同的程序状态以及相同的文件打开。 紧接在进程A生成进程Z之后的进程A和Z的状态如图3所示。
    图3.进程A产生其自身的克隆
    产卵过程的表示
  3. 在开始时,进程Z在进程A停止的同一位置开始执行。 也就是说,在开始之后,进程Z从指令14开始执行。进程A在同一条指令处继续执行。
  4. 通常,指令14中的编程逻辑会测试当前进程是子进程还是父进程-也就是说,进程Z中的指令14和进程A中的指令14分别确定其进程是后代还是后代。 为了进行区分, fork()系统调用在后代中返回0,但将进程Z的进程ID返回到后代。
  5. 经过之前的测试,流程A和流程Z分开,分别采用单独的代码路径,就好像它们都走在了道路上,并且各自采用了不同的分支。 考虑到两个旅行者在路上叉的隐喻, 催生新过程的过程通常称为分叉 。 因此,系统调用名为fork()

派生之后,进程A可能会继续运行相同的应用程序。 但是,进程Z可能会立即选择转换为另一个应用程序。 更改进程中正在运行的程序的后一种操​​作称为执行 ,但您可以将其视为轮回:尽管进程ID保持不变,但进程中的指令完全被新程序中的指令代替。 图4显示了一段时间后进程Z的状态。

图4.流程Z现在独立于其祖先流程A
产卵过程的表示

分叉

您可以从自己的专用命令行舒适地体验分叉。 首先,打开一个新的xterm 。 (您现在可能已经意识到xterm是它自己的进程,并且在xterm中,shell是xterm产生的单独的进程)。 接下来,键入:

ps  -o pid,ppid,uname,command,state,stime,time

您应该会看到以下内容:

PID  PPID USER     COMMAND          S STIME     TIME
16351 16350 mstreic  -bash            S 11:23 00:00:00
16364 16351 mstreic  ps -o pid,ppid,u R 11:24 00:00:00

根据此列表中的PPID字段, ps命令是bash shell的子级。 ( -bash的连字符表示该shell实例是登录shell。)要运行ps ,bash会创建一个新进程; 新进程将通过执行重新进行自我转换,从而成为ps的新实例。

这是另一个尝试的实验。 类型:

sleep 10 & sleep 10 & sleep 10 & ps  -o pid,ppid,uname,command,state,stime,time

您应该会看到以下内容:

$ sleep 10 & sleep 10 & sleep 10 & ps  -o pid,ppid,uname,command,state,stime,time
  PID  PPID USER     COMMAND          S STIME     TIME
16351 16350 mstreic  -bash            S 11:23 00:00:00
16843 16351 mstreic  sleep 10         S 11:42 00:00:00
16844 16351 mstreic  sleep 10         S 11:42 00:00:00
16845 16351 mstreic  sleep 10         S 11:42 00:00:00
16846 16351 mstreic  ps -o pid,ppid,u R 11:42 00:00:00

命令行产生了四个新进程。 打字符号( & )每个后sleep命令运行每个这些命令的背景下 ,或在与外壳平行。 ps是另一个生成的进程,但是它在前台运行,从而阻止Shell在终止之前运行另一个命令。 同样,所有四个过程都是外壳的生成,如PPID的值所示。 这三个sleep命令标记为S ,因为它们在睡眠时没有一个进程在消耗资源。

为了方便起见,shell会跟踪其产生的所有后台进程。 输入jobs以查看列表:

$ sleep 10 & sleep 10 & sleep 10 & 
[1] 16843
[2] 16844
[3] 16845

$ jobs
[1]   Running                 sleep 10 &
[2]   Running                 sleep 10 &
[3]   Running                 sleep 10 &

为了方便起见,这里将三个作业标记为1、2和3。 数字16843、16844和16845是每个相应进程的进程ID。 因此,后台任务1是进程ID 16843。

您可以使用这些标签从命令行操作后台作业。 例如,要终止命令,请键入kill % N ,其中N是命令的标签。 要将命令从后台移动到前景,请输入fg % N

$ sleep 10 & sleep 10 & sleep 10 &
[7] 17741
[8] 17742
[9] 17743

$ kill %7
$ jobs
[7]   Terminated              sleep 10
[8]-  Running                 sleep 10 &
[9]+  Running                 sleep 10 &

$ fg %8
sleep 10

从命令行同时和异步运行多个命令是处理自己的任务集的好方法。 一项长期运行的工作(例如,数字运算或大型汇编)非常适合放在后台。 要捕获每个后台命令的输出,请考虑使用重定向运算符>>&>>>>&将输出重定向到文件。 每当后台命令完成时,shell都会在下一个提示之前打印警报消息:

$ whoami
mstreicher
[8]-  Done                    sleep 10
[9]+  Done                    sleep 10
$

通往天空中的巨大Craft.io池

有些进程可以永久存在(例如init),而有些进程可以将它们自己化身为新的形式(例如您的shell)。 最终,大多数流程都死于自然原因-一个程序运行完成。

此外,您可以将流程放置在一种悬浮的动画中,等待重新进行动画处理。 如前面的示例所示,您可以使用kill提前终止进程。

如果命令在前台运行,并且您想挂起它,请按Control-Z

$ sleep 10
(Press Control-Z)
[1]+  Stopped                 sleep 10

$ ps
  PID  PPID USER     COMMAND          S STIME     TIME
18195 16351 mstreic  sleep 10         T 12:44 00:00:00

为了方便起见,shell暂停了该命令并为其分配了标签。 您可以像以前一样使用此标签来终止作业或将其返回到前台。 您还可以使用bg命令在后台恢复该过程:

bg %1
[1]+ sleep 10 &

如果命令在前台运行,并且您想终止它,请按Control-C

$ sleep 10
(Press Control-C
$ jobs
$

您的外壳使挂起和终止进程变得容易,但是外壳的无辜底面下有一些伏都教徒在工作。 在内部,您的Shell使用UNIX 信号来影响进程的状态。 信号是一个事件,用于警告过程。 操作系统产生许多信号,但是您可以将信号从一个进程发送到另一个进程,甚至可以发出一个进程信号。

UNIX包含各种信号,其中大多数具有特殊目的。 例如,如果您将信号SIGSTOP发送到进程,则该进程将挂起。 (有关信号的完整列表,请键入man 7 signal或键入kill -L )。 您可以使用kill命令发送信号:

$ sleep 20 &
[1] 19988

$ kill -SIGSTOP 19988

$ jobs
[1]+  Stopped                 sleep 20

最初, sleep命令在后台启动,进程ID为19988。发送SIGSTOP ,进程更改状态,变为挂起或停止。 发送另一个信号SIGCONT可使该过程重新生效,并在中断的地方继续进行。

换句话说,每次按Control-Z时,shell都会将SIGSTOP发送到前台进程。 bg命令发送SIGCONT 。 然后Control-C发送SIGTERM ,它要求进程立即终止。

某些信号可能会被进程阻止,并且可以将应用程序设计为显式“捕获”信号并以特殊方式对每个事件做出React。 例如,按需启动其他网络服务的系统服务xinetd在收到SIGHUP后重新读取其配置文件。 在Linux上,发送信号到init可以更改系统运行级别,甚至可以启动系统关闭。 (这是一个问题: kill %1kill 1什么区别?)

一个进程甚至可以发出信号。 想象一下,您在编写游戏,想给用户五秒钟的响应时间。 您的代码可以设置一个五秒钟的计时器,然后继续,例如重绘屏幕。 当计时器用尽时, SIGALRM将发送回您的进程。 zz! 时间到!

(这是问题的答案: kill %1杀死标记为1的后台作业。kill kill 1终止init,这是向操作系统发出的信号,它应该关闭整个计算机。)

在特殊情况下,还有其他信号从操作系统传输到进程。 内存冲突会刺激SIGSEGV ,立即终止进程,同时留下核心转储。 不能阻止或捕获一个特殊信号SIGKILL ,它会立即杀死一个进程。

与UNIX中的许多其他资源一样,您只能用信号通知您拥有的进程。 这样可以防止您终止重要的系统服务和其他用户的进程。 超级用户root可以发出任何进程信号。

更加神秘的魔法

UNIX有许多活动的组成部分。 它具有系统服务,设备,内存管理器等。 幸运的是,这些复杂的设计大多数都不会被看到,或者可以通过用户界面(例如外壳和窗口工具)方便使用。 更好的是,如果您想ps ,可以随时使用专用工具,例如toppskill

现在您知道流程的工作原理,您可以成为自己的一人乐队。 只需一个请求: Freebird!


翻译自: https://www.ibm.com/developerworks/aix/library/au-speakingunix8/index.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值