一.管道
管道是Linux支持的最初Unix IPC形式之一,具有以下特点:
A. 管道是半双工的,数据只能向一个方向流动;
B. 需要双工通信时,需要建立起两个管道;
C. 只能用于父子进程或者兄弟进程之间(具有亲缘关系的进程);
D. 单独构成一种独立的文件系统:管道对于管道两端的进程而言,就是一个文件,但它不是普通的文件,它不属于某种文件系统,而是自立门户,单独构成一种文件系统,并且只存在与内存中。
匿名管道的创建:该函数创建的管道的两端处于一个进程中间,在实际应用中没有太大意义;因此,一个进程在由pipe()创建管道后,一般再fork一个子进程,然后通过管道实现父子进程间的通信。因此只要两个进程中存在亲缘关系(这里的亲缘关系指的是具有共同的祖先),都可以采用管道方式来进行通信。
#include <unistd.h>
int pipe(int fd[2]);
匿名管道的读写规则:数据的读出和写入:一个进程向管道中写的内容被管道另一端的进程读出。写入的内容每次都添加在管道缓冲区的末尾,并且每次都是从缓冲区的头部读出数据。管道两端可分别用描述字fd[0]以及fd[1]来描述,需要注意的是,管道的两端是固定了任务的。即一端只能用于读,由描述字 fd[0]表示,称其为管道读端;另一端则只能用于写,由描述字fd[1]来表示,称其为管道写端。如果试图从管道写端读取数据,或者向管道读端写入数据 都将导致错误发生。一般文件的I/O函数都可以用于管道,如close、read、write等等。从管道中读取数据:如果管道的写端不存在,则认为已经读到了数据的末尾,读函数返回的读出字节数为0;当管道的写端存在时,
如果请求的字节数目大于PIPE_BUF, 则返回管道中现有数据字节数。下面例子给出了管道的具体应用,父进程通过管道发送一些命令给子进程,子进程解析命令,并根据命令作相应处理。
1 #include <unistd.h> 2 #include <sys/types.h> 3 main() 4 { 5 int pipe_fd[2]; 6 pid_t pid; 7 char r_buf[4]; 8 char** w_buf[256]; 9 int childexit=0; 10 int i; 11 int cmd; 12 13 memset(r_buf,0,sizeof(r_buf)); 14 if(pipe(pipe_fd)<0) 15 { 16 printf("pipe create error "); 17 return -1; 18 } 19 if((pid=fork())==0) 20 //子进程:解析从管道中获取的命令,并作相应的处理 21 { 22 printf(" "); 23 close(pipe_fd[1]); 24 sleep(2); 25 26 while(!childexit) 27 { 28 read(pipe_fd[0],r_buf,4); 29 cmd=atoi(r_buf); 30 if(cmd==0) 31 { 32 printf("child: receive command from parent over now child process exit "); 33 childexit=1; 34 } 35 36 else if(handle_cmd(cmd)!=0) 37 return; 38 sleep(1); 39 } 40 close(pipe_fd[0]); 41 exit(); 42 } 43 else if(pid>0) 44 //parent: send commands to child 45 { 46 close(pipe_fd[0]); 47 w_buf[0]="003"; 48 w_buf[1]="005"; 49 w_buf[2]="777"; 50 w_buf[3]="000"; 51 for(i=0;i<4;i++) 52 write(pipe_fd[1],w_buf[i],4); 53 close(pipe_fd[1]); 54 } 55 } 56 //下面是子进程的命令处理函数(特定于应用): 57 int handle_cmd(int cmd) 58 { 59 if((cmd<0)||(cmd>256)) 60 //suppose child only support 256 commands 61 { 62 printf("child: invalid command "); 63 return -1; 64 } 65 printf("child: the cmd from parent is %d ", cmd); 66 return 0; 67 }
二.命名管道FIFO
命名管道是一种特殊类型的文件,它在文件系统中以文件名的形式存在,但它的行为却和匿名管道类似FIFO文件与普通文件之间的区别:程序不能以O_RDWR模式打开FIFO文件进行读写操作,这样做的后果并未明确定义,如果一个管道以读/写方式打开, 进程就会从这个管道读回它自己的输出.如果需要在程序之间双向传递数据,最好是使用一对FIFO或管道,一个方向使用一个;或者(但不常用),采用先关闭再重新打开FIFO的方法来,明确改变数据流的方向.
open_flag标志(O_RDONLY, O_WRONLY和O_NONBLOCK)的4种合法组合方式,close调用行为并不受O_NONBLOCK标志的影响.
A. 阻塞方式读open: open(const char *path, O_RDONLY);在这种方式下,open调用将阻塞,除非有一个进程以写的方式打开同一个FIFO,否则它不会
返回。
B. 非阻塞方式读:openopen(const char *path, O_RDONLY | O_NONBLOCK);即使没有其它的进程以写的方式打开FIFO,这个open调用也将成功并立即
返回。
C. 阻塞方式写open:open(const char *path, O_WRONLY);在这种情况下,open调用将阻塞,直到有一个进程以读方式打开同一个FIFO为止。
D. 非阻塞方式写open:open(const char *path, O_WRONLY | O_NONBLOCK)这个函数调用总是立即返回,如果没有进程以读方式打开FIFO文件,open调用将返回一个错误-1并且FIFO也不会打开。如果 有一个进程以读方式打开FIFO文件,那么可以通过这个函数返回的文件描述符对这个FIFO进行写操作。
open模式的最常见的组合形式:
A. 阻塞方式读open + 阻塞方式写open 这样的形式, 它允许先启动读进程,并在open调用中等待,当写进程打开FIFO时,两个进程在open调用处取得同步,两个程序继续运行。
B. 非阻塞方式读open + 阻塞方式写open这时,读进程在即使没有写进程存在的情况下,仍能执行open调用并继续执行.随后写进程开始执行,因为FIFO已被读进程打开,它在open调用后立即继续执行.
使用O_NONBLOCK对FIFO的read和write操作约定:对一个空的,阻塞的FIFO(即没有用O_NONBLOCK标志打开)的read调用将等待,直到有数据可以读时才执行。与此相反,对一个空的,非阻塞的FIFO的read调用立即返回0字节。对一个完全阻塞FIFO的write调用将等待,直到数据可以被写入时才继续执行。如果FIFO不能接收所有写入的数据,它将按下面的规则执行:如果请求写入的数据长度 <= PIPE_BUF字节,调用失败,数据不能写入。如果请求写放的数据长度 > PIPE_BUF字节,将写入部分数据,返回实际写入的字节数,返回值也可能是0.FIFO的长度定义在limits.h中的#define PIPE_BUG语句中定义,linux下的值通常是4096字节。
FIFO的原子性:系统规定, 在一个以O_WRONLY方式打开的FIFO中,如果写入的数据长度 <= PIPE_BUF字节,那么或者写入全部字节,或者一个字节都不写入。如果保证所有写请求是发往一个阻塞的FIFO的,并且每个写请求的数据长度 <= PIPE_BUF字节,系统将会保证数据决不会交错在一起。所以,通常将每次通过FIFO传递的数据长度限制为PIPE_BUF字节是个好方法。
1 /*示例, 生产者--消费者模型 2 以阻塞方式读open + 阻塞方式写open; 3 写进程读取文件数据并发送到FIFO; 4 读进程读取FIFO中的数据并显示; 5 6 代码如下: 7 producer.c*/ 8 9 /* 10 * File 11 * producer.c 12 * 13 */ 14 15 #include <unistd.h> 16 #include <stdlib.h> 17 #include <stdio.h> 18 #include <string.h> 19 #include <fcntl.h> 20 #include <limits.h> 21 #include <sys/types.h> 22 #include <sys/stat.h> 23 24 #define FIFO_NAME "test_fifo" 25 #define BUFFER_SIZE (512) 26 27 #define TESTF_IN "test.dat" 28 #define OP_LEN (100) 29 30 FILE* fp_in; 31 32 int main(char argc, char* argv[]) 33 { 34 int pipe_fd; 35 int res; 36 int open_mode = O_WRONLY; 37 int bytes_sent = 0; 38 int bytes_read = 0; 39 char buffer[BUFFER_SIZE + 1]; 40 41 if (access(FIFO_NAME, F_OK) == -1) 42 { 43 res = mkfifo(FIFO_NAME, 0777); 44 if (res != 0) 45 { 46 fprintf(stderr, "Could not create fifo %s ", FIFO_NAME); 47 exit(EXIT_FAILURE); 48 } 49 } 50 51 printf("Process %d opening FIFO O_WRONLY ", getpid()); 52 pipe_fd = open(FIFO_NAME, open_mode); 53 printf("Process %d result %d ", getpid(), pipe_fd); 54 55 56 if (pipe_fd != -1) 57 { 58 59 if ((fp_in = fopen(TESTF_IN, "r")) < 0) 60 { 61 fprintf(stderr, "Open input file failed:%s ", TESTF_IN); 62 exit(EXIT_FAILURE); 63 } 64 65 while ((bytes_read = fread(buffer, sizeof(char), OP_LEN, fp_in))) 66 { 67 printf("PRODUCE: %d, %s", bytes_read, buffer); 68 69 res = write(pipe_fd, buffer, bytes_read); 70 if (res == -1) 71 { 72 fprintf(stderr, "Write error on pipe "); 73 exit(EXIT_FAILURE); 74 } 75 bytes_sent += res; 76 77 memset(buffer, 0, BUFFER_SIZE); 78 if (feof(fp_in) != 0) 79 { 80 printf("read over "); 81 break; 82 } 83 } 84 85 (void)close(pipe_fd); 86 fclose(fp_in); 87 } 88 else 89 { 90 exit(EXIT_FAILURE); 91 } 92 93 return 0; 94 } 95 96 consumer.c 97 98 /* 99 * File 100 * consumer.c 101 * 102 */ 103 104 105 #include <unistd.h> 106 #include <stdlib.h> 107 #include <stdio.h> 108 #include <string.h> 109 #include <fcntl.h> 110 #include <limits.h> 111 #include <sys/types.h> 112 #include <sys/stat.h> 113 114 115 #define FIFO_NAME "test_fifo" 116 #define BUFFER_SIZE (512) 117 #define OP_LEN (100) 118 119 int main(char argc, char* argv[]) 120 { 121 int pipe_fd; 122 int res; 123 int open_mode = O_RDONLY; 124 char buffer[BUFFER_SIZE + 1]; 125 int bytes_read = 0; 126 127 memset(buffer, '