首页 > 系统相关 >XSI机制的进程间通信

XSI机制的进程间通信

时间:2024-08-21 19:53:59浏览次数:13  
标签:信号量 共享内存 IPC int 间通信 消息 进程 XSI

XSI机制的进程间通信

1、XSI介绍:
什么是XSI:

X/Open国际联盟有限公司是一个欧洲基金会,它的建立是为了向UNIX环境提供标准,XSI是X/Open System Interface的缩写,也就是X/Open设计的系统接口。

X/Open的主要的目标是促进对UNIX系统、接口、网络和应用的开放式系统协议的制定。它还促进在不同的UNIX环境之间的应用程序的互操作性,以及支持对电气电子工程师协会对UNIX的可移植操作系统接口规范。

IPC对象:
  • 它用于进程间通信的XSI-IPC内核对象,类似于匿名管道、文件内核对象一样,通过使用XSI方式进行进程间通信时,系统会在内核中创建出一个XSI-IPC内核对象,让通信的进程能够共同访问该内核对象

  • XSI-IPC对象只存在于内核空间,不会在文件系统中显示,该对象需要通过IPC键值来创建和获取

IPC键值:
  • 用于创建\获取 IPC对象的凭证,是一个无符号的整数,相当于IPC对象的名字,类似于文件名

  • 在创建IPC对象时,需要创建者提供一个IPC键值,有点类似给文件取名字,但是所有已经存在的IPC对象都在同一个作用域下,因此所有进程都可以访问到,所有有很大重名的风险,所以不建议创建者自己瞎想一个IPC键值去创建IPC对象,而应该使用操作系统提供的一个自动生成IPC键值的API

#include <sys/types.h>
#include <sys/ipc.h>
​
key_t ftok(const char *pathname, int proj_id);
功能:根据项目路劲和项目编号 自动生成一个ipc键值
pathname:建议提供当前项目的路径
proj_id:项目编号
返回值:会根据项目路径和编号来计算出一个IPC键值,但是只是根据字符串内容来计算,不会检查路径是否为假
注意:使用时,建议提供当前的正确的工作路径,以及不同的项目编号,就可以获得不同的IPC键值,通过不同的IPC键值来创建不同的IPC对象
但是获取时,需要拿到相同的IPC对象,因此需要拿相同的IPC键值,因此提供相同路径和编号即可获取
IPC描述符:
  • 类似于文件描述符,是一个非负整数,它是IPC内核对象的给用户空间来访问的凭证

显示IPC对象命令:
ipcs -m  #查看共享内存IPC对象   memory
ipcs -q  #查看消息队列IPC对象   queue
ipcs -s  #查看信号量IPC对象     sem
ipcs -a  #查看所有的IPC对象           
删除IPC对象命令:
ipcrm -m  id    #删除共享内存IPC对象
ipcrm -q  id    #删除消息队列IPC对象
ipcrm -s  id    #删除信号量IPC对象

2、共享内存:
基本原理:
  • 在内核中开辟一块内存,可以让其它进程的虚拟地址与这块内存进行映射,从而达到让多个进程共享一块内存的目的,当一个进程向这块内存写数据时,其它进程就都可以读取到,这就达到了通信的目的

  • 优点:这种通信方式不存在数据的复制,是最快的进程间通信方式

  • 缺点:需要考虑同步访问的问题(用信号)

使用方式:

当一个进程向共享内存写入数据时,内核不会通知其他进程,进程从共享内存读取数据时,也无法分辨是否是通信的对方新写入的数据,为解决该问题,有以下方式:

  • 读写:一个进程只负责写,另一个只负责读,这要负责读的进程使用的是最新即可,是一种单向通信

  • 轮询:配合定时器或者闹钟,每隔一段时间就读取一次

  • 中断:配合信号,进程只要往共享内存中写入数据,就给对方发送一个约定好的信号,其它进程如果已经读取完共享内存也可以发送一个约定好的信号

