您的位置:首页 > 其它

管道使用举例

2014-10-26 21:44 218 查看
//----------------------------------------------------

//AUTHOR: lanyang123456

//DATE: 2014-10-26

//----------------------------------------------------

管道是一种两个进程间进行单向通信的机制。因为管道传递数据的单向性,管道又称为半双工管道。管道的这一特点决定了其使用的局限性。具有以下特点:

*** 数据只能由一个进程流向另一个进程(其中一个读管道,一个写管道);如果要进行双工通信,需要建 立两个管道。
*** 管道只能用于父子进程或者兄弟进程间通信。,也就是说管道只能用于具有亲缘关系的进程间通信。

#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.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);
}
printf("before fork\n");

cpid = fork();
if (cpid == -1) {
perror("fork");
exit(EXIT_FAILURE);
}

if (cpid == 0) { /* Child reads from pipe */
close(pipefd[1]); /* Close unused write end */
printf("child get parent's:\n");
while (read(pipefd[0], &buf, 1) > 0)
write(STDOUT_FILENO, &buf, 1);

write(STDOUT_FILENO, "\n", 1);
close(pipefd[0]);
printf("--------child exit-------\n");
_exit(EXIT_SUCCESS);
//exit(EXIT_SUCCESS);

} else { /* Parent writes argv[1] to pipe */
close(pipefd[0]); /* Close unused read end */
printf("parent give child %s\n", argv[1]);
write(pipefd[1], argv[1], strlen(argv[1]));
close(pipefd[1]); /* Reader will see EOF */
wait(NULL); /* Wait for child */
printf("---------parent exit-------\n");
exit(EXIT_SUCCESS);
}
}


$ gcc -o test pipe1.c

$ ./test hello

before fork

parent give child hello

child get parent's:

hello

--------child exit-------

---------parent exit-------

有个地方值得关注一下,就是exit和_exit的区别:

exit() 做的工作包括:执行退出处理函数;关闭所有标准iO流(这会导致所有的缓冲数据都会被冲洗,即写到文件);然后调用_exit();

如果这样执行程序,可看到区别。

$ ./test hello > tmp

$ cat tmp

hello

before fork

parent give child hello

---------parent exit-------

下面的代码通过管道实现重定向, 将标准输出定向到管道的写入端,标准输入定向到管道的读取端。

#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>

#define MAXLEN 2048

int main()
{
int pipefd[2];

int ret = pipe(pipefd);
if (ret < 0) {
perror("pipe");
exit(EXIT_FAILURE);
}

int pid = fork();
if (pid < 0) {
perror("fork");
exit(EXIT_FAILURE);
} else if (pid == 0) {//child
close(pipefd[0]); //关闭不需要的端

if (pipefd[1] != STDOUT_FILENO) { //重定向管道输出端到标准输出
if (dup2(pipefd[1], STDOUT_FILENO) != STDOUT_FILENO) {
perror("dup2");
exit(EXIT_FAILURE);
}
close(pipefd[1]);
}

//write(STDOUT_FILENO, "one ", strlen("one "));
execlp("ls", "ls", "-l", (char *)0); //由于将stdout重定向为管道的输入端,所以这行的执行结果被存放在管道输入端
_exit(127);
} else if(pid > 0) {//parent
close(pipefd[1]);
char buffer[MAXLEN];
char ret;
int nread = 0;
memset(buffer, 0, MAXLEN);
if (pipefd[0] != STDIN_FILENO) { //重定向管道读端到标准输入
if (dup2(pipefd[0], STDIN_FILENO) != STDIN_FILENO) {
perror("dup2");
exit(EXIT_FAILURE);
}
close(pipefd[0]);
}

/*do {
ret = read(STDIN_FILENO, buffer + nread, MAXLEN - 1);
if (ret < 0 && errno == EINTR) {
continue;
} else if (ret > 0) {
nread += ret;
}
}
while ( ret > 0);

printf("%s", buffer);
fflush(stdout);*/
execlp("wc", "wc", "-l", (char *)0); //这里wc后面没有给出文件名,所以从标准输入读取,而标准输入端被定向为管道输出端
//,所以读取的数据即为上面ls -l的执行结果
} else {
printf("fork error/n");
}

return 0;
}


 $./test2

17
17行

再举一例:

#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>

#define MAXLEN 2048

