Linux高性能服务器开发:进程通信(管道)

 本文属于牛客网上的一个项目笔记: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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值