相关API:
#include <sys/ipc.h>
#include <sys/shm.h>
​
int shmget(key_t key, size_t size, int shmflg);
功能:创建\获取一个共享内存的IPC对象
key:IPC键值,类似于文件名
size:共享内存的字节数,建议取内存页字节数的整数倍,默认1页=4096字节
    如果是想要创建,则必须指定size的大小
    如果是获取已有的共享内存,这size可取0即可
shmflg:
    0       -表示该参数无效,获取共享内存,size也无效了
    IPC_CREAT   -创建共享内存
    IPC_EXCL    -如果已经存在同一个IPC对象,返回失败
    mode        -共享内存的权限,当创建共享内存时必须加上
        例如:IPC_CREAT|0644
返回值:IPC描述符,失败-1
​
void *shmat(int shmid, const void *shmaddr, int shmflg);
功能:加载共享内存(把进程中的虚拟地址与内核中的共享内存建立映射关系)
shmid:IPC描述符,要映射哪条共享内存
shmaddr:想要映射的虚拟地址,可以给NULL由操作系统自动分配
shmflg:
    0               - 以读写方式映射共享内存
    SHM_RDONLY      - 以只读方式映射共享内存
返回值:映射成功后的虚拟内存首地址,失败返回-1
    
int shmdt(const void *shmaddr);
功能:取消虚拟内存与共享内存的映射,也叫卸载共享内存    
shmaddr:要取消映射的虚拟内存地址
    
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
功能:销毁共享内存、获取共享内存属性、设置共享内存的属性
shmid:IPC描述符
cmd:
    IPC_SET     -设置共享内存的属性  buf输入型参数,只有uid、gid、mode可设置
    IPC_STAT    -获取共享内存属性   buf输出型参数
    IPC_RMID    -删除共享内存 buf给NULL即可,
                -删除并非真正直接删除,而是对共享内存的使用计数-1,如果该计数被-1为0后,意味着系统中没有任何的进程映射这块共享内存,才会从内核中真正销毁它
                
struct shmid_ds {
   struct ipc_perm shm_perm;    //  属主和权限信息
   size_t          shm_segsz;   //  共享内存的大小
   time_t          shm_atime;   //  最后映射时间(加载)
   time_t          shm_dtime;   //  最后取消映射的时间(卸载)
   time_t          shm_ctime;   //  最后内存内容修改时间
   pid_t           shm_cpid;    //  创建者pid
   pid_t           shm_lpid;    //  最后加载、卸载者pid
   shmatt_t        shm_nattch;  //  当前映射的进程数的计数器
   ...
};
​
struct ipc_perm {
   key_t          __key;    //  IPC键值
   uid_t          uid;      //  属主id
   gid_t          gid;      //  属主组id
   uid_t          cuid;     //  创建者id
   gid_t          cgid;     //  创建者组id
   unsigned short mode;     //  权限
   unsigned short __seq;    //  序列号
};
​
编程模型:
    进程A             进程B
  创建共享内存        获取共享内存
  映射共享内存        映射共享内存
  写数据并通知对方    接收到通知后读取数据
  接收到通知后读取数据 写数据并通知对方
  取消映射           取消映射
  删除共享内存
