管道(一)
一、
q 我们把从一个进程连接到另一个进程的一个数据流称为一个“管道”
q 管道是半双工的,数据只能向一个方向流动;需要双方通信时,需要建立起两个管道
q 只能用于具有共同祖先的进程(具有亲缘关系的进程)之间进行通信;通常,一个管道由一个进程创建,然后该进程调用fork,此后父、子进程之间就可应用该管道。
二、匿名管道pipe
q 包含头文件<unistd.h>
q 功能:创建一无名管道
q 原型
q intpipe(int fd[2]);
q 参数
q fd:文件描述符数组,其中fd[0]表示读端, fd[1]表示写端
q 返回值:成功返回0,失败返回错误代码
//01pipe.c
#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>
#defineERR_EXIT(m) \
do \
{ \
perror(m); \
exit(EXIT_FAILURE); \
} while(0)
intmain(int argc, char *argv[])
{
int pipefd[2];
if (pipe(pipefd) == -1)
ERR_EXIT("pipeerror");
pid_t pid;
pid = fork();
if (pid == -1)
ERR_EXIT("forkerror");
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);
printf("buf=%s\n", buf);
return 0;
}
//02pipe.c
#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>
#defineERR_EXIT(m) \
do \
{ \
perror(m); \
exit(EXIT_FAILURE); \
} while(0)
intmain(int argc, char *argv[])
{
int pipefd[2];
if (pipe(pipefd) == -1)
ERR_EXIT("pipeerror");
pid_t pid;
pid = fork();
if (pid == -1)
ERR_EXIT("forkerror");
if (pid == 0)
{
dup2(pipefd[1],STDOUT_FILENO);
close(pipefd[1]);
close(pipefd[0]);
execlp("ls","ls", NULL);
fprintf(stderr, "errorexecute ls\n");
exit(EXIT_FAILURE);
}
dup2(pipefd[0], STDIN_FILENO);
close(pipefd[0]);
close(pipefd[1]);
execlp("wc", "wc","-w", NULL);
fprintf(stderr, "error executewc\n");
exit(EXIT_FAILURE);
}
//03cp.c
#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>
#defineERR_EXIT(m) \
do \
{ \
perror(m); \
exit(EXIT_FAILURE); \
} while(0)
intmain(int argc, char *argv[])
{
int pipefd[2];
if (pipe(pipefd) == -1)
ERR_EXIT("pipeerror");
pid_t pid;
pid = fork();
if (pid == -1)
ERR_EXIT("forkerror");
if (pid == 0)
{
dup2(pipefd[1],STDOUT_FILENO);
close(pipefd[1]);
close(pipefd[0]);
execlp("ls","ls", NULL);
fprintf(stderr, "errorexecute ls\n");
exit(EXIT_FAILURE);
}
dup2(pipefd[0], STDIN_FILENO);
close(pipefd[0]);
close(pipefd[1]);
execlp("wc", "wc","-w", NULL);
fprintf(stderr, "error executewc\n");
exit(EXIT_FAILURE);
}
管道(二)
一、当没有数据可读时
q O_NONBLOCKdisable:read调用阻塞,即进程暂停执行,一直等到有数据来到为止。
q O_NONBLOCKenable:read调用返回-1,errno值为EAGAIN。
#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>
#defineERR_EXIT(m) \
do \
{ \
perror(m); \
exit(EXIT_FAILURE); \
} while(0)
intmain(int argc, char *argv[])
{
int pipefd[2];
if (pipe(pipefd) == -1)
ERR_EXIT("pipeerror");
pid_t pid;
pid = fork();
if (pid == -1)
ERR_EXIT("forkerror");
if (pid == 0)
{
sleep(3);
close(pipefd[0]);
write(pipefd[1],"hello", 5);
close(pipefd[1]);
exit(EXIT_SUCCESS);
}
close(pipefd[1]);
char buf[10] = {0};
int flags = fcntl(pipefd[0], F_GETFL);
fcntl(pipefd[0], F_SETFL, flags |O_NONBLOCK);
int ret = read(pipefd[0], buf, 10);
if (ret == -1)
ERR_EXIT("readerror");
printf("buf=%s\n", buf);
return 0;
}
二、当管道满的时候
q O_NONBLOCKdisable: write调用阻塞,直到有进程读走数据
q O_NONBLOCKenable:调用返回-1,errno值为EAGAIN
三、如果所有管道写端对应的文件描述符被关闭,则read返回0
#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>
#defineERR_EXIT(m) \
do \
{ \
perror(m); \
exit(EXIT_FAILURE); \
} while(0)
intmain(int argc, char *argv[])
{
int pipefd[2];
if (pipe(pipefd) == -1)
ERR_EXIT("pipeerror");
pid_t pid;
pid = fork();
if (pid == -1)
ERR_EXIT("forkerror");
if (pid == 0)
{
close(pipefd[1]);
exit(EXIT_SUCCESS);
}
close(pipefd[1]);
sleep(1);
char buf[10] = {0};
int ret = read(pipefd[0], buf, 10);
printf("ret = %d\n", ret);
return 0;
}
四、如果所有管道读端对应的文件描述符被关闭,则write操作会产生信号SIGPIPE
#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>
#defineERR_EXIT(m) \
do \
{ \
perror(m); \
exit(EXIT_FAILURE); \
} while(0)
voidhandler(int sig)
{
printf("recv a sig=%d\n",sig);
}
intmain(int argc, char *argv[])
{
signal(SIGPIPE, handler);
int pipefd[2];
if (pipe(pipefd) == -1)
ERR_EXIT("pipe error");
pid_t pid;
pid = fork();
if (pid == -1)
ERR_EXIT("forkerror");
if (pid == 0)
{
close(pipefd[0]);
exit(EXIT_SUCCESS);
}
close(pipefd[0]);
sleep(1);
int ret = write(pipefd[1],"hello", 5);
if (ret == -1)
printf("writeerror\n");
return 0;
}
五、当要写入的数据量不大于PIPE_BUF时,linux将保证写入的原子性。
当要写入的数据量大于PIPE_BUF时,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>
#defineERR_EXIT(m) \
do \
{ \
perror(m); \
exit(EXIT_FAILURE); \
} while(0)
intmain(int argc, char *argv[])
{
int pipefd[2];
if (pipe(pipefd) == -1)
ERR_EXIT("pipe error");
int ret;
int count = 0;
int flags = fcntl(pipefd[1], F_GETFL);
fcntl(pipefd[1], F_SETFL, flags |O_NONBLOCK);
while (1)
{
ret = write(pipefd[1],"A", 1);
if (ret == -1)
{
printf("err=%s\n",strerror(errno));
break;
}
count++;
}
printf("count=%d\n", count);
return 0;
}
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<sys/types.h>
#include<errno.h>
#include<fcntl.h>
#defineERR_EXIT(m) \
do \
{ \
perror(m); \
exit(EXIT_FAILURE); \
} while(0)
#defineTEST_SIZE 68*1024
intmain(void)
{
char a[TEST_SIZE];
char b[TEST_SIZE];
memset(a, 'A', sizeof(a));
memset(b, 'B', sizeof(b));
int pipefd[2];
int ret = pipe(pipefd);
if (ret == -1)
ERR_EXIT("pipeerror");
pid_t pid;
pid = fork();
if (pid == 0)
{
close(pipefd[0]);
ret = write(pipefd[1], a,sizeof(a));
printf("apid=%d write %dbytes to pipe\n", getpid(), ret);
exit(0);
}
pid = fork();
if (pid == 0)
{
close(pipefd[0]);
ret = write(pipefd[1], b,sizeof(b));
printf("bpid=%d write %dbytes to pipe\n", getpid(), ret);
exit(0);
}
close(pipefd[1]);
sleep(1);
int fd = open("test.txt",O_WRONLY | O_CREAT | O_TRUNC, 0644);
char buf[1024*4] = {0};
int n = 1;
while (1)
{
ret = read(pipefd[0], buf,sizeof(buf));
if (ret == 0)
break;
printf("n=%02d pid=%dread %d bytes from pipe buf[4095]=%c\n", n++, getpid(), ret, buf[4095]);
write(fd, buf, ret);
}
return 0;
}
管道(三)
一、命名管道
q 管道应用的一个限制就是只能在具有共同祖先(具有亲缘关系)的进程间通信。
q 如果我们想在不相关的进程之间交换数据,可以使用FIFO文件来做这项工作,它经常被称为命名管道。
q 命名管道是一种特殊类型的文件
二、创建一个命名管道
q 命名管道可以从命令行上创建,命令行方法是使用下面这个命令:
$ mkfifo filename
q 命名管道也可以从程序里创建,相关函数有:
int mkfifo(const char *filename,mode_tmode);
一、匿名管道与命名管道
q 命名管道可以从命令行上创建,命令行方法是使用下面这个命令:
$ mkfifo filename
q 命名管道也可以从程序里创建,相关函数有:
int mkfifo(const char *filename,mode_tmode);
二、命名管道的打开规则
q 如果当前打开操作是为读而打开FIFO时
q O_NONBLOCKdisable:阻塞直到有相应进程为写而打开该FIFO
q O_NONBLOCKenable:立刻返回成功
q 如果当前打开操作是为写而打开FIFO时
q O_NONBLOCKdisable:阻塞直到有相应进程为读而打开该FIFO
q O_NONBLOCKenable:立刻返回失败,错误码为ENXIO
//02fifor.c
#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>
#defineERR_EXIT(m) \
do \
{ \
perror(m); \
exit(EXIT_FAILURE); \
} while(0)
intmain(int argc, char *argv[])
{
int fd;
/* fd = open("p1", O_RDONLY);*/
fd = open("p1", O_RDONLY |O_NONBLOCK);
if (fd == -1)
ERR_EXIT("openerror");
printf("open succ\n");
return 0;
}
//03fifow.c
#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>
#defineERR_EXIT(m) \
do \
{ \
perror(m); \
exit(EXIT_FAILURE); \
} while(0)
intmain(int argc, char *argv[])
{
int fd;
/*fd = open("p1",O_WRONLY);*/
fd = open("p1", O_WRONLY |O_NONBLOCK);
if (fd == -1)
ERR_EXIT("openerror");
printf("open succ\n");
return 0;
}
三、命名管道示例程序
//04fifor.c
#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>
#defineERR_EXIT(m) \
do \
{ \
perror(m); \
exit(EXIT_FAILURE); \
} while(0)
intmain(int argc, char *argv[])
{
mkfifo("tp", 0644);
int infd;
infd = open("Makefile",O_RDONLY);
if (infd == -1)
ERR_EXIT("open");
int outfd;
outfd = open("tp", O_WRONLY);
if (outfd == -1)
ERR_EXIT("open");
char buf[1024];
int n;
while ((n=read(infd, buf, 1024))>0)
{
write(outfd, buf, n);
}
close(infd);
close(outfd);
return 0;
}
//05fifow.c
#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>
#defineERR_EXIT(m) \
do \
{ \
perror(m); \
exit(EXIT_FAILURE); \
} while(0)
intmain(int argc, char *argv[])
{
int outfd;
outfd = open("Makefile2",O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (outfd == -1)
ERR_EXIT("open");
int infd;
infd = open("tp", O_RDONLY);
if (outfd == -1)
ERR_EXIT("open");
char buf[1024];
int n;
while ((n=read(infd, buf, 1024))>0)
{
write(outfd, buf, n);
}
close(infd);
close(outfd);
unlink("tp");
return 0;
}