管道 管道是Linux中进程通信的一种方式。这里所说的管道指无名管道,它具有以下特点:
它只能用于具有亲缘关系的进程之间通信
它是一个半双工的通信模式,具有固定的读端和写端
管道可以看成是一种特殊的文件,对于它的读写也可以使用普通的read()和write()等函数。但它不是普通文件,并不属于其他任何文件系统,并且只存在于内核的内存空间中
管道的创建与关闭 管道是基于文件描述符的通信方式,当一个管道建立时,它会创建两个文件描述符fds[0]和fds[1],其中fds[0]固定用于读,fds[1]固定用于写,这样就构成了一个半双工的通道。管道的关闭只需逐个关闭各个文件描述符即可
管道的创建可以通过pipe()实现:
1 2 #include <unistd.h> int pipe (int fd[2 ]) ;
成功返回0,出错返回-1
管道的读写说明 用pipe()创建的管道两端出于一个进程中,由于管道是主要用于在不同进程间通信的,因此这在实际应用中没有太大意义。实际上,通常是先创建一个管道,再通过fork()函数创建一子进程,这时子进程会继承父进程所创建的管道,这是父子进程管道的对应关系如下图:
这时父子进程都有了自己的读写通道,为了实现父子进程之间的读写,只需把无关的读端或写端关闭即可,例如将父进程的写端和子进程的读端关闭,这时就建立了一条“子进程写入,父进程读取”的通道:
同样也可以关闭父进程的读端,子进程的写端,这样就可以建立一条“子进程读取,父进程写入”的通道
pipe使用实例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 #include <unistd.h> #include <sys/types.h> #include <sys/wait.h> #include <errno.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #define MAX_DATA_LEN 256 #define DELAY_TIME 1 int main () { pid_t pid; int fds[2 ]; char buf[MAX_DATA_LEN] = {0 }; const char data[] = "Pipe Test Program" ; int real_read, real_write; if (pipe (fds) < 0 ) { perror ("pipe" ); exit (1 ); } printf ("Test start!\n" ); if ((pid = fork ()) == 0 ) { close (fds[1 ]); sleep (DELAY_TIME * 3 ); if ((real_read = read (fds[0 ], buf, MAX_DATA_LEN)) > 0 ) { printf ("%d bytes read from the pipe is '%s'\n" , real_read, buf); } else { perror ("read" ); } close (fds[0 ]); exit (0 ); } else if (pid > 0 ) { close (fds[0 ]); sleep (DELAY_TIME); if ((real_write = write (fds[1 ], data, strlen (data))) != -1 ) { printf ("Parent wrote %d bytes : '%s'\n" , real_write, data); } else { perror ("write" ); } close (fds[1 ]); waitpid(pid, NULL , 0 ); } printf ("Test end!\n" ); return 0 ; }
标准流管道 与Linux的文件操作中有基于文件流的标准I/O一样,管道的操作也支持基于文件流的模式。标准流管道将一系列的创建过程合并到一个函数popen()中完成,它完成工作有以下几步:
创建一个管道
fork()一个子进程
在父子进程中关闭不需要的文件描述符
执行exec函数族调用
执行函数中所指定的命令
这个函数可以大大减少代码的编写量,但同时也有一些不利之处,它不如管道创建函数那样灵活,并且用popen()创建的管道必须使用标准I/O函数进行操作,但不能使用read(),write()一类不带缓冲的I/O函数
关闭用popen()创建的管道,必须使用函数pclose()来关闭,该函数关闭标准I/O流,并等待命令执行结束
1 2 3 #includ <stdio.h> FILE *popen (const char *command, const char *type) ;int pclose (FILE *stream) ;
command:指向一个以null结尾的字符串,这个字符串包含一个shell命令
type:“r”,文件指针连接到command的标准输出;“w”,文件指针连接到command的标准输入
成功返回文件流指针,失败返回-1
popen在Linux嵌入式软件中常用方式为:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 #define POPEN_READ_BUFF 1024 int popen_read (const char * cmd, char * res, int len) { FILE* fp; char buf[POPEN_READ_BUFF]; int l, nread = 0 ; if (cmd == NULL || res == NULL ) { printf ("Invalid parameter\n" ); return -1 ; } if (len <= 0 ) { printf ("Invalid len\n" ); return -1 ; } len--; fp = popen(cmd, "r" ); if (fp == NULL ) { printf ("popen % failed!\n" , cmd); return -1 ; } while (fgets(buf, POPEN_READ_BUFF, fp) != NULL ) { l = strlen (buf); if (l >= len - nread) { memcpy (res + nread, buf, len - nread); res[len + 1 ] = '\0' ; pclose(fp); return len; } else { memcpy (res + nread, buf, l + 1 ); } nread += l; } pclose(fp); return nread; }
传入命令、接收执行结果的空间以及空间大小,返回读取的字节数