进程间通信

1. 知识点

进程间通信_第1张图片

这些通信方式各有各的特点,无名管道是最简单的常用于一对一的亲缘进程间通信的方

式,有名管道存在于文件系统之中,提供写入原子性特征,信号是唯一一种异步通信方式, 共享内存的效率最高,但是要结合信号量等同步互斥机制一起使用,消息队列提供一种带简 单消息标识的通信方式,套接字是一种更为宽泛意义上的进程间通信方式——它允许进程间 跨网络。

2. 管道

常说的管道通常指无名管道 (PIPE) 或者有名管道 (FIFO) ,但实际上套接字也都是 管道。这里先把 PIPE 和 FIFO 的相关接口摆出:

进程间通信_第2张图片

2.1 无名管道

先来罗列 PIPE 的特征:

1,没有名字,因此无法使用 open( )。

2,只能用于亲缘进程间 (比如父子进程、兄弟进程、祖孙进程……) 通信。

3,半双工工作方式:读写端分开。

4,写入操作不具有原子性,因此只能用于一对一的简单通信情形。

5,不能使用 lseek( )来定位。

进程间通信_第3张图片

pipe.c

#include
#include
#include
#include
#include

//无名管道的使用
int main()
{
    //创建一个数组,来装管道的读写端
    int fd[2];

    //创建无名管道
    int ret = pipe(fd);
    if(ret==-1){
        perror("pipe()");
        return -1;
    }

    //定义读写缓冲区
    char wbuf[128];
    char rbuf[128];

    
    //创建一个子进程
    pid_t pid = fork();

    if(pid==0){//子进程
        //从管道读数据
        read(fd[0],rbuf,sizeof(rbuf)-1);
        printf("你爹说:%s\n",rbuf);
        
    }
    if(pid>0){//父进程
        //往管道写数据
        fgets(wbuf,sizeof(wbuf),stdin);//从键盘获取输入的字符串
        write(fd[1],wbuf,strlen(wbuf));
    }



    return 0;
}

2.2 有名管道

有名管道 FIFO 的特征:

1,有名字,存储于普通文件系统之中。

2,任何具有相应权限的进程都可以使用 open( )来获取 FIFO 的文件描述符。

3,跟普通文件一样:使用统一的 read( )/write( )来读写。

4,跟普通文件不同:不能使用 lseek( )来定位,原因同 PIPE。

5,具有写入原子性,支持多写者同时进行写操作而数据不会互相践踏。

6,First In First Out,最先被写入 FIFO 的数据,最先被读出来。

rose.c

#include
#include
#include
#include
#include
#include
#include

//有名管道进程间通信

int main()
{

    //access检测文件是否存在,F_OK,R_OK,W_OK,X_OK
    if(access("/home/lsf/jincheng_course/myfifo",F_OK)==-1){//mufifo不存在则创建管道
         //创建一个有名管道
        int ret_fifo = mkfifo("/home/lsf/jincheng_course/myfifo",0777);
        if(ret_fifo==-1){
            perror("file exist");
        }
    }


    //打开文件
    int fd = open("/home/lsf/jincheng_course/myfifo",O_RDONLY);
    if(fd==-1){
        perror("open myfifo failed");
        return -1;
    }

    char buf[128];

    //读文件
    while(1){
        memset(buf,0,sizeof(buf));//清空
        read(fd,buf,sizeof(buf)-1);

        printf("[from jack]:%s\n",buf);

        if(strcmp(buf,"bye\n")==0){
            break;
        }

    }
    
    close(fd);

    return 0;
}

jack.c

#include
#include
#include
#include
#include
#include
#include

//有名管道进程间通信

int main()
{

    //access检测文件是否存在,F_OK,R_OK,W_OK,X_OK
    if(access("/home/lsf/jincheng_course/myfifo",F_OK)==-1){//mufifo不存在则创建管道
         //创建一个有名管道
        int ret_fifo = mkfifo("/home/lsf/jincheng_course/myfifo",0777);
        if(ret_fifo==-1){
            perror("file exist");
        }
    }

    //打开文件
    int fd = open("/home/lsf/jincheng_course/myfifo",O_WRONLY);
    if(fd==-1){
        perror("open myfifo failed");
        return -1;
    }

    char buf[128];

    //写文件
    while(1){

        fgets(buf,sizeof(buf),stdin);

        write(fd,buf,sizeof(buf)-1);

        if(strcmp(buf,"bye\n")==0){//因为我们输入完bye后还会输入一个回车会被fgets读取到,所以为了避免错我们加一个回车
            break;
        }

    }
    
    close(fd);

    return 0;
}

