Linux操作系统pipe()-execlp()-dup2()分析
目标:使用Linux system call API实现sort | uniq | wc -l < data.txt。
涉及的知识点:
- 使用C调用shell命令
- 使用管道,在各命令间通信
- 标准输入输出重定向
execlp()执行shell command
头文件:<unistd.h>
函数原型:
int execlp(const char *file, const char *arg, ... (char *) NULL);
函数说明:
execlp()会PATH环境变量所指的目录中查找符合参数file的文件名,找到后便执行该文件。arg为命令名,后跟参数列表,可以为空(NULL)。最后一个参数必须用空指针(NULL)作结束。如果函数调用成功,进程自己的执行代码就会变成加载程序的代码,execlp()后边的代码也就不会执行了。
使用示例:
execlp(“/bin/ls”, “ls”, NULL);
pipe()管道
头文件:< unistd.h>
函数原型:
int pipe(int pipefd[2]);
函数说明:
pipe()会创建一个管道,它仅能实现单向通信,引用于进程间通信中。函数返回两个整型值分别指向管道输出输出文件描述符,其中pipefd[0]指向管道读端,而pipefd[1]指向管道的写端。写入管道的数据由kernel中开的缓冲区存储,直到另一端读取。也就是说这个缓冲区有大小,一般为4k bytes。
使用示例:
int fd[2];
if(pipe(fd) == -1){
perror();
return ;
}
使用fork()克隆子进程时,父进程创建出的管道的文件描述符被复制,所以子进程也有相同文件描述副本,两者指向同一个管道。两进程可通过此管道完成通信。本次实验目的为将sort命令输出通过管道传递数据,作为uniq的输入,同理,uniq的输出作为wc的输入。
现在剩下最后一个问题,uniq,wc命令输入方式由两种,标准输入或文件。通过文件的方式,需要先读取命令的输出,写至文件中,最后让下一命令读该文件。此方式操作较为麻烦,还需要处理中间文件。另一种方式,使用重定向。这里需要使用dup2()的方法。
dup2()实现重定向
头文件:<unistd.h>
函数原型:
int dup2(int oldfd, int newfd);
int dup(int oldfd);
函数说明:
先说dup(),此系统调用会创建一个oldfd文件描述符副本,并使用一个最小未使用的整数作为新的文件描述符。此后,oldfd和newfd可以互换使用,它们指向同一个打开的文件描述符,因此共享文件偏移和文件状态标志。但两个文件描述符不会共享文件描述符的标志位。函数返回值为newfd。
那么使用dup怎么重定向呢?
close(1); //关闭标准输出文件描述符
int newfd = dup(oldfd); //此时,newfd为1
printf(“hello,world!\n”); //此时,本应输出至stdout,被重定向至oldfd中。
再说dup2(),该函数于dup类似。不同的是,dup2()不会使用最小未使用整数作为新的文件描述符,而是使用参数newfd的文件描述符作为newfd。如果此时的参数newfd已被打开暂用,则该函数会先关闭它,然后再使用。函数返回值是参数newfd的文件描述符。本例正是使用该函数。
使用示例:
dup2(oldfd, STDOUT_FILENO); //仅一句,已实现STDOUT_FILENO重定向至oldfd。
本例目标源码
#include <stdio.h>
#include <unistd.h> //pipe, fork
#include <string.h> //strlen
#include <sys/wait.h> //wait
#include <sys/types.h>
#include <fcntl.h>
#include <sys/stat.h>
int main(int argc[], char *argv[])
{
int fd1[2], fd2[2]; //pipe
pid_t fpid;
if(pipe(fd1) == -1){
perror("pipe fd1");
return 1;
}
fpid = fork();
if(fpid < 0){
perror("fork");
return 1;
}
if(fpid == 0){ // child process
printf("sort process, id is %d\n", getpid());
if(dup2(fd1[1], STDOUT_FILENO) == -1){
perror("dup2");
return 1;
}
close(fd1[0]);
if(execlp("/usr/bin/sort", "sort", NULL) == -1){
perror("sort");
return 1;
}
//close(fd1[1]);
}
if(pipe(fd2) == -1){
perror("pipe fd2");
close(fd1[0]); close(fd1[1]);
return 1;
}
fpid = fork();
if(fpid < 0){
perror("fork");
close(fd1[0]); close(fd1[1]);
close(fd2[0]); close(fd2[1]);
return 1;
}
if(fpid == 0){ // child process
printf("uniq process, id is %d\n", getpid());
if(dup2(fd1[0], STDIN_FILENO) == -1){
perror("dup2");
return 1;
}
if(dup2(fd2[1], STDOUT_FILENO) == -1){
perror("dup2");
return 1;
}
close(fd1[1]);
close(fd2[0]);
if(execlp("/usr/bin/uniq", "uniq", NULL) == -1){
perror("uniq");
return 1;
}
}
fpid = fork();
if(fpid < 0){
perror("fork");
return 1;
}
if(fpid == 0){ // child process
printf("wc process, id is %d\n", getpid());
if(dup2(fd2[0], STDIN_FILENO) == -1){
perror("dup2");
return 1;
}
close(fd2[1]);
close(fd1[0]);
close(fd1[1]);
if(execlp("/usr/bin/wc", "wc", "-l", NULL) == -1){
perror("wc");
return 1;
}
}
close(fd1[0]);close(fd1[1]);
close(fd2[0]);close(fd2[1]);
wait(NULL);
return 0;
}