过渡章节(更多实际操作)
在本章,我们讨论的是Unix系统的进程创建,unix利用的是一对系统调用:fork()和exec()。当父进程希望等待它创建的子进程完成执行的时候,也会调用到wait()。本章节的重点:
如何创建和控制进程
OS需要什么接口用于创建和控制进程?如何设计这些接口使其具备强大的功能和性能并且易于使用?
5.1 fork()
fork()用于创建新的进程[C63],然而需要注意的是,它是你调用的最奇怪的程序之一。以下面的程序为例:
运行上述程序得到输出:
详细分析:程序第6行输出hello world以及程序的进程id(缩写PID) , 然后第7行fork函数被调用,此时OS创建了一个新的进程,奇怪的地方来了:新创建的进程是该进程的复制品。这意味着,对于操作系统来说,现在又两个相同的程序p1在运行,但是他并没有输出子进程的pid,而是先完成了父进程的输出
5.2 wait()
下面,我们在程序中加入wait():
运行的输出为:
这时候我们再看,子进程并非是完全的父进程的copy,fork的返回值不同,父进程的返回值是大于0 的值,子进程是0.
我们注意到,该程序(p1.c)的输出并非是确定值,假设我们只有一个cpu,在我们的例子。首先父进程执行了,然后跳到了子进程,但是,相反的状况也可能存在。我们之后会重点讨论这个问题,也就是CPU调度。使用wait()函数可以使执行的顺序得以确定.
5.3 exec()
当想要运行与调用的程序不同的程序的时候,exec()至关重要。
注:strdup()是字符串复制函数,返回新配制字符串的地址;wc是计数字母程序
执行结果为:
上例中,execvp中传入一个可执行程序的名称(wc),然后运行输出p3.c的行数(第二个参数传入第一个可执行函数)、单词数和字母数。程序的堆、栈以及其他的内存空间被重新初始化。这意味着,他没有创建新的进程,而是将现在正在运行的程序直接转到调用的程序,在exec在子进程中执行之后,仿佛p3.c没有运行过。成功运行的exec不会返回任何值
5.4 why ? Motivating The API
你可能会问,为什么创建子进程这么简单,我们还需要exec函数呢?然而,在unix shell中,fork和exec的不同之处至关重要,因为这能让shell在fork之后、exec之前运行代码。这样能实现切换即将运行的程序的环境,可以构建更多的特性。
shell也是一个用户程序,他给你提供了一个提示接口,然后等待命令输入。通常情况下,输入一个命令后,shell会首先弄清楚命令所在文件的位置,然后fork创建一个子进程来运行该命令,调用exec来执行命令,然后通过wait来等待该程序运行完成。当子进程完成后,shell从wait中返回,然后重新输出提示符,等待下一个命令。
例如:
prompt> wc p3.c > newfile.txt
上述命令中,wc的输出重定向到输出文件newfile.txt中,shell的执行过程:子进程创建完毕后,在调用exec之前,shell关闭了标准输出,然后打开newfile.txt,这样即将运行的wc程序的所有的输出都会被定向到文件中,而不是输出屏幕。
图5.4是显示了重定向的例子,Unix系统从0开始找可用的文件描述,在下例中 STDOUT FILENO是第一个可用的,所以调用open函数的时候,首先被分配。子进程后续所创造的标准输出,会被导向到新打开的文件,而不是屏幕。
上述程序的输出为:
有两个值得注意的点:1.)p4运行的是偶仿佛没有任何事情发生,shell马上给出了下个指令的prompt。但是事实上,p4调用了fork创建了子进程,然后将wc程序传入exec运行,并且输出全部被重定向到p4.output里面了。2)我们用cat指令可以打开输出文件找到输出内容。
Unix的pipes也是类似的方式,但是调用了系统函数pipe(),在这种情况下,进程的输出被连接到内核管道,另一个进程的输入也连接到了同一个管道,因此,实现了直接对接,建立起了又长又实用的命令链。例如,在一个文件中,查找一个单词,然后数出现了多少次,可以使用pipe、grep、wc实现,输入指令:
grep -o foo file | wc -l
就可以实现。
我们揭开了process api的面纱,但是还有很多调用细节还需要进一步学习。目前来说,fork和exec的组合是一个创建和操控进程有力的工具。
5.5 进程控制和用户
除了上述接口,Unix系统还有很多的调用接口,比如说kill用于进程的暂停、死亡以及其他有用的命令。为了便于使用,也会有快捷键,例如ctrl+c用于终止进程,ctrl+z用于暂停,后续可以使用fg 等内置命令进行恢复。
tips 善于使用manual pages,man 指令能给你几乎所需的所有信息
进程间的通信,利用信号量实现,进程应该调用siganal()来获取不同的信号。这样做能让进程在运行的时候,如果收到了特殊的信号可以挂起。【SR05】中有关于信号量的详细介绍。
这也就引入了一个问题,谁可以发送信号量?通常来说,我们的系统有多个用户在同时使用,如果其中一个用户可以任意发送信号量(例如SIGINT来中断进程,很有可能终止进程),会影响到系统的可用性和安全性能。因此,有密码的用户登录机制被利用。不同的用户有不同的权限,通常用户只有控制自己的进程的权限,操作系统负责将系统的资源调度实现整个系统的运行。
5.6 常见指令和工具
有很多常见的指令,例如 ps 可以查看目前正在运行的进程,top可以显示进程占用的系统资源。通常情况下,我们会保持一些窗口显示,例如,我们始终保持 MenuMeters (from Raging Menace software) r运行在我们的工具栏上,所以我们可以看到有多少CPU正在被利用,一般来说,关于正在发生的事情的信息越多越好。
tips: the superviser root
通常一个系统会有个root用户,可以管理系统,并且操作其他用户的进程。
5.7 summary
我们简单介绍了unix进程处理的一些接口,进一步的了解请阅读 Stevens and Rago [SR05],。当然,上述进程控制、进程间的关系和信号量章节可以提取出很多有用的信息。近期的一篇论文 [B+19]指出了fork()的一些问题,并且推崇了spawn()来创建APIs,所以本文也是带着作者的主观认知的,尽可能多阅读相关文献。
要点总结
每个进程有一个PID
fork()用于创建子进程
wait() 让父进程等待子进程完成
exec类别的函数允许子进程可以做与父进程不同的事情
信号量用于进程间通信。
用户机制指定了进程的控制权限
超级用户可以控制整个系统
5.8 Refs
[B+19] “A fork() in the road” by Andrew Baumann, Jonathan Appavoo, Orran Krieger, Timothy Roscoe. HotOS ’19, Bertinoro, Italy. A fun paper full of fork()ing rage. Read it to get an opposing viewpoint on the UNIX process API. Presented at the always lively HotOS workshop, where systems researchers go to present extreme opinions in the hopes of pushing the community in new directions.
[C63] “A Multiprocessor System Design” by Melvin E. Conway. AFIPS ’63 Fall Joint Computer Conference, New York, USA 1963. An early paper on how to design multiprocessing systems; may be the first place the term fork() was used in the discussion of spawning new processes.
[DV66] “Programming Semantics for Multiprogrammed Computations” by Jack B. Dennis and Earl C. Van Horn. Communications of the ACM, Volume 9, Number 3, March 1966. A classic paper that outlines the basics of multiprogrammed computer systems. Undoubtedly had great influence on Project MAC, Multics, and eventually UNIX.
[J16] “They could be twins!” by Phoebe Jackson-Edwards. The Daily Mail. March 1, 2016.. This hard-hitting piece of journalism shows a bunch of weirdly similar child/parent photos and is frankly kind of mesmerizing. Go ahead, waste two minutes of your life and check it out. But don’t forget to come back here! This, in a microcosm, is the danger of surfing the web.
[L83] “Hints for Computer Systems Design” by Butler Lampson. ACM Operating Systems Review, Volume 15:5, October 1983. Lampson’s famous hints on how to design computer systems. You should read it at some point in your life, and probably at many points in your life.
[QI15] “With Great Power Comes Great Responsibility” by The Quote Investigator. Available: https://quoteinvestigator.com/2015/07/23/great-power. The quote investigator concludes that the earliest mention of this concept is 1793, in a collection of decrees made at the French National Convention. The specific quote: “Ils doivent envisager qu’une grande responsabilit est la suite insparable d’un grand pouvoir”, which roughly translates to “They must consider that great responsibility follows inseparably from great power.” Only in 1962 did the following words appear in Spider-Man: “...with great power there must also come–great responsibility!” So it looks like the French Revolution gets credit for this one, not Stan Lee. Sorry, Stan.
[SR05] “Advanced Programming in the UNIX Environment” by W. Richard Stevens, Stephen A. Rago. Addison-Wesley, 2005. All nuances and subtleties of using UNIX APIs are found herein. Buy this book! Read it! And most importantly, live it.