运行步骤

因为windows不支持管道文件,所以不能再共享文件夹中运行

我们需要复制一份代码到别的目录,这了我复制到用户目录下的jincheng_course

进程间通信_第4张图片

编译运行两个文件

进程间通信_第5张图片

再开辟一个新的窗口运行jack

老窗口运行rose

进程间通信_第6张图片

输入bye退出

这里补充知识点

1. access检测文件是否存在

2. 使用fgets读取输入,最后需要回车,因为fgets会读取到回车所以我们在比较退出代码时要留意 比如if(strcmp(buf,"bye\n")==0)

2.3  自我实现日志服务器例程

进程间通信_第7张图片

my_log_write.c

#include
#include
#include
#include
#include
#include
#include
#include

//自我实现日志服务器例程

int main()
{

    //判断管道文件是否存在
    if(access("/home/lsf/jincheng_course/logfifo",F_OK)==-1){//如果不存在
        //创建有名管道文件
        int ret_fifo = mkfifo("/home/lsf/jincheng_course/logfifo",0777);
        if(ret_fifo==-1){
            perror("file exist");
        }
    }

    //打开管道文件
    int fd = open("/home/lsf/jincheng_course/logfifo",O_WRONLY);
    if(fd==-1){
        perror("open logfifo failed");
        return -1;
    }

    char buf[128];//定义缓冲区
    // char tbuf[64];//定义时间缓冲区

    int count = 0;//写30次不写了

    while(1){

        //获取进程号
        int pid = getpid();

        //获取时间方法一
        // time_t current_time;
        // struct tm* time_info;
        // time(¤t_time);//获取时间
        // time_info = localtime(¤t_time);//函数将时间信息转换为本地时间结构
        // // 格式化当前时间为字符串
        // strftime(tbuf, sizeof(tbuf), "%Y-%m-%d %H:%M:%S", time_info);

        //获取时间方法二
        time_t curTime;
        sleep(1);
        curTime++;
        time(&curTime);
        char* curData = ctime(&curTime);

        //拼接时间和进程号
        snprintf(buf,sizeof(buf),"this time:%s pid = %d\n",curData,pid);

        //写文件
        write(fd,buf,strlen(buf));
        printf("%s\n",buf);

        count++;
        if(count==30){
            break;
        }
    }

    close(fd);


    return 0;
}

my_log_read.c

#include
#include
#include
#include
#include
#include
#include
#include

//自我实现日志服务器例程

int main()
{

    //判断管道文件是否存在
    if(access("/home/lsf/jincheng_course/logfifo",F_OK)==-1){//如果不存在
        //创建有名管道文件
        int ret_fifo = mkfifo("/home/lsf/jincheng_course/logfifo",0777);
        if(ret_fifo==-1){
            perror("file exist");
        }
    }

    //打开管道文件
    int fd = open("/home/lsf/jincheng_course/logfifo",O_RDONLY);
    if(fd==-1){
        perror("open logfifo failed");
        return -1;
    }

    //创建日志文件
    int fd2 = open("/home/lsf/jincheng_course/sys_log.txt",O_RDWR | O_CREAT,0777);
    if(fd2==-1){
        perror("open sys_log.txt failed");
        return -1;
    }

    char buf[128];//定义缓冲区

    int count = 0;//读30次不读了

    while(1){
        //读出管道文件
        memset(buf,0,sizeof(buf));//清空

        read(fd,buf,sizeof(buf)-1);
        printf("%s\n",buf);//打印

        //写入日志文件
        write(fd2,buf,strlen(buf));

        count++;
        if(count==30){
            break;
        }
    }

    close(fd);


    return 0;
}

运行代码

一样复制到用户目录中运行

编译运行

再开辟三个窗口

运行./write

进程间通信_第8张图片

最后生成日志文件

进程间通信_第9张图片

进程间通信_第10张图片

你可能感兴趣的:(进程线程,c语言,linux,算法)