管道
管道是一种两个进程间进行单向通信的机制。因为管道传递数据的单向性,管道又称为半双工管道。管道只能在具有公共祖先的两个进程之间使用。
每当在管道中键入一个命令序列,让shell执行时,shell都会为每一条命令单独创建一个进程,用管道将前一条命令进程的标准输出与后一条命令的标准输入相连接。
pipe函数
#include <unistd.h>
int pipe(int fd[2]);
由fd返回两个文件描述符,fd[0]为读端,fd[1]为写端。fd[1]的输出是fd[0]的输入。
fstat函数瑞管道的每一段都返回一个FIFO类型的文件描述符,可以用A_ISFIFO宏来测试管道
示例代码:父子进程间通过管道进行通信
#include<stdio.h>
#include<unistd.h>
#include<sys/wait.h>
#include<sys/types.h>
int main()
{
int fd[2];
// 创建匿名管道
pipe(fd);
// fork子进程来读取管道内容,父进程往管道中写入数据
pid_t pid = fork();
if(pid < 0)
{
perror("fork err");
return -1;
}
else if(pid == 0)
{
// 子进程关闭写端
close(fd[1]);
// 重定向读端到标准输入
dup2(fd[0],0);
close(fd[0]);
// 读取
char buf[1024];
fgets(buf,sizeof(buf),stdin);
printf("child get: %s",buf);
return 0;
}
// 父进程关闭读端
close(fd[0]);
// 重定向写端到标准输出
dup2(fd[1],1);
close(fd[1]);
// 通过标准输出给管道发送数据
printf("give you something\n");
// 回收子进程
while(waitpid(-1,NULL,WNOHANG) > 0)
{
;
}
return 0;
}
命名管道FIFO
未命名管道只能在两个相关的进程之间使用,而且这两个相关的进程还要有一个共同创建了他们的祖先进程。但是通过FIFO不相关的进程也能交换数据。
创建fifo
#include <sys/stat.h>
int mkfifo(const char *path, mode_t mode);
shell命令: mkfifo "fifoname"
当open一个FIFO时需要注意
在没有指定O_NONBLOCK的情况下(默认没有),只读open要阻塞到某个其他的进程以写方式打开这个FIFO为之。同理只写open也会阻塞到某个其他进程以读方式打开为止。
如果指定了O_NONBLOCK,则只读open立即返回,而只写open在没有一个对应的读端打开的情况下会返回-1,并且设置errno为ENXIO。
示例代码:模拟两个进程聊天
#include<stdio.h>
#include<fcntl.h>
#include<string.h>
#include<signal.h>
#include<sys/wait.h>
#include<unistd.h>
#include<sys/types.h>
void handle(int arg)
{
int status;
// 回收子进程同时退出程序
pid_t pid = waitpid(-1,&status,WNOHANG);
if(pid > 0)
{
printf("对方已离线\n");
exit(0);
}
}
int main(int argc, char* argv[])
{
// 保存用户姓名
printf("Please enter your name: ");
char name[64];
memset(name,0,sizeof(name));
fgets(name,sizeof(name),stdin);
// 创建信号捕捉函数,捕捉子进程退出信号,因为当对方关闭进程时已经无人进行交互了
// 则自己也调用捕捉函数退出
signal(SIGCHLD,handle);
int fd1, fd2;
// 通过命令行参数0和1简单判断两个不同的人
// 主要为区分开读写端的打开操作
// 两次打开程序分别设置1和0进行通信
if(argv[1][0] == '0')
{
fd1 = open("fifo1",O_RDONLY);
fd2 = open("fifo2",O_WRONLY);
}
else if(argv[1][0] == '1')
{
fd2 = open("fifo1",O_WRONLY);
fd1 = open("fifo2",O_RDONLY);
}
char buf[1024];
memset(buf,0,sizeof(buf));
pid_t pid = fork();
if(pid == 0)
{
// 关闭写端
close(fd2);
while(1)
{
if(read(fd1,buf,sizeof(buf)) == 0)
{
break;
}
printf("%s",buf);
}
printf("child exit!\n");
return 0;
}
// 关闭读端
close(fd1);
while(1)
{
fgets(buf,sizeof(buf),stdin);
// 判断输入,如果没有输入则跳过,判断标准为1是因为回车键
if(strlen(buf) <= 1)
{
continue;
}
// 将用户姓名按(name: 数据)格式发送
write(fd2,name,strlen(name)-1);
write(fd2,": ",2);
write(fd2,buf,strlen(buf)+1);
}
return 0;
}