//  shmA.c
​
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <string.h>
​
static int shmid;
static char* shm;
​
//  接收到对方发送信号 才读取数据
void sigread(int num)
{
    printf("\nread:%s\n",shm);
    if(0 == strncmp(shm,"quit",4))
    {   
        printf("对方不想鸟你了\n");
​
        //  取消映射
        if(shmdt(shm))
        {
            perror("shmdt");
            exit(EXIT_FAILURE);
        }
​
        //  删除共享内存
        if(shmctl(shmid,IPC_RMID,NULL))
        {
            perror("shmctl");
            exit(EXIT_FAILURE);
        }      
        //  必须结束进程 return没有用
        exit(EXIT_SUCCESS);
    }   
}
int main(int argc,const char* argv[])
{
    signal(SIGRTMIN,sigread);
    printf("我是进程%u\n",getpid());
​
    //  创建共享内存
    shmid = shmget(ftok(".",110),4096,IPC_CREAT|0644);
    if(0 > shmid)
    {
        perror("shmget");
        return EXIT_FAILURE;
    }
​
    //  映射共享内存
    shm = shmat(shmid,NULL,0);
    if((void*)-1 == shm)
    {
        perror("shmat");
        return EXIT_FAILURE;
    }
​
    //  获取对方的进程id
    pid_t pid = 0;
    printf("请输入与我通信的进程ID:");
    scanf("%u",&pid);
​
    //  写入数据并通知对方
    for(;;)
    {
        printf(">>>");
        scanf("%s",shm);
​
        //  发送信号
        kill(pid,SIGRTMIN);
        if(0 == strncmp(shm,"quit",4))
        {
            printf("你终止通信了\n");
            break;
        }
    }
​
    //  取消映射
    if(shmdt(shm))
    {
        perror("shmdt");
        return EXIT_FAILURE;
    }
    //  删除共享内存
    if(shmctl(shmid,IPC_RMID,NULL))
    {
        perror("shmctl");
        return EXIT_FAILURE;
    }
    return EXIT_SUCCESS;
}
​
​
​

3、消息队列:
基本原理:
  • 由系统内核维护的一个链式队列,每个节点称为一条消息,每条消息由消息类型、数据、长度信息组成

  • 和管道类似,可以双向通信,并且从中读取一个消息后,会自动出队,而且不是按照顺序读取,是按照消息类型读取

相关API:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
​
int msgget(key_t key, int msgflg);
功能:创建\获取消息队列
key:IPC键值
msgflg:
    0           -获取,不存在则失败
    IPC_CREAT   -创建,不存在则创建,存在会获取,除非
    IPC_EXCL    -排斥,已存在则创建失败
    mode        -消息队列权限,创建时必给
返回值:IPC描述符,失败-1
    
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
功能:向消息队列发送消息包
msqid:IPC描述符
msgp:提供一个包含有消息类型和消息内容的消息包内存块,参考格式如下:
    struct msgbuf {
       long mtype;       // 消息类型
       char mtext[1];    // 消息内容,也可以使用柔性数组
   };
msgsz:提供消息包中消息内容的字节数,不包含消息类型的字节数
msgflg:
    0       -如果当消息队列中没有空闲空间,该函数会一直阻塞
    IPC_NOWAIT  -当消息队列中没有空闲空间,不会阻塞,返回-1
返回值:成功0 失败-1
​
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);
功能:从消息读取消息包
msqid:IPC描述符
msgp:接收消息包的结构体首地址,也是由消息类型+消息内容组成,输出型参数
msgsz:消息包的字节数,如果实际消息包的大小>msgsz,会立即返回-1
msgtyp:要接收的消息类型
    0   -获取消息队列中的第一条消息
    >0  -获取指定消息类型的消息
    <0  -获取消息队列中,消息类型小于或等于abs(msgtyp)的消息,如果有多个,则接收最小值的
msgflg:
    0           阻塞等待
    IPC_NOWAIT  如果消息队列中没有该消息类型的消息,直接返回
    MSG_EXCEPT  获取消息队列中第一个不等于msgtyp的消息,msgtyp>0
    MSG_NOERROR 如果包含它,则当消息包的大小>msgsz时,不会报错,并接收msgsz字节
返回值:成功接收到的消息的字节数
​
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
功能:销毁消息队列、获取消息队列属性、设置消息队列属性
shmid:IPC描述符
cmd:
    IPC_SET     -设置消息队列的属性  buf输入型参数,只有uid、gid、mode可设置
    IPC_STAT    -获取消息队列属性   buf输出型参数
    IPC_RMID    -删除消息队列 buf给NULL即可
