操作系统介绍
哈Ha! 我想提请您注意一系列文章-我认为是OSTEP的有趣文献的翻译。 本文相当深入地讨论了类Unix操作系统的工作,即处理组成现代OS的进程,各种调度程序,内存和其他类似组件。 您可以
在这里看到所有材料的原始材料。 请注意,翻译是非专业地进行的(相当自由),但是我希望我保留了一般的含义。
有关此主题的实验室工作可以在这里找到:
其他部分:
您可以通过
电报查看我的频道=)
报警! 这个讲座有一个实验室! 观看
github流程API
考虑在UNIX系统上创建进程的示例。 它通过两个系统调用
fork()和
exec()发生 。
前叉()呼叫

考虑一个进行fork()调用的程序。 其实施结果如下。

首先,我们输入main()函数,并将字符串的输出执行到屏幕上。 该字符串包含过程标识符,其原始名称为
PID或过程标识符。 该标识符在UNIX上用于引用进程。 下一条命令将调用fork()。 此时,将创建该过程的几乎完全相同的副本。 对于OS,看起来好像系统在运行相同程序的2个副本一样,这又将退出fork()函数。 从main()函数开始,将不再执行新创建的子进程(相对于创建它的父进程)。 应当记住,子进程不是父进程的精确副本,尤其是它具有自己的地址空间,自己的寄存器,自己的指向可执行指令的指针等。 因此,返回给fork()函数调用者的值将有所不同。 特别是,父进程将收到子进程的PID值作为返回值,而子进程将收到0值。基于这些返回码,已经可以分离进程并强制它们各自执行其工作。 此外,该程序的执行没有严格定义。 在分为两个过程之后,操作系统也开始遵循它们,并计划它们的工作。 如果在单核处理器上执行,则其中一个进程将继续工作,在这种情况下为父进程,然后子进程将获得控制。 重新启动时,情况可能会有所不同。
通话等待()

考虑下面的程序。 在此程序中,由于存在
wait()调用,因此父进程将始终等待子进程完成其工作。 在这种情况下,我们将严格定义的文本输出到屏幕。

致电exec()

考虑对
exec()的调用。 当我们要运行完全不同的程序时,此系统调用很有用。 在这里,我们将调用
execvp()运行wc程序,这是一个字计数程序。 调用exec()会发生什么? 可执行文件的名称和一些参数作为参数传递给此调用。 之后,将下载此可执行文件中的代码和静态数据,并覆盖其自身的代码段。 其余的内存部分(例如堆栈和堆)将重新初始化。 之后,操作系统仅执行程序,并向其传递一组参数。 因此,我们没有创建新进程,只是将当前正在运行的程序转换为另一个正在运行的程序。 在执行exec()之后,后代给人的印象是原始程序原则上似乎没有启动。
这种启动的复杂性对于Unix shell来说是绝对正常的,并且允许该shell在调用
fork()之后但在调用
exec()之前执行代码。 这样的代码的一个示例可以是在直接启动外壳程序之前,将其环境调整为正在启动的程序的需求。
Shell只是一个用户程序。 她向您显示提示行,并等待您为其写一些内容。 在大多数情况下,如果在此处编写程序的名称,则外壳程序将找到其位置,调用fork()方法,然后创建一个新进程,它将调用某些exec()类型并等待使用wait()调用执行。 当子进程结束时,shell从wait()调用返回并再次显示提示,并等待输入下一个命令。
分隔fork()和exec()允许外壳程序执行以下操作,例如:
wc文件> new_file。在此示例中,wc的输出重定向到文件。 shell实现此目的的方法非常简单-在调用
exec()之前创建子进程时,shell关闭标准输出流并打开
new_file文件,因此启动的
wc程序的所有输出都将重定向到该文件而不是屏幕。
Unix管道以类似的方式实现,不同之处在于它们使用pipe()调用。 在这种情况下,进程的输出流将连接到位于内核中的管道队列,另一个进程的输入流将附加到该队列中。