进程间通讯的四种方式

版权声明:本文为博主原创文章,但部分内容来源自互联网,大家可以随意转载,点赞或留言均可! https://blog.csdn.net/csdn_kou/article/details/82908922

通信方法 无法介于内核态与用户态的原因
管道(不包括命名管道) 局限于父子进程间的通信。
消息队列 在硬、软中断中无法无阻塞地接收数据。
信号量 无法介于内核态和用户态使用。
共享内存 需要信号量辅助,而信号量又无法使用。

共享内存

共享内存是最快的进程间通讯的方式

  • 原因:相对于其他几种方式,共享内存直接在进程的虚拟地址空间进行操作,不再通过执行进入内核的系统调用来传递彼此的数据
    在这里插入图片描述

  • 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
  • shmctl函数
功能: ⽤用于控制共享内存
原型 int shmctl(int shmid, int cmd, struct shmid_ds *buf);
参数shmid: 由shmget返回的共享内存标识码
cmd: 将要采取的动作(有三个可取值)
buf: 指向⼀一个保存着共享内存的模式状态和访问权限的数据结构
返回值: 成功返回0;失败返回-1
  • shmdt函数
功能: 将共享内存段与当前进程脱离
原型 int shmdt(const void *shmaddr);
参数shmaddr: 由shmat所返回的指针
返回值: 成功返回0;失败返回-1
注意: 将共享内存段与当前进程脱离不等于删除共享内存段
#include<stdio.h>
#include<stdlib.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include<string.h>
#include<errno.h>

typedef struct _Teacher
{
    char name[64];
    int age;
}Teacher;

int main(int argc, char *argv[])
{
    int ret = 0;
    int    shmid;
    //创建共享内存 ,相当于打开文件,文件不存在则创建
    shmid = shmget(0x2234, sizeof(Teacher), IPC_CREAT | 0666); 
    if (shmid == -1)
    {
        perror("shmget err");
        return errno;
    }
    printf("shmid:%d \n", shmid);
    Teacher *p = NULL;
    //将共享内存段连接到进程地址空间
    p = shmat(shmid, NULL, 0);//第二个参数shmaddr为NULL,核心自动选择一个地址
    if (p == (void *)-1 )
    {
        perror("shmget err");
        return errno;
    }
    strcpy(p->name, "aaaa");
    p->age = 33;
    //将共享内存段与当前进程脱离
    shmdt(p);
        
    printf("键入1 删除共享内存,其他不删除\n");
    
    int num;
    scanf("%d", &num);
    if (num == 1)
    {
        //用于控制共享内存
        ret = shmctl(shmid, IPC_RMID, NULL);//IPC_RMID为删除内存段
        if (ret < 0)
        {
            perror("rmerrr\n");
        }
    }                 

    return 0;    
}
#include<stdio.h>
#include<stdlib.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include<string.h>
#include<errno.h>

typedef struct _Teacher
{
    char name[64];
    int age;
}Teacher;

int main(int argc, char *argv[])
{
    int ret = 0;
    int    shmid;
    //shmid = shmget(0x2234, sizeof(Teacher), IPC_CREAT |IPC_EXCL | 0666); 
    //打开获取共享内存
    shmid = shmget(0x2234, 0, 0); 
    if (shmid == -1)
    {
        perror("shmget err");
        return errno;
    }
    printf("shmid:%d \n", shmid);
    Teacher *p = NULL;
    //将共享内存段连接到进程地址空间
    p = shmat(shmid, NULL, 0);
    if (p == (void *)-1 )
    {
        perror("shmget err");
        return errno;
    }
    
    printf("name:%s\n", p->name);
    printf("age:%d \n", p->age);
    //将共享内存段与当前进程脱离
    shmdt(p);
    
    printf("键入1 程序暂停,其他退出\n");
    
    int num;
    scanf("%d", &num);
    if (num == 1)
    {
        pause();
    }                
    return 0;
}

选取简单的代码,可运行观察效果

信号

信号量和以前的IPC通信方式不同,信号量的本质是计数器,用于多进程对共享数据对象的访问。

在进程访问临界资源之前,需要测试信号量,如果为正数,则信号量-1并且进程可以进入临界区,若为非正数,则进程挂起放入等待队列,直至有进程退出临界区,释放资源并+1信号量,此时唤醒等待队列的进程。

信号量本身就是临界资源,所以必须是原子操作。

管道

单向,一段输入,另一端输出,先进先出FIFO。管道也是文件。管道大小4096字节。

  • 特点:管道满时,写阻塞;空时,读阻塞。
  • 分类:普通管道(仅父子进程间通信)位于内存;命名管道位于文件系统,没有亲缘关系管道只要知道管道名也可以通讯。
  • 管道是由内核管理的一个缓冲区(buffer),相当于我们放入内存中的一个纸条。管道的一端连接一个进程的输出。这个进程会向管道中放入信息。管道的另一端连接一个进程的输入,这个进程取出被放入管道的信息。一个缓冲区不需要很大,它被设计成为环形的数据结构,以便管道可以被循环利用。当管道中没有信息的话,从管道中读取的进程会等待,直到另一端的进程放入信息。当管道被放满信息的时候,尝试放入信息的进程会等待,直到另一端的进程取出信息。当两个进程都终结的时候,管道也自动消失在这里插入图片描述
  • 匿名管道与命名管道的区别
    • 匿名管道由pipe函数创建并打开。
    • 命名管道由mkfifo函数创建,打开用open
    • FIFO(命名管道)与pipe(匿名管道)之间唯一的区别在它们创建与打开的方式不同,一但这些工作完成之后,它们具有相同的语义。

消息队列

消息队列是先进先出FIFO原则
ipcs & ipcrm命令

  • ipcs: 显示IPC资源
  • ipcrm: 手动删除IPC资源
    在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/csdn_kou/article/details/82908922