多进程通信(IPC)--匿名管道和命名管道

Unix进程之间的通信主要有四种:管道,共享内存,消息队列,套接字。

1、匿名管道

       匿名管道是一种未命名的、单向管道,通常用来在一个父进程和一个子进程之间传输数据。匿名的管道只能实现本地机器上两个进程间的通信,而不能实现跨网络的通信。使用pipes函数来创建管道:

    int pipe(int filedes[2]);  

该函数通过参数返回两个文件描述符,filedes[0] 用于读,filedes[1] 用于写,事实上,从 filedes[0] 中读出的数据即是向 filedes[1] 中写入的数据。

匿名管道比较简单,看一个例子,代码就不解释了

#include <stdio.h>
#include <unistd.h>

#define MAX_SIZE 20
#define SUB_COUNT 3
int main()
#include <stdio.h>
#include <unistd.h>

#define MAX_SIZE 20
#define SUB_COUNT 3
int main()
{
    int fd[2];
    char w_buf[MAX_SIZE] = {0};
    char r_buf[MAX_SIZE] = {0};
    pid_t pid;

    if (pipe(fd) < 0)
        printf("pipe error\n");

    int i = 0;
    for (; i < SUB_COUNT; ++i)
    {
        if ((pid = fork()) < 0)
        {
            printf("fork error\n");
        }
        else if (pid == 0)
        {
            close(fd[0]);
            memset(w_buf, 0, sizeof(w_buf));
            sprintf(w_buf, "%d", getpid());
            int wc = write(fd[1], w_buf, MAX_SIZE);
            exit(0);
        }
        else
        {
            memset(r_buf, 0, sizeof(r_buf));
            int rd = read(fd[0], r_buf, MAX_SIZE*SUB_COUNT);
            printf("sub = %d, selfpid = %d-->%s\n", pid, getpid(), r_buf);
        }
    }

    exit(0);

    return 0;
}

2、命名管道(FIFO)
    FIFO不同于管道之处在于它提供一个路径名与之关联,以FIFO的文件形式存在于文件系统中。

具有如下属性

与FIFO的创建进程不存在亲缘关系的进程,只要可以访问该路径,就能够彼此通过FIFO相互通信;

FIFO严格遵循先进先出(first in first out),对管道及FIFO的读总是从开始处返回数据,对它们的写则把数据添加到末尾。

③读写类似文件操作,但不支持诸如lseek()等文件定位操作。

创建函数

int mkfifo(const char * pathname, mode_t mode)

       该函数的第一个参数是一个普通的路径名,也就是创建后FIFO的名字。第二个参数与打开普通文件的open()函数中的mode 参数相同。如果mkfifo的第一个参数是一个已经存在的路径名时,会返回EEXIST错误,所以一般典型的调用代码首先会检查是否返回该错误,如果确实返回该错误,那么只要调用打开FIFO的函数就可以了。一般文件的I/O函数都可以用于FIFO,如close、read、write等。

先看代码

读的一端代码:

#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>

#define BUF_SIZE 128
int main(int argc, char *argv[])
{
    char filename[50] = {0};
    strcpy(filename, argv[1]);
    char r_buf[BUF_SIZE] = {0};

    //int fd = open(filename, O_RDONLY|O_NONBLOCK,0);
    int fd = open(filename, O_RDONLY,0);
    if (fd < 0)
    {
        printf("open error fd = %d\n", fd);
        return 0;
    }

    while (1)
    {
        int wc = read(fd, r_buf, BUF_SIZE);
        if (wc <= 0)
        {
            printf("read error\n");
            break;
        }
        printf("read counts = %d, buf is %s \n", wc, r_buf);
    }

    return  0;
}
写的一端代码:

#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>

