进程间通讯

进程间通讯是进程间数据传输 数据共享 进程控制 时间通知 , 根据需求的不同,操作系统提供多种不同的进程间通信方式

进程通讯–提供一个公共的媒介

管道

linux管道继承于unix:匿名管道/命名管道
管道是一个半双工通信: 提供双向选择,但是只能单向传输
管道创建成功后,提供一个io操作:返回文件描述符作为句柄
句柄(文件描述符)有两个:
一个用于读取数据,一个用于写入数据

匿名管道只能用于有血缘关系的进程间通讯
命名管道可以用于任意的进程间通讯

原理: 操作系统在内核中提供一块缓冲区(只要进程能够访问到这块缓冲区就可以实现通信)
匿名管道
通过子进程复制父进程的方式(复制文件描述符)实现通信

#include <unistd.h>
功能:创建一无名管道
原型
int pipe(int fd[2]);
参数
fd:文件描述符数组,其中fd[0]表示读端, fd[1]表示写端
返回值: 成功返回0.失败返回错误代码

读写特性:
管道中没有数据,则 read 会阻塞,直到读取到数据(有数据写入到管道了)
管道中若数据满了,则 write 会触发异常–SIGPIPE(导致进程退出)
若所有写端被关闭,则read读完数据后不会阻塞,直接返回0
进程在操作管道时,如果没有用到某一段,则把这一端关闭关闭

  • 当没有数据可读时
  • O_NONBLOCK disable:read调用阻塞,即进程暂停执行,一直等到有数据来到为止。
  • O_NONBLOCK enable:read调用返回-1,errno值为EAGAIN。
  • 当管道满的时候
  • O_NONBLOCK disable:write调用阻塞,直到有进程读走数据
  • O_NONBLOCK enable:调用返回-1,errno值为EAGAIN
  • 如果所有管道写端对应的文件描述符被关闭,则read返回0
  • 如果所有管道读端对应的文件描述符被关闭,则write操作会产生信号SIGPIPE,进而可能导致write进程 退出
  • 当要写入的数据量不大于PIPE_BUF时,linux将保证写入的原子性。
  • 当要写入的数据量大于PIPE_BUF时,linux将不再保证写入的原子性

管道特点:

  • 通常情况下一个管道由一个进程创建,然后该进程调用fork. 由此父, 子进程之间就可以应用该管道
  • 管道提供流式服务
  • 一般情况下进程退出,管道退出,管道声明周期随进程
  • 内核会对管道操作进行同步与互斥
  • 管道是半双工通讯,数据只能向一个方向流动; 需要双方通信时,需要建立两个管道
    管道符: ’ | ’ ps -ef | gerp pipe
    shell – 父进程
    ps : —子进程1 -> 将处理结果打印到标准输出–标准输出重定向
    grep – 子进程2 -> 循环从标准输入读取数据–标准输入重定向

命名管道
命名管道可在命令行上创建

$ mkfifo filename

同时也可在程序中创建

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

int main(int argc, char *argv[])
{
mkfifo("p2", 0644);
return 0;
}

匿名管道与命名管道的区别:

  • 匿名管道用pipe函数创建
  • 命名管道用 mkfifo 创建,打开用open
  • FIFO(命名管道) 与pipe (匿名管道) 之间唯一的区别就是创建于打开方式不同, 之后的操作相同

命名管道的打开规则:

  • 如果当前打开操作是为读而打开FIFO
    O_NONBLOCK disable:阻塞直到有相应进程为写而打开该FIFO
    O_NONBLOCK enable:立刻返回成功
  • 如果当前打开操作是为写而打开FIFO时
    O_NONBLOCK disable:阻塞直到有相应进程为读而打开该FIFO
    O_NONBLOCK enable:立刻返回失败,错误码为ENXIO

共享内存
共享内存是最快的IPC(Interprocess communication)形式,一旦这样的内存映射到共享它的进程的地址空间,这些进程间数据传递不再涉及到内核, 即进程不再通过执行进入内核的系统调用来传递数据
在这里插入图片描述
操作步骤:

  1. 创建共享内存 shmget
  2. 将共享内存映射到虚拟地址空间 shmat
  3. 直接对这块内存进行操作 memcpy
  4. 接触映射关系 shmdt
  5. 删除共享内存 shmctl

进程间通信方式的查看: ipcs [-m/-s/-q] shmid
进程间通信方式的删除: Ipcrm [-m/-s/-q] shmid

共享内存函数
shmget函数

功能:用来创建共享内存
原型
int shmget(key_t key, size_t size, int shmflg);
参数
key:这个共享内存段名字
size:共享内存大小
shmflg:由九个权限标志构成,它们的用法和创建文件时使用的mode模式标志是一样的
返回值:成功返回一个非负整数,即该共享内存段的标识码;失败返回-1

shmat函数

功能:将共享内存段连接到进程地址空间
原型
void *shmat(int shmid, const void *shmaddr, int shmflg);
参数
shmid: 共享内存标识
shmaddr:指定连接的地址
shmflg:它的两个可能取值是SHM_RND和SHM_RDONLY
返回值:成功返回一个指针,指向共享内存第一个节;失败返回-1