​
struct msqid_ds {
   struct ipc_perm msg_perm;     // 与shmctl的一致
   time_t          msg_stime;    /* Time of last msgsnd(2) */
   time_t          msg_rtime;    /* Time of last msgrcv(2) */
   time_t          msg_ctime;    // 最后属性修改时间
   unsigned long   __msg_cbytes; // 消息队列中的字节数
   msgqnum_t       msg_qnum;     // 消息队列中的消息数
   msglen_t        msg_qbytes;   // 消息队列运行的最大字节数
   pid_t           msg_lspid;    /* PID of last msgsnd(2) */
   pid_t           msg_lrpid;    /* PID of last msgrcv(2) */
};
​
编程模型:
        进程A             进程B
    创建消息队列        获取消息队列
    发送消息(type:a)    接收消息(type:a)
    接收消息(type:b)    发送消息(type:b) 
    销毁消息队列      

4、信号量
基本原理:
  • 所谓信号量就是内核中维护的一个全局变量,当做计数器,用于计数多进程工作中的共享资源数,也是为了限制多进程对共享资源的使用

  • 信号量是一种数据操作锁,本身是不具备数据交换通信功能的,而是通过控制其他进程的通信资源来协助实现进程间通信

    • 假设操作系统中有n个共享资源,需要把信号量的值设置为n

    • 当有m个进程需要以独占的形式使用k个共享资源,并且m*k>n,需要使用信号量

    • 每个进程在使用共享资源之前先尝试执行信号量-1操作

    • 如果信号量>0时,说明剩余共享资源够用,则-1后拿到共享资源去执行,当执行完毕后,需要把共享资源归还,信号量+1操作

    • 如果信号量<=0时,说明共享资源不足,则进程阻塞等待,直到信号量的值>0,才重新唤醒后,继续尝试-1

  • 注意:进程之间资源几乎都是相互独立的,共享资源很少,所以很少用到信号量

相关API:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
​
int semget(key_t key, int nsems, int semflg);
功能:创建或获取信号量集合
key:IPC键值
nsems:表示信号量集合中信号量的数量,一般写1即可
semflg:
    0           -获取,不存在则失败
    IPC_CREAT   -创建,不存在则创建,存在会获取,除非
    IPC_EXCL    -排斥,已存在则创建失败
    mode        -消息队列权限,创建时必给
返回值:IPC描述符,失败-1
    
int semop(int semid, struct sembuf *sops, size_t nsops);
功能:对信号量集中的信号量进行加减操作
semid:IPC描述符
sops:结构体数组首地址
struct sembuf{
    unsigned short sem_num;  // 信号量在信号集中的下标
    short          sem_op;   //  操作数
                //  如果sem_op大于0,则将其加到下标sem_num号信号量中,相当于资源释放
                //  如果sem_op小于0,则将其对下标sem_num号信号量中的值相减,相当于获取资源
    short          sem_flg;  // 操作标记位
                //  0 如果下标sem_num号信号量不够减,进程会阻塞等待,直到资源数够减为止
                //  IPC_NOWAIT  不阻塞等待
       };
nsops:表示sops结构体数组的指针中指向了多少个结构体,就是要操作的信号量的数量,一般写1
​
int semctl(int semid, int semnum, int cmd, ...);
功能:删除信号量、获取信号量属性、设置信号量属性
semnum:表示对信号量集合中semnum下标的信号量进行操作
​
cmd:
    IPC_SET     -设置信号量的属性  buf输入型参数,只有uid、gid、mode可设置
    IPC_STAT    -获取信号量属性    buf输出型参数
    IPC_RMID    -删除信号量  buf给NULL即可
    GETALL      -获取信号量集合中所有信号量的值,放入semun.array
    SETALL      -设置信号量集合中所有信号量的值,通过semun.array修改
    SETVAL      -设置信号量集合中某个信号量的值,通过semun.val修改
    GETVAL      -获取信号量集合中某个信号量的值,通过返回值获取
根据cmd的选择,使用第四个参数semun的值
 union semun {
       int              val;    /* Value for SETVAL */
       struct semid_ds *buf;    /* Buffer for IPC_STAT, IPC_SET */
       unsigned short  *array;  /* Array for GETALL, SETALL */
       struct seminfo  *__buf;  /* Buffer for IPC_INFO
                                   (Linux-specific) */
   };
​

