Process Control

进程控制

  • pid
  • fork
  • wait
  • exec

pid

每一个进程都有一个独特的非负整型pid

pid可以用来生成独特的文件名

进程终止时 pid会被回收重用

大部分unix系统会有代码来延迟重用从而保证新创建的进程和刚刚终止的进程pid相同

pid 1 通常为init process, 会在启动结束时被kernel invoke 其program file在/etc/init (老版本的UNIX系统)或者/sbin/init (较新版本。 这个进程负责bring up unix system after kernel has been bootstrapped by reading system-dependent initialization files: the /etc/rc* files or /etc/inittab and the files in /etc/init.d. 来带系统进入一定的状态:比如多用户状态。

init process is a user process (with superuser privileges) that never dies.

例子: mac os X 10.4里的launchd process

通常每个unix系统有自己的内核进程, 有的系统pid2时pagedaemon 负责支持系统虚拟内存paging

以下所有函数都没有error return

fork

现存进程可以用fork来创建新进程

这个函数被call一次会返回两次

在子进程中返回值为0 原因:子进程只能有一个父进程 可以getppid来获得

parent process返回值为子进程pid 原因:避免子进程pid被其他进程获

子进程父进程在fork之后会继续执行接下来的命令

The child is a copy of the parent. For example, the child gets a copy of the parent’s data space, heap, and stack. 但是会share text segment

Modern implementations don’t perform a complete copy of the parent’s data, stack, and heap, since a fork is often followed by an exec. Instead, a technique called copy-on-write (COW) is used. These regions are shared by the parent and the child and have their protection changed by the kernel to read-only. If either process tries to modify these regions, the kernel then makes a copy of that piece of memory only, typically a ‘‘page’’ in a virtual memory system.

通常父子进程执行顺序是nondeterministic的 这取决与kernel的调度算法

若需要sychronize action 可以使用如上代码中的sleep来使父进程等待子进程的执行, 但我们没有保证2秒的时间是合适的

其他同步方式 race conditions section 8.9 signal section 10.16

sizeof 和 strlen的 区别:

When we write to standard output, we subtract 1 from the size of buf to avoid writing the terminating null byte. Although strlen will calculate the length of a string not including the terminating null byte, sizeof calculates the size of the buffer, which does include the terminating null byte.

Another difference is that using strlen requires a function call, whereas sizeof calculates the buffer length at compile time,(更快) as the buffer is initialized with a known string and its size is fixed.

为什么打印了两遍?write是没有缓冲的。 stdio库是有缓冲的:stdout如果连接着terminal device 会被line buffered 否则会被fully buffered

所以刚开始的printf缓存区被清空了 但当我们重定向输出到文件的时候 printf打印了两行。 在第二个情况下 printf在fork前被呼叫了1次,但在呼叫fork后buffer里缓存还在,buffer里的内容被拷贝到子进程 此时父子进程都有这个相同的 内容没被清空的buffer。 最后一个printf appends its data to the existing buffer.进程终止时 缓存区清空.

### file shareing

当我们重定向父进程的stdout输出的时候 子进程stdout也被重定向

这是fork的一个特性: all file descriptors that are open in the parent are duplicated in the child. The parent and the child share a file table entry for every open descriptor

也就是说 父子进程同时进行输出到stdout时 一定要共享file offset. 若父进程输出被重定向,我们需要子进程在输出到stdout时更新父进程file offset。 这样不仅子进程可以在父进程wait时候输出到stdout,在子进程结束输出时父进程可以在同位置继续之前的输出。 如果没有共享file offset 这种互动会很难实现

通常有两种情况来处理 fork之后的descriptors的使用

  1. 父进程等待子进程结束:parent need to do nothing. 任何共享的descriptors会被子进程更新
  2. 父进程子进程都有自己的事情要干:fork之后父子进程各自关闭其不需要的descriptors,open descriptors互不干涉 这种情况常见于网络服务器

其他被子进程继承的属性:

  • Real user ID, real group ID, effective user ID, and effective group ID
  • Supplementary group IDs
  • Process group ID
  • Session ID
  • Controlling terminal
  • The set-user-ID and set-group-ID flags
  • Current working directory
  • Root directory
  • File mode creation mask
  • Signal mask and dispositions
  • The close-on-exec flag for any open file descriptors
  • Environment
  • Attached shared memory segments
  • Memory mappings
  • Resource limits

父子进程的区别:

  • fork返回值
  • pid
  • ppid
  • 子进程的tms_utime, tms_stime, tms_cutime, and tms_cstime值为0
  • 父进程的文件锁不会被子进程继承
  • Pending alarms are cleared for the child.
  • The set of pending signals for the child is set to the empty set.

fork通常失败的原因有 太多进程存在于系统或者 对于当前用户进程数量超过系统限制: CHILD_MAX specifies the maximum number of simultaneous processes per real user ID.

fork 的两种使用

  • 当一个进程想要自我复制从而 父子进程同时执行一片相同的代码时:常见于网络服务器 the parent waits for a service request from a client. When the request arrives, the parent calls fork and lets the child handle the request. The parent goes back to waiting for the next service request to arrive.
  • 当一个进程想要执行一个不同的程序 这种情况下通常子进程使用exec right after it returns from the fork.(spawn by some other OS)
## wait

When a process terminates, either normally or abnormally, the kernel notifies the parent by sending the SIGCHLD signal to the parent.

Because the termination of a child is an asynchronous event—it can happen at any time while the parent is running—this signal is the asynchronous notification from the kernel to the parent.

The parent can choose to ignore this signal, or it can provide a function that is called when the signal occurs: a signal handler.

The default action for this signal is to be ignored.

For now, we need to be aware that a process that calls wait or waitpid can:

  • Block, if all of its children are still running
  • Return immediately with the termination status of a child, if a child has terminated and is waiting for its termination status to be fetched
  • Return immediately with an error, if it doesn’t have any child processes

If the process is calling wait because it received the SIGCHLD signal, we expect wait to return immediately. But if we call it at any random point in time, it can block.

打赏
  • 版权声明: 本博客所有文章除特别声明外,均采用 Apache License 2.0 许可协议。转载请注明出处!
  • © 2016-2020 th2zz

请我喝杯咖啡吧~

支付宝
微信