#define BUF_SIZE 128
int main(int argc, char *argv[])
{
    char filename[50] = {0};
    strcpy(filename, argv[1]);
    char w_buf[BUF_SIZE] = {0};

    if (mkfifo(filename, O_CREAT|O_EXCL) < 0 && errno != EEXIST)
        printf("create %s error\n", filename);

    //int fd = open(filename, O_WRONLY|O_NONBLOCK,0);
    int fd = open(filename, O_WRONLY);
    if (fd < 0)
    {
        if(errno==ENXIO)
            printf("open error; no reading process\n");
        printf("open error fd = %d\n", fd);
        return 0;
    }
    strcpy(w_buf, "fdslfl dfjdlfj");

    int i = 0;
    for (; i < 5; ++i)
    {
        char tem[BUF_SIZE] = {0};
        sprintf(tem, "%s+%d", w_buf, i);
        int wc = write(fd, tem, BUF_SIZE);
        if (wc < 0)
        {
            printf("write error\n");
        }
        printf("write counts %d \n", wc);
    }

    return  0;
}

运行结果
写的一端:
write counts 128 
write counts 128 
write counts 128 
write counts 128 
write counts 128 
读的一端:
read counts = 128, buf is fdslfl dfjdlfj+0 
read counts = 128, buf is fdslfl dfjdlfj+1 
read counts = 128, buf is fdslfl dfjdlfj+2 
read counts = 128, buf is fdslfl dfjdlfj+3 
read counts = 128, buf is fdslfl dfjdlfj+4 
read error
分析代码:
①写的一端:先打开并创建一个命名管道,即存在磁盘的文件,查看文件有没有读写权限,可通过mkfifo -m 0644 fifo_server,修改权限,管道的写端打开管道可以设置成阻塞模式,即内核会检查有没有读的一端打开该管道,如果没有则一直阻塞。也可设置成非阻塞模式,即打开后就返回。
②读的一端:打开管道也有阻塞和非阻塞模式,阻塞模式下,需要内核检查到有写该管道的一端才返回,否则一直阻塞,非阻塞是open立即返回
③为了保证读端和写端没有出现缓存大小也引起取值错误,一般声明一个同样大小的缓冲区解决。






匿名管道命名管道都是用于进程通信的方式,但它们之间有一些区别。 匿名管道匿名管道是一种单向通信方式,只能用于父子进程或者兄弟进程之间的通信。在创建匿名管道时,操作系统会自动为其分配一个文件描述符,通过该文件描述符,进程可以进行读操作。 匿名管道的创建方式如下: ```C int pfd[2]; pipe(pfd); // 创建匿名管道 ``` 其中,pfd[0]是管道的读端,pfd[1]是管道端。 匿名管道的使用方式如下: ```C char buf[1024]; pid_t pid; int pfd[2]; pipe(pfd); pid = fork(); if (pid == 0) { // 子进程 close(pfd[1]); // 关闭端 read(pfd[0], buf, sizeof(buf)); // 读取数据 printf("child process read from pipe: %s\n", buf); close(pfd[0]); // 关闭读端 } else if (pid > 0) { // 父进程 close(pfd[0]); // 关闭读端 write(pfd[1], "hello world", strlen("hello world")); // 入数据 close(pfd[1]); // 关闭端 } else { perror("fork"); exit(1); } ``` 命名管道命名管道也是一种单向通信方式,但可以用于任意进程之间的通信。在创建命名管道时,需要指定一个路径名,并且需要手动创建该文件。操作系统会为其分配一个文件描述符,通过该文件描述符,进程可以进行读操作。 命名管道的创建方式如下: ```C int fd; mkfifo("/tmp/myfifo", 0666); // 创建命名管道 fd = open("/tmp/myfifo", O_RDONLY); // 打开命名管道 ``` 其中,"/tmp/myfifo"是文件路径名,0666是文件权限。 命名管道的使用方式如下: ```C char buf[1024]; int fd; fd = open("/tmp/myfifo", O_RDONLY); read(fd, buf, sizeof(buf)); // 读取数据 printf("read from fifo: %s\n", buf); close(fd); ``` 需要注意的是,命名管道入操作可以在任意进程中进行,只要有权限打开该文件即可。如果多个进程同时入数据到同一个命名管道,可能会导致数据混乱。因此,使用命名管道时需要特别注意数据的同步问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值