给出几个使用Linux管道的代码样例
环境
- Ubunt 16.04 64 位
- Kernel 4.10.0-28-generic
- gcc v5.5.0
- go v1.12.4
pipe
使用系统接口 pipe
,接口声明如下
1 2 3
| #include <unistd.h>
int pipe(int pipefd[2]);
|
返回值若为 -1
则表示创建失败,成功创建则返回 0
,数组 pipefd
第一个元素为读管道的文件句柄,第二个为写管道的文件句柄,多进程情况下图示如下
注意,管道只能在具有公共祖先的两个进程之间使用
example1
代码实例取自 http://man7.org/
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
| #include <sys/wait.h> #include <sys/types.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include "common.h"
int main(int argc, char *argv[]) { int pipefd[2]; pid_t cpid; char buf; if (argc != 2) { fprintf(stderr, "Usage: %s <string>\n", argv[0]); exit(EXIT_FAILURE); } if (pipe(pipefd) == -1) { perror("pipe"); exit(EXIT_FAILURE); } cpid = fork(); if (cpid == -1) { perror("fork"); exit(EXIT_FAILURE); } if (cpid == 0) { close(pipefd[1]); while (read(pipefd[0], &buf, 1) > 0) write(STDOUT_FILENO, &buf, 1); write(STDOUT_FILENO, "\n", 1); close(pipefd[0]); _exit(EXIT_SUCCESS); } else { close(pipefd[0]); write(pipefd[1], argv[1], strlen(argv[1])); close(pipefd[1]); wait(NULL); exit(EXIT_SUCCESS); } }
|
编译运行 ./a.out 2233
,父进程读入用户的输入,通过管道交给子进程输出至标准输出流
example2
代码样例取自 APUE 15.2 节,有删改。父进程复制读取用户的文件路径,读取文件,将内容通过管道传入给子进程,子进程从管道中管道中读取数据,传入给 /bin/more
输出
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 68 69 70 71
| #define __USE_POSIX2 #include <sys/wait.h> #include <sys/types.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h>
void handle_err_todo(char *msg) { fprintf(stderr, msg, ""); exit(EXIT_FAILURE); }
#define DEF_PAGER "/bin/more" #define MAXLINE 1000
int main(int argc, char *argv[]) { int8_t n; int fd[2]; pid_t pid; char *pager, *argv0; char line[MAXLINE]; FILE *fp;
if (argc != 2) handle_err_todo("argc"); if ((fp = fopen(argv[1], "r")) == NULL) handle_err_todo("fopen"); if (pipe(fd) < 0) handle_err_todo("pipe"); if ((pid = fork()) < 0) handle_err_todo("fork");
if (pid > 0) { close(fd[0]); while (fgets(line, MAXLINE, fp) != NULL) { n = strlen(line); if (write(fd[1], line, n) != n) handle_err_todo("parent write"); } if (ferror(fp)) handle_err_todo("fgets"); close(fd[1]); if (waitpid(pid, NULL, 0) < 0) handle_err_todo("waitpid"); exit(EXIT_SUCCESS); } else { close(fd[1]); if (fd[0] != STDIN_FILENO) { if (dup2(fd[0], STDIN_FILENO) != STDIN_FILENO) handle_err_todo("dpu2"); close(fd[0]); } pager = DEF_PAGER; if ((argv0 = strrchr(pager, '/')) != NULL) argv0++; else argv0 = pager;
if (execl(pager, argv0, (char *)0) < 0) handle_err_todo("execl"); } exit(EXIT_SUCCESS); }
|
编译运行: ./a.out filename.txt
popen,pclose
popen
简化了 pipe
的操作,接口如下
1 2 3 4 5
| #include <stdio.h>
FILE *popen(const char *command, const char *type);
int pclose(FILE *stream);
|
popen
的第二参数,如果为 r
,则父进程从管道中读取子进程写的数据,如果为 w
,则父进程向管道中写数据提供给子进程读取
以下例子为对之前 example2
代码的改进,代码样例取自 APUE 15.3 节,有删改
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
| #define __USE_POSIX2 #include <sys/wait.h> #include <sys/types.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h>
void handle_err_todo(char *msg) { fprintf(stderr, msg, ""); exit(EXIT_FAILURE); }
#define PAGER "/bin/more" #define MAXLINE 1000
int main(int argc, char *argv[]) { char line[MAXLINE]; FILE *fpin, *fpout;
if (argc != 2) handle_err_todo("argc"); if ((fpin = fopen(argv[1], "r")) == NULL) handle_err_todo("fopen"); if ((fpout = popen(PAGER, "w")) == NULL) handle_err_todo("popen");
while (fgets(line, MAXLINE, fpin) != NULL) { if (fputs(line, fpout) == EOF) handle_err_todo("fputs"); } if (ferror(fpin)) handle_err_todo("fgets"); if (pclose(fpout) == -1) handle_err_todo("pclose");
exit(EXIT_SUCCESS); }
|
Go语言使用管道
以下代码为go语言中使用管道,由父进程向子进程中写入数据,最终由子进程输出的例子,代码改编自《自己动手写Docker》第3章
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
| package main
import ( "fmt" "io/ioutil" "log" "os" "os/exec" )
func main() { if os.Args[0] == "/proc/self/exe" { childProcess() return }
var ( cmd *exec.Cmd err error readPipe, writePipe *os.File ) cmd = exec.Command("/proc/self/exe") cmd.Stdin = os.Stdin cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr if readPipe, writePipe, err = os.Pipe(); err != nil { log.Fatalf("os.Pipe() failed: %v", err) } cmd.ExtraFiles = []*os.File{readPipe} if err = cmd.Start(); err != nil { log.Fatalf("cmd.Start() failed: %v", err) } log.Printf("start child process %d", cmd.Process.Pid) writePipe.WriteString("hey child process!") writePipe.Close()
cmd.Wait() }
func childProcess() { var ( pipe *os.File msgByteArr []byte err error ) log.Println("Child process start") pipe = os.NewFile(uintptr(3), "pipe") if msgByteArr, err = ioutil.ReadAll(pipe); err != nil { log.Fatalf("init read pipe error %v", err) } fmt.Printf("get data from main process: %s\n", string(msgByteArr)) }
|