日期:2014-05-16  浏览次数:20601 次

Linux Kernel 核心中文手册(5)--进程间通信机制

Linux Kernel 核心中文手册(5)--进程间通信机制
2011年01月16日
  Interprocess Communication Mechanisms (进程间通讯机制)
  进程之间互相通讯并和核心通讯,协调它们的行为。 Linux 支持一些进程间通讯(
  IPC )的机制。信号和管道是其中的两种, Linux 还支持系统 V IPC (用首次出现的
  Unix 的版本命名)的机制。
  5.1 Signals (信号)
  信号是 Unix 系统中使用的最古老的进程间通讯的方法之一。用于向一个或多个进
  程发送异步事件的信号。信号可以用键盘终端产生,或者通过一个错误条件产生,比如
  进程试图访问它的虚拟内存中不存在的位置。 Shell 也使用信号向它的子进程发送作业
  控制信号。
  有一些信号有核心产生,另一些可以由系统中其他有权限的进程产生。你可以使用
  kill 命令( kill  l )列出你的系统的信号集,在我的 Linux Intel 系统输出:
  1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL
  5) SIGTRAP 6) SIGIOT 7) SIGBUS SIGFPE
  9) SIGKILL 10) SIGUSR1 11) SIGSEGV 12) SIGUSR2
  13) SIGPIPE 14) SIGALRM 15) SIGTERM 17) SIGCHLD
  18) SIGCONT 19) SIGSTOP 20) SIGTSTP 21) SIGTTIN
  22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ
  26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO
  30) SIGPWR
  在 Alpha AXP Linux 系统上编号不同。进程可以选择忽略产生的大多数信号,有两
  个例外: SIGSTOP (让进程停止执行)和 SIGKILL (让进程退出)不可以忽略,虽然
  进程可以选择它如何处理信号。进程可以阻塞信号,如果它不阻塞信号,它可以选择自
  己处理或者让核心处理。如果核心处理,将会执行该信号的缺省行为。例如,进程接收
  到 SIGFPE (浮点意外)的缺省动作是产生 core 并退出。信号没有固有的优先级,如
  果一个进程同时产生了两个信号,它们会以任意顺序出现在进程中并按任意顺序处理。
  另外,也没有机制可以处理统一种类的多个信号。进程无法知道它接收了 1 还是 42 个
  SIGCONT 信号。
  Linux 用进程的 task_struct 中存放的信息来实现信号机制。支持的信号受限于处
  理器的字长。 32 位字长的处理器可以有 32 中信号,而 64 位的处理器,比如 Alpha
  AXP 可以有多达 64 种信号。当前待处理的信号放在 signal 域, blocked 域放着要
  阻塞的信号掩码。除了 SIGSTOP 和 SIGKILL ,所有的信号都可以被阻塞。如果产生了
  一个被阻塞的信号,它一直保留待处理,直到被解除阻塞。 Linux 也保存每一个进程如
  何处理每一种可能的信号的信息,这些信息放在一个 sigaction 的数据结构数组中,每
  一个进程的 task_struct 都有指针指向对应的数组。这个数组中包括处理这个信号的例
  程的地址,或者包括一个标志,告诉 Linux 该进程是希望忽略这个信号还是让核心处理
  。进程通过执行系统调用改变缺省的信号处理,这些调用改变适当的信号的 sigaction
  和阻塞的掩码。
  并非系统中所有的进程都可以向其他每一个进程发送信号,只有核心和超级用户可
  以。普通进程只可以向拥有相同 uid 和 gid 或者在相同进程组的进程发送信号。通过
  设置 task -- struct 的 signal 中适当的位产生信号。如果进程不阻塞信号,而且
  正在等待但是可以中断(状态是 Interruptible ),那么它的状态被改为 Running 并
  确认它在运行队列,通过这种方式把它唤醒。这样调度程序在系统下次调度的时候会把
  它当作一个运行的候选。如果需要缺省的处理, Linux 可以优化信号的处理。例如如果
  信号 SIGWINCH ( X window 改变焦点)发生而使用缺省的处理程序,则不需要做什么
  事情。
  信号产生的时候不会立刻出现在进程中,它们必须等到进程下次运行。每一次进程
  从系统调用中退出的时候都要检查它的 signal 和 blocked 域,如果有任何没有阻塞的
  信号,就可以发送。这看起来好像非常不可靠,但是系统中的每一个进程都在调用系统
  调用,比如向终端写一个字符的过程中。如果愿意,进程可以选择等待信号,它们挂起
  在 Interruptible 状态,直到有了一个信号。 Linux 信号处理代码检查 sigaction 结
  构中每一个当前未阻塞的信号。
  如果信号处理程序设置为缺省动作,则核心会处理它。 SIGSTOP 信号的缺省处理是
  把当前进程的状态改为 Stopped ,然后运行调度程序,选择一个新的进程来运行。 SI
  GFPE 信号的缺省动作是让当前进程产生 core ( core dump ),让它退出。变通地,
  进程可以指定自己的信号处理程序。这是一个例程,当信号产生的时候调用而且 sigac
  tion 结构包括这个例程的地址。 Linux 必须调用进程的信号处理例程,至于具体如何
  发生是和处理器相关。但是,所有的 CPU 必须处理的是当前进程正运行在核心态,并正
  准备返回到调用核心或系统例程的用户态的进程。解决这个问题的方法是处理该进程的
  堆栈和寄存器。进程程序计数器设为它的信号处理程序的地址,例程的参数加到调用结
  构或者通过寄存器传递。当进程恢复运行的时候显得信号处理程序是正常的调用。
  Linux 是 POSIX 兼容的,所以进程可以指定调用特定的信号处理程序的时候要阻塞
  的信号。这意味着在调用进程的信号处理程序的时候改变 blocked 掩码。信号处理程序
  结束的时候, blocked 掩码必须恢复到它的初始值。因此, Linux 在收到信号的进程
  的堆栈中增加了对于一个整理例程的调用,把 blocked 掩码恢复到初始值。 Linux 也
  优化了这种情况:如果同时几个信号处理例程需要调用的时候,就在它们堆积在一起,
  每次退出一个处理例程的时候就调用下一个,直到最后才调用整理例程。
  5.2 Pipes (管道)
  普通的 Linux shell 都允许重定向。例如:
  $ ls | pr | lpr
  把列出目录文件的命令 ls 的输出通过管道接到 pr 命令的标准输入上进行分页。
  最后, pr 命令的标准输出通过管道连接到 lpr 命令的标准输入上,在缺省打印机上打
  印出结果。管道是单向的字节流,把一个进程的标准输出和另一个进程的标准输入连接
  在一起。没有一个进程意识到这种重定向,和它平常一样工作。是 shell 建立了进程之
  间的临时管道。在 Linux 中,使用指向同一个临时 VFS I 节点(本身指向内存中的一
  个物理页)的两个 file 数据结构来实现管道。图 5.1 显示了每一个 file 数据结构包
  含了不同的文件操作例程的向量表的指针:一个用于写,另一个从管道中读。这掩盖了
  和通用的读写普通文件的系统调用的不同。当写进程向管道中写的时候,字节拷贝到了
  共享的数据页,当从管道中读的时候,字节从共享页中拷贝出来。 Linux 必须同步对于
  管道的访问。必须保证管道的写和读步调一致,它使用锁、等待队列和信号( locks ,
  wait queues and signals )。
  参见 include/linux/inode_fs.h
  当写进程向管道写的