进程间通信及管道

Linux 下进程间通信

处于用户态的不同进程之间是彼此隔离。

Linux下的进程通信方式:

  • 管道及有名管道

管道可用于具有亲缘关系进程间的通信,
有名管道,除具有管道所具有的功能外,它还允许无亲缘关系进程间的通信。

  • 信号

信号是在软件层次上对中断机制的一种模拟。

  • 消息队列

消息队列是消息的链接表。具有写权限的进程可以向消息队列中按照一定的规则添加新消息;对消息队列有读权限的进程则可以从消息队列中读取消息。

  • 共享内存

它使得多个进程可以访问同一块内存空间,不同进程可以及时看到对方进程中对共享内存中数据的更新。这种通信方式需要依靠某种同步机制,如互斥锁和信号量等。

  • 信号量

主要作为进程间以及同一进程不同线程之间的同步手段。

  • 套接字(socket)

更为一般的进程间通信机制,它可用于不同机器之间的
进程间通信,应用非常广泛。

管道

管道是基于文件描述符的通信方式,当一个管道建立时,它会创建两个文件描述符fds[0]和fds[1],其中fds[0]固定用于读管道,而fd[1]固定用于写管道。

这样就构成了一个半双工的通道。

关闭管道时,只要close两个文件描述符。

  • 创建管道

SYNOPSIS

#include <unistd.h>

int pipe(int pipefd[2]);
  • 读写管道

fork的子进程也继承了父进程的管道。父子进程分别拥有自己的读写的通道,为了实现父子进程之间的读写,只需把无关的读
端或写端的文件描述符关闭即可。

实例:

#include <unistd.h>
#include <sys/types.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>

int main()
{
    int pipe_fd[2];
    pid_t pid;
    char buf_r[100] = {0};
    char* p_wbuf;
    int r_num;

    if(pipe(pipe_fd) < 0){
        printf("pipe create error\n");
        return -1;
    }

    if((pid = fork()) == 0){
        printf("\n");
        close(pipe_fd[1]);
        /* make sure parent process write over */
        sleep(2);

        if((r_num = read(pipe_fd[0],buf_r,100)) > 0){
            printf("%d numbers read from the pipe is %s\n",r_num,buf_r);
        }

        close(pipe_fd[0]);
        exit(0);
    }
    else if(pid>0){
        close(pipe_fd[0]);
        if(write(pipe_fd[1],"Hello",5) != -1)
            printf("parent write1 success!\n");
        if(write(pipe_fd[1]," Pipe",5) != -1)
            printf("parent write2 success!\n");

        close(pipe_fd[1]);
        sleep(3);

        waitpid(pid,NULL,0);
        exit(0);
    }
}

结果输出:

xxx@xxx-pc:~/Documents$ ./a.out 
parent write1 success!
parent write2 success!

10 numbers read from the pipe is Hello Pipe

标准流管道

标准流管道是基于文件流的管道,由popen函数创建。完成了以下事:

  • 创建一个管道
  • fork一个子进程
  • 在父子进程中关闭不必要的文件描述符
  • 执行exec函数族调用
  • 执行函数中指定的命令

用popen创建的管道必须使用标准I/O函数进行操作,
但不能使用前面的read、write一类不带缓冲的I/O 函数。

SYNOPSIS

#include <stdio.h>

/* command: 指向null结尾的字符串,被送到/bin/sh以-c参数执行
 * type: "r",文件指针连接到command的标准输出;"w",文件指针连接到command的标准输入
 * return: 文件指针
 */
FILE *popen(const char *command, const char *type);

int pclose(FILE *stream);

实例:

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

#define BUFSIZE 1000

int main()
{
    FILE *fp;
    char *cmd = "ps -ef";
    char buf[BUFSIZE] = {0};

    if((fp = popen(cmd,"r")) == NULL)
        perror("popen");
    while((fgets(buf,BUFSIZE,fp)) != NULL)
        printf("%s",buf);
    pclose(fp);
    exit(0);
}

FIFO

有名管道它可以使互不相关的两个进程实现彼此通信。

该管道可以通过路径名来指出,并且在文件系统中是可见的。在建立了管道之后,两个进程就可以把它当作普通文件一样进行读写操作,使用非常方便。

在创建管道成功之后,就可以使用open、read、write这些函数了。

在管道的读写中有阻塞的可能,open以O_NONBLOCK打开管道可以设置为非阻塞模式。

SYNOPSIS

#include <sys/types.h>
#include <sys/stat.h>

int mkfifo(const char *pathname, mode_t mode);

实例:

读进程中创建FIFO

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#define FIFO "/tmp/myfifo"

int main()
{
    int fd;
    char buf[256] = {0};

    unlink(FIFO);
    if(mkfifo(FIFO, 0666) < 0){
        printf("cannot create fifo\n");
    }

    fd = open(FIFO, O_RDONLY | O_NONBLOCK, 0);
    if(fd == -1){
        perror("open");
        exit(1);
    }

    while(1){
        memset(buf, 0, sizeof(buf));
        if(read(fd, buf, sizeof(buf)) == -1){
            if(errno == EAGAIN){
                printf("no data yet\n");
            }
        }
        printf("read %s from FIFO\n", buf);
        sleep(1);
    }
    return 0;
}

另外一个进程,写进程写FIFO

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#define FIFO "/tmp/myfifo"

int main(int argc, char *argv[])
{
    int fd;
    char buf[256] = {0};

    fd = open(FIFO, O_WRONLY | O_NONBLOCK, 0);
    if(fd == -1){
        perror("open");
        if(errno == ENXIO){
            printf("open error; no reading process\n");
        }
        exit(1);
    }

    if(argc != 2){
        return -1;
    }
    memcpy(buf, argv[1], strlen(argv[1]));

    if(write(fd, buf, sizeof(buf)) == -1){
        /* nonblock mod call the block operation */
        if(errno == EAGAIN){
            printf("The FIFO has not been read yet. Please try later\n");
        }
    }else{
        printf("write %s to FIFO\n", buf);
    }
    return 0;
}

结果如下:

先启动读进程,后启动写进程。

FIFO文件
xxx@xxx-pc:~/Documents$ ls -l /tmp/myfifo 
prw-rw-r-- 1 xxx xxx 0 Apr 28 01:37 /tmp/myfifo

读进程
xxx@xxx-pc:~/Documents$ ./read
read  from FIFO
read my-fifo from FIFO

写进程
xxx@xxx-pc:~/Documents$ ./write my-fifo
write my-fifo to FIFO

如果先启动写进程,open FIFO管道时会报错。

xxx@xxx-pc:~/Documents$ ./write my-world2
open: No such device or address
open error; no reading process

转载于:https://www.cnblogs.com/fuluwwa/p/6773308.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值