OSTEP阅读笔记:5-CPU api

这里写目录标题

Read with Questions.

  1. 如何创建和控制进程?OS提供哪些接口?
  2. 如何设计这些接口?

fork

作用:创建新的进程。
过程:调用fork创建的子进程是一个副本,不会从main()开始。
子进程不是完全一致的copy,它拥有自己的地址空间、PID、寄存器、PC等,返回给fork()的值是不同的。
不确定性并发:子进程和父进程的进行顺序是不确定的,在多道程序设计中这一点很重要。由CPU调度程序决定进程的运行顺序。

wait

作用:延迟执行。
过程:有时父进程需要等待子进程完成后才执行。可以使用wait来让进程的输出具有确定性。
(注意,有时候wait会在子进程退出之前返回,具体参考xv6手册)
(linux中,有六种exec的变体:execl, execlp, execle, execv, execvp, execvpe)

exec

作用:运行与调用程序不同的程序。
过程:给定可执行文件的名称和一些参数,将从该可执行文件中加载代码(和静态数据),并用它覆盖当前代码段(和当前静态数据);程序的堆和堆栈以及内存空间的其他部分将重新初始化,然后操作系统运行该程序,传入参数作为该进程的argv。因此,exec并不是创建一个新的进程,它是将现有进程转换成另一个不同的运行程序。
成功的exec调用不会返回

Why? Motivating The API

为什么要为创建新进程建造这样奇怪的应用程序接口?
事实证明,fork和exec分离是在建立UNIX shell中的必要部分,因为它允许shell在调用fork之后、调用exec之前运行代码。代码可以改变即将运行的程序的运行环境,从而可以轻松构建各种有趣的功能。比如输入输出重定向,管道等,不用在程序开始运行前改变任何东西。

shell:shell是一个用户程序,向用户显示提示,然后等待输入内容。
用户在其中键入命令(可执行程序名称以及所需参数)。
shell找出可执行文件在文件系统中的位置
调用fork 创建一个子进程来运行该命令。
调用某些exec变体来运行该命令
调用wait 等待完成该命令。
子进程完成时,shell从wait返回并再次打印提示,准备下一条命令。
(shell有很多类型,比如tcsh, bash, zsh)

重定向,UNIX开始的文件描述符是0,标准输出流STDOUT FILENO是第一个可用的,因此在调用open()被赋值,后续由子进程写入标准输出文件符。

UNIX的管道系统以类似的方式实现,使用pipe()系统调用函数。
一个进程的输出连接到内核管道,另一个进程的输入连接到统一管道。因此,一个过程的输出可以无缝地用作下一个过程的输入,并且可以将冗长的命令链串在一起。

Process Control And Users

UNIX系统中其他用于和进程进行交互的接口:kill(),发送信号到进程。包括暂停、撕掉的指令和其他有用的命令。
eg. 快捷键
ctrl-c 发送SIGINT(中断)
ctrl-z 发送SIGTSTP(停止)
系统进行通信时,进程使用signal()调用来捕获各种信号,将特定信号发送给进程时,进程将暂停其正常执行并响应该信号,运行特定的代码段。
用户现代操作系统的概念,用户登陆获取对系统资源的访问权限,智能控制自己的流程;操作系统将资源分配给每个用户(及其进程),以实现总体系统目标。

Useful Tools

ps:查看正在运行的进程
(更多参阅man pages)
top:展示系统进程和CPU、资源占用
kill&killall

superuser(亦称为root)
系统通常需要一个能够管理系统的用户,而不是像大多数用户那样受到限制。这样的用户应该能够杀死任意进程(例如,如果它以某种方式滥用系统),即使该进程不是由该用户启动的。这样的用户还应该能够运行强大的命令,比如shutdown(毫不奇怪,shutdown会关闭系统)。

Summary

Key Process API Terms

name content
PID process name
fork create a new process
wait a parent waits for its child to complete execution
exec a child to break free from its similarity to its parent and execute an entirely new program
UNIX shell use fork(), wait(), exec() to launch user commands
signals cause jobs to stop, continue, terminate
users controll processes, only control their own processes
superuser control all processes

Homework

模拟作业

  1. 运行./fork.py -s 10,预测每一步进程树的样子。使用-c来检查答案。
  2. 模拟器提供fork_percentage,由-f标志控制。值越高,下一个操作越可能是一个fork。通过大量操作将派生百分比从0.1更改为0.9,随着百分比的变化,最终的进程树是什么样子?
  3. 使用-t标志切换输出。给定一组进程树,说出采取了哪些行动?
  4. 一个子进程退出后,它在进程树中的孩子有什么变化?创建一个特例:./fork.py -A a+b, b+c, c+d, c+e, c-. 这个例子中a创建了b,b创建c,c创建d和e,然后c退出。进程树会是什么样子? 如果用-R标志呢?了解有关独自处理孤立过程的更多信息。
  5. -F标志,跳过中间步骤,仅要求填写最终的果成熟。运行./fork.py -F并尝试是否可以通过查看生成的一系列操作来写下最后的树。
  6. 同时使用-t和-F,显示了最终的流程树,随后填写已发生的操作。通过查看树可以确定发生的确切操作吗?什么情况下能指出,什么情况下不能?

编程作业

  • 写一个程序,调用fork,在调用fork前,让主程序接受一个变量x,并设置它的值为100。子进程中这个变量的值是多少?如果子进程和父进程都改变了x的值,变量会怎样?
    OSTEP阅读笔记:5-CPU api子进程中变量值一开始和父进程相同,都是100。
    父子进程中对x的值的改变是独立的,不会影响其他进程内x的值。

  • 写一个程序,打开文件(使用open()),调用fork创建新进程。子进程和父进程都可以访问open返回的文件描述符吗?他们并行(同时)写入文件时会发生什么?

  • 写一个程序,调用fork。子进程输出“hello”,父进程输出“goodbye”。确保子进程总是先输出,要求不在父进程中使用wait
    OSTEP阅读笔记:5-CPU api最初的想法是通过pipe管道,让子进程给父进程一个输入后,父进程再printf,但是实际操作过程中发现没办法靠这个读入实现。
    然后是一个比较暴力的方法,让父进程sleep(1),强行阻塞到子进程进行结束。
    最后比较合适的方法是使用vfork(),直接保证子进程先运行,直到子进程遇到exit后父进程才开始运行。

  • 写一个程序,调用fork,然后调用一些exec的形式来运行程序/bin/ls。尝试所有Linux上exec的变体,为什么同样的基本调用会有这么多的变体?

  • 写一个程序,在父进程中使用wait来等待子进程结束,wait返回什么?如果你在子进程中使用wait会发生什么?
    OSTEP阅读笔记:5-CPU api子进程没有子进程,直接返回-1,父进程返回的是子进程的pid。

  • 对前面的程序稍作修改,使用waitpid而不是wait,什么时候waitpid有用?

  • 写一个程序,创建子进程,然后在子进程中关闭标准输出流(STDOUT_FILENO),如果子进程在关闭描述符后调用printf()来打印一些输出会发生什么?
    OSTEP阅读笔记:5-CPU api关闭标准输出流后子进程不会产生输出。

  • 写一个程序,创建两个子进程,然后用一个子进程的标准输出连接到另一个子进程的标准输入。使用pipe()来完成这个程序。

上一篇:Linux下的进程概论与编程二(进程控制)


下一篇:【linux系统】unix编程之进程(基础版)