本文属于牛客网上的一个项目笔记:Linux高性能服务器开发课
匿名管道(pipe)
匿名管道只能用于有关系的进程通信(父子,兄弟,祖孙)
创建
#include <unistd.h>
int pipe(int pipefd[2]);
功能:创建匿名管道
传出参数:
pipefd[0] 读端
pipefd[1] 写端
返回值:
0 成功 -1 失败
管道(循环队列)是默认阻塞的,如果管道为空,read阻塞,如果管道满了 wirte阻塞;
查看管道缓冲大小命令
ulimit -a
查看管道缓冲大小函数
long fpathconf(int fd, int name);
long size = fpathconf(pipefd[0],_PC_PIPE_BUF);
用例:
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
int main(){
//在创建子进程之前创建管道。
int pipefd[2];
if(pipe(pipefd) == -1){
perror("pipe");
exit(0);
}
pid_t pid = fork();
if(pid==0){
// 子进程 从管道写数据 写端 pipefd[1]
char buf[1024] = {0};
while (1)
{
char *str = "22222222222222222222";
write(pipefd[1],str,strlen(str));
printf("write over\n");
}
}else if(pid > 0){
//父进程 从管道读端 读数据 pipefd[0]
char buf[1024] = {0};
while(1){
int len = read(pipefd[0],buf,sizeof(buf));
printf("parent pid %d,reve size:%d,date:%s\n",getpid(),len,buf);
}
}
}
实验:利用pipe实现ps aux|grpe xxx
/*分析:
实现ps aux|grpe xxx
子进程实现ps aux并将结果重定向输出到管道
父进程从管道读取数据,过滤后输出到终端
*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <string.h>
#include <unistd.h>
#include <stdio.h>
#include <wait.h>
int main(){
int pipefd[2];
if(pipe(pipefd)==-1){
perror("pipe");
exit(0);
}
pid_t pid = fork();
if(pid==0){
// child
// dp2(fd1,fd2); 将fd2重定向到fd1
dup2(pipefd[1],STDOUT_FILENO);
//执行ps aux
execlp("ps","ps","aux",NULL);
perror("execlp");
exit(0);
}else if(pid > 0){
// parent
close(pipefd[1]);
//读取数据 过滤数据 输出
char buf[1024]={0};
//读数据
// 因为数据很多 所以循环读完,read会返回读取成功的字节数。
// 这里的-1 是减掉字符串的结束符。
int len = -1;
while((len = read(pipefd[0],buf,sizeof(buf)-1)) > 0){
printf("%s",buf);
memset(buf,0,1024);
}
// 回收子进程资源
wait(NULL);
}
return 0;
}
有名管道(fifo)
匿名管道只能用在有亲缘关系的进程之间通信,fork后子进程共享父进程的文件描述符表,故父子进程中都有读写端。
有名管道:以文件的形式存在于系统中,但是文件仅供两个进程之间通信时能够找到同一个文件描述符,并不会向这个文件实体写数据,仍是往内核缓冲区(内存)中写数据。
因此通过这个文件,不相关的进程也能通信。即使使用fifo的进程退出,fifo文件也不会消失,和读写普通文件一样。
原型:
创建:
命令行
mkfifo fifoname
函数
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *pathname,mode_t mode);
参数 -mode:文件权限和open的参数一样
返回值 成功0 失败-1并设置errno
用例:
1、创建fifp
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main(){
char *pathname = "/home/fang/newcodercpp/lesson06/my_fifo";
// 判断是否存在
if(access(pathname,F_OK)==-1){
if(mkfifo(pathname,0664) == -1){
perror("fifo");
exit(0);
}
}
return 0;
}
2、写fifo
// 客户端B
#include <sys/types.h>
#include <time.h>
#include <sys/stat.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
typedef struct MSG{
struct tm p;
char message[1024];
char sender[10];
char rever[10];
} msg;
char *fifo1 = "/home/fang/newcodercpp/lesson07/fifo1";
char *fifo2 = "/home/fang/newcodercpp/lesson07/fifo2";
int main(){
// 打开写管道fifo1
int fifo_w = open(fifo1,O_WRONLY);
//写
time_t timep;
msg write_msg;
while (1) {
time(&timep);
struct tm t = *gmtime(&timep);
write_msg.p = t;
strcpy(write_msg.sender, "xiaohong");
strcpy(write_msg.rever, "Jane");
printf("输入内容:");
printf("发送时间:%d年%d月%d日\n",t.tm_year+1990,t.tm_mon+1,t.tm_mday);
scanf("%s", write_msg.message);
write(fifo_w,&write_msg,sizeof(write_msg));
}
}
3、读fifo
// 客户端A
/*
A与B通信,A和B都将随意的读写。
A创建一个子进程,父进程读,子进程写
B同理
AB通信的消息结构体
struct MSG{
struct tm *p;
char message[1024];
char sender[10];
char rever[10];
...
};
*/
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
typedef struct MSG{
struct tm p;
char message[1024];
char sender[10];
char rever[10];
} msg;
char *fifo1 = "/home/fang/newcodercpp/lesson07/fifo1";
char *fifo2 = "/home/fang/newcodercpp/lesson07/fifo2";
int main(){
// 打开读管道fifo1
int fifo_r = open(fifo1,O_RDONLY);
//读
while(1){
// sleep(1);
// 读整个结构体
msg read_msg;
read(fifo_r,&read_msg,sizeof(read_msg));
printf("发送者:%s 接收者:%s\n",read_msg.sender,read_msg.rever);
printf("发送时间:%d年%d月%d日\n",read_msg.p.tm_year+1990,read_msg.p.tm_mon+1,read_msg.p.tm_mday);
// printf("发送时间:%d年%d月%d日%d时%d分%d秒\n",p->tm_year+1990,p->tm_mon+1,p->tm_mday,p->tm_hour+8,p->tm_min,p->tm_sec);
printf("消息内容:%s\n",read_msg.message);
}
}
注意:
管道:
1、如果管道的写端还有连接,且管道中没有数据,则读端会阻塞;
2、如果管道的写端没有连接(全部关闭),且管道中没有数据,则读端read()返回0,不会阻塞;
3、读端没有连接,这时候写数据,则会产生SIGPIPE信号,不处理的话,进程一般会异常退出
4、读端存在有连接,这时候写数据
设置管道读端pipefd[0]非阻塞
int flags = fcntl(pipefd[0],G_GETFL);// 获取目前的flag
flags |= O_NONBLOCK;
fcntl(pipefd[0],F_SETFL,flags);//设置新的flag