管道
管道的概念就不说了,相信学习过操作系统知识的人都应该知道。管道的功能就是进行进程间的通讯,就像它的名字,管道不只是在进程间传递一两个数那么简单。闲话不多说,以下是两个能在进程间传输数据的函数:
FILE *popen(const char *command, const char *open_mode);
int pclose(FILE *stream_to_close); 函数popen通过执行参数command指向的字符串来调用另一个程序,open_mode是‘r’或‘w’的一个,当open_mode为r时,另一个程序将把输出放在该popen函数返回的文件指针指向的文件里,那么当前进程就能够通过读取该文件来读取另一个程序的输出。当open_mode为w时,被调用的程序能通过fwrite函数来获得当前进程的数据。因此该管道是一个单工系统,若函数运行失败,函数将返回NULL。对应于popen函数的pclose函数的功能是在管道数据传输完毕后将管道关闭,如果在数据传输结束之前就调用了这个函数,那么进程将进入等待直到数据传输结束。如果在调用该函数时,被调用的进程已经进入了等待状态,那么函数将返回一个错误。下面是一个简单的示例,程序调用命令“unname - a”打印系统信息,然后程序读取文件里存储的系统信息: #include <unistd.h> #include <stdlib.h> #include <stdio.h> #include <string.h> int main() { FILE *read_fp; char buffer[BUFSIZ + 1]; int chars_read; memset(buffer, ‘/0’, sizeof(buffer)); read_fp = popen("uname -a", "r"); if (read_fp != NULL) { chars_read = fread(buffer, sizeof(char), BUFSIZ, read_fp); if (chars_read > 0) { printf("Output was:-/n%s/n", buffer); } pclose(read_fp); exit(EXIT_SUCCESS); } exit(EXIT_FAILURE); } 上面的两个函数是在较高的级别上进行管道操作,而函数pipe则在较低的级别上对管道进行控制,该函数的定义如下: int pipe(int file_descriptor[2]); 参数是两个文件描述符,写入到file_descriptor[1]里的数据将以先进先出的顺序读入到file_descriptor[0]里。这很容易理解,故不举例。 信号 信号可以用作进程间的通信工具。信号有很多种,可以查阅文件bits/signum.h来了解信号的类型定义。接收到指定信号的进程将会执行指定代码。函数signal用来指定进程接收的信号的类型和接收到信号后的行为,该函数定义如下: int signal (int sig, __sighandler_t handler); 第一个参数sig指定了要处理的信号的类型,可以取除了SIGKILL和SIGSTOP以外的任何信号。第二个参数是接收到信号的动作,它可以是一个函数或SIG_IGN或SIG_DFL。如果是一个函数,那么要求该函数拥有一个int型参数且返回值为int型。SIG_IGN表示忽略指定信号,那么此后进程再接收到指定信号时将不再响应。除非再次调用signal函数,且第一个参数是指定信号而第二个参数是SIG_DFL。SIG_DFL表示恢复系统对指定信号的响应。 当进程正在执行一个系统调用时,进程若接收到信号,则对于大部分系统调用(原子函数)来说,信号在系统调用结束前不会被响应。少数几个能被信号中断的系统调用是read,write,open,wait,pause等。 当信号的信号处理函数正在执行而又接收到该信号时,该信号将被存储下来,等到当前的信号处理函数执行完毕后再次调用信号处理函数。但是若信号处理函数还未执行,又接收到相同的信号,则信号不会被累积。因此若两个进程同时向一个进程发送信号,那么其中的一个信号可能会丢失。 若想在一个进程里向其它进程发送信号,使用函数kill,其定义如下: int kill(pid_t pid, int sig); 参数pid指定信号发送的对象进程,它可以是某个进程的ID,也可以是以下的值:0,-1,<-1。若pid的值为0,则信号会被发送到当前进程所在进程组的所有进程。当pid的值为-1,信号按进程标识符从高到低的顺序发送给全部的进程。当pid的值小于-1时,信号发送给标识符与pid绝对值相同的进程。(在此要说明的是,一个用户的进程只能向同一用户的进程发送信号,而不能向其他用户发送信号。) 参数sig指定要发送信号的类型,可以是任何有效的类型。 系统调用alarm和pause 用函数alarm可以对信号进行定时,使用alarm函数可以设定一个报警时钟,当时钟定时到时的时候,该信号会被发送。作为一个系统掉调用,它的定义如下: unsigned int alarm(unsigned int seconds); 参数seconds指定了信号在发送前等待的时间。alarm不会暂停进程的运行,而且在fork函数调用后的子进程里,该时钟失效。alarm调用不会积累,重复调用将使最新的时钟有效。 系统调用pause将使进程暂停直到某个信号到来。 系统调用setjmp和longjmp 有时候我们希望在接收到某个信号后,进程返回到以前的位置重新执行,库调用setjmp和longjmp就是用来实现该功能的,setjmp能保存程序的当前位置,而longjmp能将程序转回保存的位置,这两个函数在setjmp.h里的定义如下: int setjmp(jmp_buf env); void longjmp(jmp_buf env, int val); setjmp的参数env用来保存程序当前位置的堆栈环境,而longjmp的参数env则是setjmp保存的位置,val设置setjmp的返回值。