今天继续研究linux系统编程,继上次对信号进行研究之后,这次开始一个新的章节-----管道,在正式开始之前,先把今天让自己很激动的事说一下,小小的兴奋,经过下周后自己的创业产品用户量就有一个质的飞越了,百万级的,虽说离最终的目标还有很远,但是,这说明自己团队最近几个月的辛苦付出是值得的,也让自己对这次的创业更加有期待了,小小骄傲一下,我的学习任务还得继续坚持,谁叫咱是搞技术的呢,言归正传,正入正题:
#include <unistd.h> #include <sys/stat.h> #include <sys/wait.h> #include <sys/types.h> #include <fcntl.h> #include <stdlib.h> #include <stdio.h> #include <errno.h> #include <string.h> #include <signal.h> #include <sys/time.h> #define ERR_EXIT(m) \ do \ { \ perror(m); \ exit(EXIT_FAILURE); \ } while(0) int main(int argc, char *argv[]) { int pipefd[2]; if (pipe(pipefd) == -1)//创建一个管道之后,就会得到两个文件描述符 ERR_EXIT("pipe error"); pid_t pid; pid = fork();//创建父子进程来演示数据通讯的目的 if (pid == -1) ERR_EXIT("fork error"); if (pid == 0) {//子进程发送数据 close(pipefd[0]);//关闭子进程管道的读端,因为没有用 write(pipefd[1], "hello", 5);//向子进程管道的写端传入数据 close(pipefd[1]);//传递完之后将其关掉 exit(EXIT_SUCCESS); } //父进程读取数据 close(pipefd[1]);//关闭父进程的写端,因为没有用 char buf[10] = {0}; read(pipefd[0], buf, 10);//从管道的读端读入数据 close(pipefd[0]);//关闭管道的读端 printf("buf=%s\n", buf); return 0; }
编译运行:
这个通过管道达到进程间传递数据的例子比较简单,下面用程序来模拟下面的这个shell命令的效果:
我们可以用子进程来运行ls,父进程来运行wc -w命令,具体代码如下:
第一步:
#include <unistd.h> #include <sys/stat.h> #include <sys/wait.h> #include <sys/types.h> #include <fcntl.h> #include <stdlib.h> #include <stdio.h> #include <errno.h> #include <string.h> #include <signal.h> #include <sys/time.h> #define ERR_EXIT(m) \ do \ { \ perror(m); \ exit(EXIT_FAILURE); \ } while(0) int main(int argc, char *argv[]) { int pipefd[2]; if (pipe(pipefd) == -1) ERR_EXIT("pipe error"); pid_t pid; pid = fork(); if (pid == -1) ERR_EXIT("fork error"); if (pid == 0) {//子进程运行ls命令, execlp("ls", "ls", NULL); fprintf(stderr, "error execute ls\n"); exit(EXIT_FAILURE); } //父进程运行wc -w命令 execlp("wc", "wc", "-w", NULL); fprintf(stderr, "error execute wc\n");//如果执行execlp运行失败了,才会执行到这 exit(EXIT_FAILURE); }
【说明】:关于execlp函数的使用,可以参考博文:http://www.cnblogs.com/webor2006/p/3507913.html
第二步,重定向文件描述符,这是实现的关键:
因为ls命令标准是输出到标准输出设备(屏幕)当中,wc命令是从标准输入设备获取数据,而现在,我们希望ls命令输出到管道的写端,而wc命令是从管道的读端获取数据,那该怎么办呢?文件描述符的复制既可达到这个目的,具体代码如下:
#include <unistd.h> #include <sys/stat.h> #include <sys/wait.h> #include <sys/types.h> #include <fcntl.h> #include <stdlib.h> #include <stdio.h> #include <errno.h> #include <string.h> #include <signal.h> #include <sys/time.h> #define ERR_EXIT(m) \ do \ { \ perror(m); \ exit(EXIT_FAILURE); \ } while(0) int main(int argc, char *argv[]) { int pipefd[2]; if (pipe(pipefd) == -1) ERR_EXIT("pipe error"); pid_t pid; pid = fork(); if (pid == -1) ERR_EXIT("fork error"); if (pid == 0) {//子进程运行ls命令, dup2(pipefd[1], STDOUT_FILENO);//将标准输出复制到管道的写端,也就是说标准输出定位到了管道的写端 close(pipefd[1]);//这时管道的读写端都没用了,将其关闭 close(pipefd[0]); execlp("ls", "ls", NULL);//这时ls输出则为管道的写端了,由于文件描述符重定向了 fprintf(stderr, "error execute ls\n"); exit(EXIT_FAILURE); } //父进程运行wc -w命令 dup2(pipefd[0], STDIN_FILENO);//将标准输入重定向管道的读端,所以wc命令这时就会从管道的读端来获取数据喽 close(pipefd[0]); close(pipefd[1]); execlp("wc", "wc", "-w", NULL); fprintf(stderr, "error execute wc\n");//如果执行execlp运行失败了,才会执行到这 exit(EXIT_FAILURE); }
编译运行:
下面再来看一个有关文件描述符复制的程序,先看效果,再来分析其原理:
#include <unistd.h> #include <sys/stat.h> #include <sys/wait.h> #include <sys/types.h> #include <fcntl.h> #include <stdlib.h> #include <stdio.h> #include <errno.h> #include <string.h> #include <signal.h> #include <sys/time.h> #define ERR_EXIT(m) \ do \ { \ perror(m); \ exit(EXIT_FAILURE); \ } while(0) int main(int argc, char *argv[]) { close(0); open("Makefile", O_RDONLY); close(1); open("Makefile2", O_WRONLY | O_CREAT | O_TRUNC, 0644); execlp("cat", "cat", NULL); return 0; }
编译运行:
为啥能实现文件的拷贝效果呢?咱们来分析一下程序:
默认情况下:
而下面这句代码过后:
close(0);
open("Makefile", O_RDONLY);
close(1);
open("Makefile2", O_WRONLY | O_CREAT | O_TRUNC, 0644);
而最后一句关键代码如下:
execlp("cat", "cat", NULL);
我们通常用cat可以查看一个文件内容:
但是,如果cat不带参数,那是什么意思呢?
如效果所示,不带参数的cat命令实际上是从标准输入获取数据,写入到标准输出当中,所以也就是从Makefile文件获取数据,写入到Makefile2文件当中,如果Makefile2文件不存在则会主动创建一个,所以就实现了一个cp命令喽,是不是很有技巧。
【说明】:关于文件描述符的复制,可以参考博文:http://www.cnblogs.com/webor2006/p/3498443.html
好了,今天的内容学到这,虽说内容不多,但是得好好理解,下回见!!