对于Dup2 的理解:
源代码:
1 #include <unistd.h> 2 #include <stdio.h> 3 #include <stdlib.h> 4 #include <string.h> 5 #include <time.h> 6 7 #define MSGSIZE 20 8 #define READ 0 9 #define WRITE 1 10 11 int main(int argc, char const *argv[]) 12 { 13 int p[2], bytes, res, c; 14 char inbuf[10240]; 15 int pid; 16 printf("%c", 11); 17 if(pipe(p) == -1){// creat the pipe , if pipe is built failed , exit . 18 perror("pip call"); 19 exit(1); 20 } 21 pid = fork(); 22 if(pid != 0){// creat parent pid and child pid. 23 close(p[READ]);//close parent pipe read 24 dup2(p[WRITE], 1); 25 close(p[WRITE]);//close parent pipe write 26 execlp(argv[1], argv[1], NULL); 27 } 28 else{ 29 30 close(p[WRITE]);//close child pipe write 31 32 dup2(p[READ],0); 33 34 close(p[READ]);//close child pipe read 35 36 execlp(argv[2], argv[2], NULL); 37 } 38 return 0; 39 }
通过命令行输出:
./a.out “ls” “ps”
仅仅在终端执行了ps的命令, 而没有看到ls 命令的结果。
因此,开始走入了第一个误区:父进程没有执行
通过调试 在父进程执行if条件中加入以下代码:
if(pid != 0){
printf("4556\n");
close(p[READ]);
dup2(p[WRITE], 1);
close(p[WRITE]);
printf("4556\n");
execlp(argv[1], argv[1], NULL);
}
加入了2个printf , 但是只有dup2 上面的printf 结果输出到屏幕上,因此我注释了 dup2(p[WRITE], 1); 结果在父进程if语句中的dup2 后面的命令都执行并且输出到屏幕上了。通过查找dup2 命令发现了重定向的强大之处。
先解释下 dup2 命令,
int dup2(int filedes, int filedes2);
说明:
用dup2 则可以用filedes2 参数指定新描述符的数值。如果filedes2 已经打开,则先将其关闭。如若filedes 等于filedes2,则返回filedes2,而不关闭它。
dup2(p[WRITE], 1); 这句话可以理解为将标准输出重定向到 p[WRITE], 因此在这句话后面的所有printf 语句打印或者exec 执行的内容都输入到了p[WRITE]中。刚开始有个迷惑,就是既然已经close(1)了,为什么还能输入到p[WRITE]中,通过自己的直觉判断,应当是close(1)关闭了屏幕的输出,但是它有缓冲区保存printf打印出的内容,并且由于重定向的关系,输进了p[WRITE]中。
代码:
1 #include <unistd.h> 2 #include <stdio.h> 3 #include <stdlib.h> 4 #include <string.h> 5 #include <time.h> 6 7 #define MSGSIZE 20 8 #define READ 0 9 #define WRITE 1 10 11 int main(int argc, char const *argv[]) 12 { 13 int p[2], bytes, res, c; 14 char inbuf[10240]; 15 int pid; 16 printf("%c", 11); 17 if(pipe(p) == -1){// creat the pipe , if pipe is built failed , exit . 18 perror("pip call"); 19 exit(1); 20 } 21 pid = fork(); 22 if(pid != 0){// creat parent pid and child pid. 23 close(p[READ]);//close parent pipe read 24 dup2(p[WRITE], 1); 25 close(p[WRITE]);//close parent pipe write 26 printf("123!\n"); 27 execlp(argv[1], argv[1], NULL); 28 perror("execlp");//error output 29 } 30 else{ 31 while(1){ 32 res = read(p[READ], inbuf, 10240); 33 if(res>0) 34 printf("%s\n", inbuf); 35 break; 36 } 37 38 close(p[WRITE]);//close child pipe write 39 40 dup2(p[READ],0); 41 42 close(p[READ]);//close child pipe read 43 44 execlp(argv[2], argv[2], NULL); 45 } 46 return 0; 47 }
通过在子进程中用while(1)循环读取p[READ]内容,发现读出了父进程本应在屏幕上打印的内容,因此父进程是执行了所有命令行,只是通过重定向命令存到了p[WRITE]管道中。
由于有dup2(p[READ], 0) 命令,因此猜测标准输入的文件描述符定向到了p[READ] , 因此如果猜测没错,通过getchar()读取文件标准输入并把P[READ]的内容输出到屏幕上则证明我猜想没错。
代码:
1 #include <unistd.h> 2 #include <stdio.h> 3 #include <stdlib.h> 4 #include <string.h> 5 #include <time.h> 6 7 #define MSGSIZE 20 8 #define READ 0 9 #define WRITE 1 10 11 int main(int argc, char const *argv[]) 12 { 13 int p[2], bytes, res, c; 14 char inbuf[10240]; 15 int pid; 16 printf("%c", 11); 17 if(pipe(p) == -1){// creat the pipe , if pipe is built failed , exit . 18 perror("pip call"); 19 exit(1); 20 } 21 pid = fork(); 22 if(pid != 0){// creat parent pid and child pid. 23 close(p[READ]);//close parent pipe read 24 dup2(p[WRITE], 1); 25 close(p[WRITE]);//close parent pipe write 26 printf("123!\n"); 27 execlp(argv[1], argv[1], NULL); 28 perror("execlp");//error output 29 } 30 else{ 31 // while(1){ 32 // res = read(p[READ], inbuf, 10240); 33 // if(res>0) 34 // printf("%s\n", inbuf); 35 // break; 36 // } 37 38 close(p[WRITE]);//close child pipe write 39 40 dup2(p[READ],0); 41 while((c=getchar()) != -1){ 42 printf("%c", c); 43 } 44 close(p[READ]);//close child pipe read 45 46 execlp(argv[2], argv[2], NULL); 47 } 48 return 0; 49 }
通过在dup2(p[READ], 0) 后面while循环读入输入流输入的字符并且打印出来, 发现结果果然是p[READ]的内容,猜疑没错。
为了更清楚的理解dup2的重定向含义,想理解dup2(fd,0)和dup2(0,fd)功能相同吗?
为了得到答案,找些资料发现,答案是不同。
测试代码:
1 #include <stdio.h> 2 #include <unistd.h> 3 #include <fcntl.h> 4 5 #define BUFMAXSIZE 4096 6 7 int main(int argc, char *argv[]) 8 { 9 int fd; 10 int n; 11 char buf[BUFMAXSIZE]; 12 int fs; 13 fs = open("test", O_RDWR); 14 if((fd = open("duan", O_RDWR )) == -1) 15 { 16 perror("open error!"); 17 return(1); 18 } 19 20 dup2(fd, 1); // dup2(0,fd); 21 22 while((n = read(fs, buf, BUFMAXSIZE)) > 0) 23 { 24 printf("begin to read...\n"); 25 if(write(STDOUT_FILENO, buf, n) != n) 26 { 27 perror("write error!"); 28 return(1); 29 } 30 printf("end to write...\n"); 31 } 32 if(n < 0) 33 { 34 perror("read error"); 35 return(1); 36 } 37 38 return 0; 39 }
dup(fd, 0) 这段代码测试, 打印出了 duan 文件里面的内容。
之后创建个文件 Levi 里面写和duan 文件不同的内容。
通过./a. out < Levi输出:
第一个输出是 dup(fd, 0) 输出了Duan 文件的内容。即是fd的内容
第二个输出是 dup(0, fd) 输出了 Levi 文件的内容。即是 通过文件重定向到标准输入的内容。
从图中的输出结果已经可以看到两者的区别了。
第一种dup2(fd,0)之前已经将 fd 初始化指向到文本 Duan了,
并且不会被后面的代码所修改。
第二种dup2(0,fd)则将 fd 重新指向到文件描述符0所代表的文件(即终端标准输入)了。
那么可以看到,程序的执行中不会再读取 Duan 文件了。
而是进入了一种交互模式。
另外,这时“输入重定向”也可以生效了。
文件描述符0被 “<” 重定向到 Duan 了
所以,这里直接输出了该文本的内容。
dup2(fd,0) 相当于“输入重定向”的功能,
dup2(0,fd) 不是表示 fd 所指的文件接收来自终端的输入,因为,fd 已经不再指向原来的那个文件了。
它和文件描述符0 已经在共享同一个文件表项(即指向终端标准输入的那个表项)了。
“输出重定向”的功能可以用 dup2(fd ,1) 替代。
dup2(fd,1) 和 dup2(1,fd) 也是大同小异。