int main()
{
int pipefd[2];

int ret = pipe(pipefd);
if (ret < 0) {
perror("pipe");
exit(EXIT_FAILURE);
}

int pid = fork();
if (pid < 0) {
perror("fork");
exit(EXIT_FAILURE);
} else if (pid == 0) {//child
close(pipefd[0]); //关闭不需要的端

if (pipefd[1] != STDOUT_FILENO) { //重定向管道输出端到标准输出
if (dup2(pipefd[1], STDOUT_FILENO) != STDOUT_FILENO) {
perror("dup2");
exit(EXIT_FAILURE);
}
close(pipefd[1]);
}

printf("child write to pipe.\n");//现在向标准输出写入,即是向管道写入
printf("child write to pipe.\n");
fflush(stdout);//必须的,若无,上面的写到管道的字符串不会立即发送到管道
write(STDOUT_FILENO, "one ", strlen("one "));
execlp("ls", "ls", "-l", (char *)0); //由于将stdout重定向为管道的输入端,所以这行的执行结果被存放在管道输入端
_exit(127);

} else if(pid > 0) {//parent
close(pipefd[1]);
char buffer[MAXLEN];
int ret;
int nread = 0;

memset(buffer, 0, MAXLEN);
do {
ret = read(pipefd[0], buffer + nread, MAXLEN - 1);
if (ret < 0 && errno == EINTR) {
continue;
} else if (ret > 0) {
nread += ret;
}
}
while ( ret > 0);

printf("parent output: %s", buffer);
printf("\n");

//execlp("wc", "wc", "-l", (char *)0); //这里wc后面没有给出文件名,所以从标准输入读取,而标准输入端被定向为管道输出端
//,所以读取的数据即为上面ls -l的执行结果
} else {
printf("fork error/n");
}

return 0;
}

$ gcc -o test2.1 pipe2.1.c

$ ./test2.1

parent output: child write to pipe.

child write to pipe.

one total 88

-rwxrwxr-x 1 lanyang lanyang 7671 10月 26 21:01 fifo

-rw-rw-r-- 1 lanyang lanyang 1341 10月 26 21:02 fifo.c

-rw-rw-r-- 1 lanyang lanyang 1209 10月 26 21:01 fifo.c~

-rw-rw-r-- 1 lanyang lanyang 1498 10月 25 16:46 notes

-rw-rw-r-- 1 lanyang lanyang 1503 10月 25 10:06 notes~

-rw-rw-r-- 1 lanyang lanyang 1194 10月 26 16:33 pipe1.1.c

-rw-rw-r-- 1 lanyang lanyang 1194 10月 26 16:32 pipe1.1.c~

-rw-rw-r-- 1 lanyang lanyang 1410 10月 26 15:26 pipe1.c

-rw-rw-r-- 1 lanyang lanyang 1426 10月 26 15:22 pipe1.c~

-rw-rw-r-- 1 lanyang lanyang 1751 10月 26 16:49 pipe2.1.c

-rw-rw-r-- 1 lanyang lanyang 1758 10月 26 21:25 pipe2.c

-rw-rw-r-- 1 lanyang lanyang 1738 10月 26 17:23 pipe2.c~

-rwxrwxr-x 1 lanyang lanyang 7770 10月 25 15:30 test

-rwxrwxr-x 1 lanyang lanyang 7623 10月 26 16:33 test1.1

-rwxrwxr-x 1 lanyang lanyang 7674 10月 26 17:23 test2

-rwxrwxr-x 1 lanyang lanyang 7944 10月 26 21:29 test2.1

-rw-rw-r-- 1 lanyang lanyang   70 10月 26 21:21 tmp

若程序改为:

#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>

#define MAXLEN 2048

int main()
{
int pipefd[2];

int ret = pipe(pipefd);
if (ret < 0) {
perror("pipe");
exit(EXIT_FAILURE);
}

int pid = fork();
if (pid < 0) {
perror("fork");
exit(EXIT_FAILURE);
} else if (pid == 0) {//child
close(pipefd[0]); //关闭不需要的端

if (pipefd[1] != STDOUT_FILENO) { //重定向管道输出端到标准输出
if (dup2(pipefd[1], STDOUT_FILENO) != STDOUT_FILENO) {
perror("dup2");
exit(EXIT_FAILURE);
}
close(pipefd[1]);
}

printf("child write to pipe.\n");//现在向标准输出写入,即是向管道写入
printf("child write to pipe.\n");
//fflush(stdout);//必须的,若无,上面的写到管道的字符串不会立即发送到管道
//write(STDOUT_FILENO, "one ", strlen("one "));
execlp("ls", "ls", "-l", (char *)0); //由于将stdout重定向为管道的输入端,所以这行的执行结果被存放在管道输入端
_exit(127);

} else if(pid > 0) {//parent
close(pipefd[1]);
char buffer[MAXLEN];
int ret;
int nread = 0;

memset(buffer, 0, MAXLEN);
do {
ret = read(pipefd[0], buffer + nread, MAXLEN - 1);
if (ret < 0 && errno == EINTR) {
continue;
} else if (ret > 0) {
nread += ret;
}
}
while ( ret > 0);

printf("parent output: %s", buffer);
printf("\n");

//execlp("wc", "wc", "-l", (char *)0); //这里wc后面没有给出文件名,所以从标准输入读取,而标准输入端被定向为管道输出端
//,所以读取的数据即为上面ls -l的执行结果
} else {
printf("fork error/n");
}

return 0;
}