shmaddr为NULL,核心自动选择一个地址
shmaddr不为NULL且shmflg无SHM_RND标记,则以shmaddr为连接地址。
shmaddr不为NULL且shmflg设置了SHM_RND标记,则连接的地址会自动向下调整为SHMLBA的整数倍。公式:shmaddr - (shmaddr % SHMLBA)
shmflg=SHM_RDONLY,表示连接操作用来只读共享内存

shmdt函数

功能:将共享内存段与当前进程脱离
原型
int shmdt(const void *shmaddr);
参数
shmaddr: 由shmat所返回的指针
返回值:成功返回0;失败返回-1
注意:将共享内存段与当前进程脱离不等于删除共享内存段

shmctl函数

功能:用于控制共享内存
原型
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
参数
shmid:由shmget返回的共享内存标识码
cmd:将要采取的动作(有三个可取值)
buf:指向一个保存着共享内存的模式状态和访问权限的数据结构
返回值:成功返回0;失败返回-1

在这里插入图片描述
命名管道实现通讯小例:
fifo_myread.c

  1 /**********************************************************
  2  * Author        : yang
  3  * Email         : wk_eternity@163.com
  4  * Last modified : 2019-07-25 15:16
  5  * Filename      : fifo_myread.c
  6  * Description   : 命令管道的基本使用
  7  * 命令管道可见于文件系统,会创建一个文件系统(文件只是名字)
  8  * int mkfifo (const char *pathname, mode_t mode);
  9  *  pathname :  管道文件的路径名
 10  *  mode :      创建文件的权限
 11  *  返回值 : 0 失败:-1
 12  * *******************************************************/
 13 #include <stdio.h>
 14 #include <unistd.h>
 15 #include <stdlib.h>
 16 #include <errno.h>
 17 #include <string.h>
 18 #include <fcntl.h>
 19 
 20 int main()
 21 {
 22     char *file = "./tmp.fifo";                                                                                                                                       
 23     umask(0);
 24     int ret = mkfifo(file, 0664);
 25     if(ret < 0){
 26         //如果文件不是因为已经存在而报错, 则退出
 27         if (errno != EEXIST) {
 28             perror ("mkfifo error");
 29             return -1;
 30         }
 31     }
 32     printf("create fifo success!\n");
 33     int fd  = open(file, O_RDONLY);
 34     if (fd < 0){ 
 35         perror("open errno");
 36         return -1;
 37     }
 38     printf("open fifo success!\n");
 39 
 40     while(1) {
 41         sleep(5);
 42         char buf[1024] = {0};
 43         int ret = read(fd, buf, 1023);
 44         if (ret < 0) {
 45             perror("write error!\n");
 46             return -1;
 47         }else if(ret == 0){
 48             printf("write closed!\n");
 49             return -1;
 50         }
 51         printf("buf : [%s]\n",buf);
 52     }
 53     close(fd);
 54     return 0;
 55 }

fifo_write.c

  1 #include <stdio.h>
  2 #include <unistd.h>
  3 #include <errno.h>
  4 #include <fcntl.h>
  5 #include <string.h>
  6 #include <stdlib.h>
  7 
  8 int main()
  9 {
 10     char *file = "./tmp.fifo";                                                  
 11     umask(0);
 12     int ret = mkfifo(file, 0664);                                               
 13     if(ret < 0){                                                                                                                                                     
 14         //如果文件不是因为已经存在而报错,则退出                                 
 15         if (errno != EEXIST){                                                   
 16         perror("mkfifo error!\n");
 17         return -1;                                                              
 18         }
 19     }
 20     printf("create fifo succes!\n");
 21     int fd = open(file, O_WRONLY);                                              
 22     if (fd < 0) {
 23         perror("open error");                                                   
 24         return -1;                                                              
 25     }                                                                           
 26     printf("open file success!\n");
 27     while(1) {
 28         char buf[1024] = {0};                                                   
 29         scanf("%s", buf);
 30         write(fd, buf, strlen(buf));                                            
 31     }                                                                           
 32     close(fd);
 33     return 0;                                                                   
 34 }

在两个终端下运行可执行文件,即可实现简单通讯.
匿名管道完成ps -ef|grep pipe命令

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

int main()
{
    int fd[2];

    int ret = pipe(fd);
    if (ret < 0) {
        perror("pipe error\n");
        return -1;
    }
    int pid1 = fork();
    if (pid1 == 0){
        //child1 -- ps-ef
        close(fd[0]);
        dup2(fd[1], 1);
        execlp("ps", "ps", "-ef",NULL);
        exit(0);
    }
    int pid2 = fork();
    if (pid2 == 0) {
        //child2 -- grep pipe
        close(fd[1]);
        dup2(fd[0], 0);
        execlp("grep","grep" ,"pipe", NULL);
        exit(0);
    }

    close(fd[0]);
    close(fd[1]);
    waitpid(pid1, NULL, 0);
    printf("child1 exit----\n");
    waitpid(pid2, NULL, 0);
    printf("child2 exit----\n");
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值