标签:信号量,共享内存,IPC,int,间通信,消息,进程,XSI
From: https://blog.csdn.net/zzt_is_me/article/details/141401764

相关文章

  • 线程与进程的区别(多进程与多线程)
    线程与进程的区别(多进程与多线程)资源:进程采用虚拟空间+用户态/内核态机制,所以就导致进程与进程之间是互相独立的,各自的资源不可见。在同一进程中的各个线程都可以共享该进程所拥有的资源。多进程之间资源是独立的,多线程之间资源是共享的。通信:由于进程之间是互相独立的,需......
  • IO进程(学习)2024.8.21
    目录进程间通信进程间通信方式无名管道特点读写特性函数接口有名管道特点函数接口读写特性无名管道和有名管道的区别 信号信号的概念信号的分类信号的处理方式信号产生的方式信号信号函数发送信号闹钟信号进程间通信进程间通信方式1..早期的进程间通信......
  • Linux学习之进程
    进程进程process是指正在执行的程序;是程序正在运行的一个实例。它由程序指令,和从文件、其它程序中读取的数据或系统用户的输入组成。进程状态在进程的生命周期内,进程总会从一个状态转变到另一个状态。Linux中,一个进程有下面的可能状态:Running:正在运行(它是系统中的当前进程)或......
  • WPS关闭后,进程依然在后台运行的解决办法
    问题wps启动后在启动wps后,什么都不做,打开进程管理器,发现居然运行了3个wps进程:win10只会显示wps进程:win11显示比较准确:关闭后在关闭wps,再去任务管理器查看,发现在win10上依然存有1个wps进程在后台运行,win11上显示明确,显示wpscloudsvr在后台运行。解决方案进入到wp......
  • 深入理解Linux内核进程的管理与调度
    一,前戏1.1进程调度内存中保存了对每个进程的唯一描述,并通过若干结构与其他进程连接起来.调度器面对的情形就是这样,其任务是在程序之间共享CPU时间,创造并行执行的错觉,该任务分为两个不同的部分,其中一个涉及调度策略,另外一个涉及上下文切换.1.2进程的分类linux把......
  • 进程相关命令和函数
    查询进程相关命令ps aux查看进程相关信息1.就绪态、运行态R2.睡眠态、等待态可唤醒等待态S不可唤醒等待态D3.停止态T4.僵尸态Z5.结束态top根据CPU占用率查看进程相关信息kill和killallkill和killall发送一个信号kill -2 PID  15发送信号+PID对应......
  • Linux多进程
    进程的概述进程是计算机科学中的一个基本概念,它指的是在操作系统中正在执行的程序的实例在Linux操作系统中,进程是程序执行的实体,是资源分配的基本单位在在Ubuntu中,通过使用ps命令可以查看当前的进程列表psaux进程与程序的区别定义:程序:程序是一组指令的集合,它们被编写......
  • 多任务进程与线程
    多任务进程与线程一、多任务介绍​ 我们生活中有很多事情是同时进行的,比如开车的时候手和脚共同来驾驶汽车,再比如唱歌跳舞也是同时进行的;用程序来模拟:fromtimeimportsleepdefsing():foriinrange(3):print("正在唱歌...%d"%i)sleep(1)defda......
  • Linux 进程
    进程含义:进程是一个程序执行的过程,会去分配内存资源,具有并发特性(同一时段,运行多个任务)。进程的基本特性:(1)动态性:进程是程序的一次执行,它有着创建、活动、暂停、终止等过程,具有一定的生命周期,是动态地产生、变化和消亡的。动态性是进程最基本的特征。(2)并发性:指多个进程实体......
  • TCPIP路由技术第一卷第七章第六部分案例研究二多eigrp进程Passive接口自动汇总和手工
    tcp/ip_eigrp-2案例研究r1:routereigrp90nonetwork15.1.1.10.0.0.0r5:noroutereigrp90routereigrp1network0.0.0.00.0.0.0r1:routereigrp1network15.1.1.10.0.0.0routereigrp90redistributeeigrp1(在eigrp和eigrp之间重分布不需要加metric)rout......