$ gcc -o test2.1 pipe2.1.c

lanyang@lanyang-ThinkPad-Edge-E431:~/exercise/pipe$ ./test2.1

parent output: total 92

-rwxrwxr-x 1 lanyang lanyang 7671 10月 26 21:01 fifo

-rw-rw-r-- 1 lanyang lanyangn 1341 10月 26 21:02 fifo.c

-rw-rw-r-- 1 lanyang lanyang 1209 10月 26 21:01 fifo.c~

-rw-rw-r-- 1 lanyang lanyang 1498 10月 25 16:46 notes

-rw-rw-r-- 1 lanyang lanyang 1503 10月 25 10:06 notes~

-rw-rw-r-- 1 lanyang lanyang 1194 10月 26 16:33 pipe1.1.c

-rw-rw-r-- 1 lanyang lanyang 1194 10月 26 16:32 pipe1.1.c~

-rw-rw-r-- 1 lanyang lanyang 1410 10月 26 15:26 pipe1.c

-rw-rw-r-- 1 lanyang lanyang 1426 10月 26 15:22 pipe1.c~

-rw-rw-r-- 1 lanyang lanyang 2838 10月 26 21:31 pipe2.1.c

-rw-rw-r-- 1 lanyang lanyang 2836 10月 26 21:31 pipe2.1.c~

-rw-rw-r-- 1 lanyang lanyang 1758 10月 26 21:25 pipe2.c

-rw-rw-r-- 1 lanyang lanyang 1738 10月 26 17:23 pipe2.c~

-rwxrwxr-x 1 lanyang lanyang 7770 10月 25 15:30 test

-rwxrwxr-x 1 lanyang lanyang 7623 10月 26 16:33 test1.1

-rwxrwxr-x 1 lanyang lanyang 7674 10月 26 17:23 test2

-rwxrwxr-x 1 lanyang lanyang 7835 10月 26 21:32 test2.1

-rw-rw-r-- 1 lanyang lanyang   70 10月 26 21:21 tmp

会发现使用printf写到管道中的数据丢失了。这是因为printf带有缓冲区,标准输入重定向为管道写断后,变为全缓冲。printf的数据不会立刻放到管道中。

后面再执行exec,输出的数据会将前面的数据覆盖,于是管道中就不会有printf的数据了。这个地方如果使用write则会正常,因为write不带缓冲,直接写到管道中。如果带缓冲区的printf,则fflush一下。

命名管道:

#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

#define FNAME "/tmp/TestFIFO"

int main()
{
char buffer[32] = {0};

//注意下面的0666
//4 2 1分别表示读、写和执行权限
//第一个6表示文件所有者具有读写权限
//第二个6表示同组用户权限
//地三个6表示其他用户读写权限

int mkRet = mkfifo(FNAME, 0666); //mkfifo的第一个参数必须是不存在的文件,执行第二次就会出错

if (mkRet == -1)
{
perror("mkfifo failed\n");
exit(1);
}
int pid = fork();
if (pid < 0) {
perror("fork error");
exit(1);
}
else if (pid == 0) //子进程
{
int fd = open(FNAME, O_RDONLY);
read(fd, buffer, sizeof(buffer)); //read会一直等待可读数据
printf("r content = %s\n", buffer);
close(fd);
}
else if (pid > 0)
{
// wait(NULL);
char s[] = "what a nice day!";
int fd = open(FNAME, O_WRONLY);
size_t len = sizeof(s);
printf("len = %d\n", len);
write(fd, s, sizeof(s));
printf("write over!\n");
close(fd);
}

return 0;
}


$ gcc -o fifo fifo.c

$ ./fifo

len = 17

write over!

r content = what a nice day!
第二次执行,则会出错。

$ ./fifo

mkfifo failed

: File exists

参考

http://blog.csdn.net/wutaozhao/article/details/6010665

http://blog.csdn.net/ljianhui/article/details/10202699
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: