📄 00000006.htm
字号:
<HTML><HEAD> <TITLE>BBS水木清华站∶精华区</TITLE></HEAD><BODY><CENTER><H1>BBS水木清华站∶精华区</H1></CENTER>发信人: coolzhang (coolzhang), 信区: Linux <BR>标 题: linux bible 第五章 进程间通讯机制 <BR>发信站: BBS 水木清华站 (Mon Oct 25 17:48:53 1999) <BR> <BR>发信人: coolzhang (coolzhang), 信区: UNIX <BR>标 题: linux bible 第五章 进程间通讯机制 <BR>发信站: 武汉白云黄鹤站 (Mon Oct 25 15:02:50 1999), 站内信件 <BR> <BR>第五章 进程间通讯机制 <BR> <BR> <BR>进程在核心的协调下进行相互间的通讯。Linux支持大量进程间通讯(IPC)机制。 <BR>除了信号和管道外,Linux还支持Unix系统V中的IPC机制。 <BR> <BR>5.1 信号 <BR> <BR>信号是Unix系统中的最古老的进程间通讯方式。它们用来向一个或多个进程发 <BR>送异步事件信号。信号可以从键盘中断中产生,另外进程对虚拟内存的非法存 <BR>取等系统错误环境下也会有信号产生。信号还被shell程序用来向其子进程发送 <BR>任务控制命令。 <BR> <BR>系统中有一组被详细定义的信号类型,这些信号可以由核心或者系统中其它具有 <BR>适当权限的进程产生。使用kill命令(kill -l)可以列出系统中所有已经定义的 <BR>信号。在我的系统(Intel系统)上运行结果如下: <BR> <BR> 1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL <BR> 5) SIGTRAP 6) SIGIOT 7) SIGBUS 8) SIGFPE <BR> 9) SIGKILL 10) SIGUSR1 11) SIGSEGV 12) SIGUSR2 <BR>13) SIGPIPE 14) SIGALRM 15) SIGTERM 17) SIGCHLD <BR>18) SIGCONT 19) SIGSTOP 20) SIGTSTP 21) SIGTTIN <BR>22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ <BR>26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO <BR>30) SIGPWR <BR> <BR>当我在Alpha AXP中运行此命令时,得到了不同的信号个数。除了两 <BR>个信号外,进程可以忽略这些信号中的绝大部分。其一是引起进程终 <BR>止执行的SIGSTOP信号,另一个是引起进程退出的SIGKILL信号。至于其 <BR>它信号,进程可以选择处理它们的具体方式。进程可以阻塞信号,如若 <BR>不阻塞,则可以在自行处理此信号和将其转交核心处理之间作出选择。 <BR>如果由核心来处理此信号,它将使用对应此信号的缺省处理方法。 <BR>比如当进程接收到SIGFPE(浮点数异常)时,核心的缺省操作是引起core <BR>dump和进程的退出。信号没有固有的相对优先级。如果在同一时刻对于 <BR>一个进程产生了两个信号,则它们将可能以任意顺序到达进程并进行处 <BR>理。同时Linux并不提供处理多个相同类型信号的方式。即进程无法区分 <BR>它是收到了1个还是42个SIGCONT信号。 <BR> <BR>Linux通过存储在进程task_struct中的信息来实现信号。信号个数受到处 <BR>理器字长的限制。32位字长的处理器最多可以有32个信号而64位处理器如 <BR>Alpha AXP可以有最多64个信号。当前未处理的信号保存在signal域中, <BR>并带有保存在blocked中的被阻塞信号的屏蔽码。除了SIGSTOP和SIGKILL <BR>外,所有的信号都能被阻塞。当产生可阻塞信号时,此信号可以保持一直 <BR>处于待处理状态直到阻塞释放。Linux保存着每个进程处理每个可能信号的 <BR>信息,它们保存在每个进程task_struct中的sigaction数组中。这些信息 <BR>包括进程希望处理的信号所对应的过程地址,或者指示是忽略信号还是由 <BR>核心来处理它的标记。通过系统调用,进程可以修改缺省的信号处理过程, <BR>这将改变某个信号的sigaction以及阻塞屏蔽码。 <BR> <BR>并不是系统中每个进程都可以向所有其它进程发送信号:只有核心和超级 <BR>用户具有此权限。普通进程只能向具有相同uid和gid的进程或者在同一进 <BR>程组中的进程发送信号。信号是通过设置task_struct结构中signal域里的 <BR>某一位来产生的。如果进程没有阻塞信号并且处于可中断的等待状态,则 <BR>可以将其状态改成Running,同时如确认进程还处在运行队列中,就可以通 <BR>过信号唤醒它。这样系统下次发生调度时,调度管理器将选择它运行。如 <BR>果进程需要缺省的信号处理过程,则Linux可以优化对此信号的处理。例如 <BR>SIGWINCH(X窗口的焦点改变)信号,其缺省处理过程是什么也不做。 <BR> <BR>信号并非一产生就立刻交给进程,而是必须等待到进程再次运行时才交给 <BR>进程。每次进程从系统调用中退出前,它都会检查signal和blocked域,看 <BR>是否有可以立刻发送的非阻塞信号。这看起来非常不可靠,但是系统中每个 <BR>进程都在不停地进行系统调用,如向终端输出字符。当然进程可以选择去等 <BR>待信号,此时进程将一直处于可中断状态直到信号出现。对当前不可阻塞信 <BR>号的处理代码放置在sigaction结构中。 <BR> <BR>如果信号的处理过程被设置成缺省则由核心来应付它。SIGSTOP信号的缺省 <BR>处理过程是将当前进程的状态改变成为Stopped并运行调度管理器以选择一 <BR>个新进程继续运行。SIGFPE的缺省处理过程则是引起core dump并使进程退 <BR>出。当然,进程可以定义其自身的信号处理过程。一旦信号产生,这个过程 <BR>就将被调用。它的地址存储在sigaction结构中。核心必须调用进程的信号 <BR>处理例程,具体如何去做依赖于处理器类型,但是所有的CPU必须处理这个 <BR>问题:如果信号产生时,当前进程正在核心模式下运行并且马上要返回调用 <BR>核心或者系统例程的进程,而该进程处在用户模式下。解决这个问题需要操 <BR>纵进程的堆栈及寄存器。进程的程序计数器被设置成其信号处理过程的地址, <BR>而参数通过调用框架或者寄存器传递到处理例程中。当进程继续执行时,信 <BR>号处理例程好象普通的函数调用一样。 <BR> <BR>Linux是POSIX兼容的,所以当某个特定信号处理例程被调用时,进程可以设 <BR>定哪个信号可以阻塞。这意味着可以在进程信号处理过程中改变blocked屏蔽 <BR>码。当信号处理例程结束时,此blocked屏蔽码必须设置成原有值。 <BR>因此,Linux添加了一个过程调用来进行整理工作,通过它来重新设置被发送 <BR>信号进程调用栈中的原有blocked屏蔽码。 <BR>对于同一时刻几个信号处理过程,Linux通过堆栈方式来优化其使用,每当一 <BR>个处理过程退出时,下一个处理过程必须等到整理例程结束后才执行。 <BR> <BR>5.2 管道 <BR> <BR>一般的Linux shell程序都允许重定向。如 <BR> <BR> <BR>$ ls | pr | lpr <BR> <BR> <BR>在这个管道应用中,ls列当前目录的输出被作为标准输入送到pr程序中,而 <BR>pr的输出又被作为标准输入送到lpr程序中。管道是单向的字节流,它将某个 <BR>进程的标准输出连接到另外进程的标准输入。但是使用管道的进程都不会意 <BR>识到重定向的存在,并且其执行结果也不会有什么不同。shell程序负责在进 <BR>程间建立临时的管道。 <BR> <BR> <BR>在Linux中,管道是通过指向同一个临时VFS inode的两个file数据结构来实现 <BR>的,此VFS inode指向内存中的一个物理页面。图5.1中每个file数据结构指 <BR>向不同的文件操作例程向量,一个是实现对管道的写,另一个从管道中读。 <BR> <BR>这样就隐藏了读写管道和读写普通的文件时系统调用的差别。当写入进程对 <BR>管道写时,字节被拷贝到共享数据页面中,当读取进程从管道中读时,字节 <BR>从共享数据页面中拷贝出来。Linux必须同步对管道的访问。它必须保证读者 <BR>和写者以确定的步骤执行,为此需要使用锁、等待队列和信号等同步机制。 <BR> <BR>当写者想对管道写入时,它使用标准的写库函数。表示打开文件和打开管道的 <BR>描叙符用来对进程的file数据结构集合进行索引。Linux系统调用使用由管道 <BR>file数据结构指向的write过程。这个write过程用保存在表示管道的VFS inode <BR>中的信息来管理写请求。 <BR> <BR>如果没有足够的空间容纳对所有写入管道的数据,只要管道没有被读者加锁。 <BR>则Linux为写者加锁,并把从写入进程地址空间中写入的字节拷贝到共享数据 <BR>页面中去。如果管道被读者加锁或者没有足够空间存储数据,当前进程将在管 <BR>道inode的等待队列中睡眠,同时调度管理器开始执行以选择其它进程来执行。 <BR>如果写入进程是可中断的,则当有足够的空间或者管道被解锁时,它将被读者 <BR>唤醒。当数据被写入时,管道的VFSinode被解锁,同时任何在此inode的等待队 <BR>列上睡眠的读者进程都将被唤醒。 <BR> <BR>从管道中读出数据的过程和写入类似。 <BR> <BR>进程允许进行非阻塞读(这依赖于它们打开文件或者管道的方式),此时如果 <BR>没有数据可读或者管道被加锁,则返回错误信息表明进程可以继续执行。阻塞 <BR>方式则使读者进程在管道inode的等待队列上睡眠直到写者进程结束。当两个进 <BR>程对管道的使用结束时,管道inode和共享数据页面将同时被遗弃。 <BR> <BR>Linux还支持命名管道(named pipe),也就是FIFO管道,因为它总是按照先进 <BR>先出的原则工作。第一个被写入的数据将首先从管道中读出来。和其它管道不 <BR>一样,FIFO管道不是临时对象,它们是文件系统中的实体并且可以通过mkfifo <BR>命令来创建。进程只要拥有适当的权限就可以自由使用FIFO管道。打开FIFO管 <BR>道的方式稍有不同。其它管道需要先创建(它的两个file数据结构,VFS inode <BR>和共享数据页面)而FIFO管道已经存在,只需要由使用者打开与关闭。在写者 <BR>进程打开它之前,Linux必须让读者进程先打开此FIFO管道;任何读者进程从中 <BR>读取之前必须有写者进程向其写入数据。FIFO管道的使用方法与普通管道基本 <BR>相同,同时它们使用相同数据结构和操作。 <BR>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -