进程间通信—IPC对象通信

一.共享内存(Shared Memory)

  • 效率:共享内存是进程间通信效率最高的方式。

  • 操作流程

    1. 生成唯一的 key 值。
    2. 通过 shmget 申请共享内存对象。
    3. 使用 shmat 将共享内存映射到本地内存。
    4. 读写共享内存。
    5. 使用 shmdt 撤销映射。
    6. 使用 shmctl 删除共享内存对象。
  • 所需头文件
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
  • key值创建方式

                   1.IPC_PRIVATE 固定的私有键值,其值等于 0x0,一般用于有亲缘关系的进程间使                            用。
                   2.ftok()创建临时键值。

1.ftok 函数

  • 函数原型key_t ftok(const char *pathname, int proj_id);

  • 功能:通过该函数可以将pathname指定的路径用来以proj_id生成唯一的临时键值。

  • 参数:pathname 路径+名称->任意文件,只要不会被删除重建即可。
               proj_id 整形的数字,一般用ASCII码的单字符。表示与参数1的运算。

  • 返回值:成功返回唯一键值,失败为-1。

2.shmget 函数

  • 函数原型int shmget(key_t key, size_t size, int shmflg);

  • 功能:使用唯一键值key向内核提出共享内存使用申请。

  • 参数:key 唯一键值。
               size 要申请的共享内存大小。
               shmflg 申请的共享内存访问权限,八进制表示。
                           如果是第一个申请,则用IPC_CREAT;
                           如果要检测是否存在,用IPC_EXCL。

  • 返回值:成功返回共享内存id,一般用shmid表示,失败为-1。

3.shmat 函数

  • 函数原型void *shmat(int shmid, const void *shmaddr, int shmflg);
  • 功能:将指定shmid对应的共享内存映射到本地内存。

  • 参数:shmid 要映射的本地内存。
               shmaddr 本地可用的地址,如果不确定则用NULL,表示由系统自动分配。
               shmflg 0,表示读写,SHM_RDONLY表示只读。

  • 返回值:成功返回映射的地址,一般等于shmaddr,失败为-1。

4.shmdt 函数

  • 函数原型int shmdt(const void *shmaddr);
  • 功能:将本地内存与共享内存断开映射关系。

  • 参数:shmaddr 要断开的映射地址。

  • 返回值:成功返回0,失败为-1。

5.shmctl 函数

  • 函数原型int shmctl(int shmid, int cmd, struct shmid_ds *buf);
  • 功能:修改共享内存属性,也可以删除指定的共享内存对象。

  • 参数:shmid 要删除的共享内存对象。
               cmd    IPC_RMID 删除对象的宏。
               buf      NULL表示只删除对象。

  • 返回值:成功返回0,失败为-1。

6.部分问题探讨

  1. 共享内存数据的存储方式是拷贝还是剪切?

           在共享内存的上下文中,数据的存储方式是拷贝(Copy)。当一个进程写入数据到共享内存时,它实际上是将数据从自己的地址空间复制到共享内存区域。同样地,当进程从共享内存读取数据时,它是从共享内存区域复制数据到自己的地址空间。
  2. 共享内存的数据如果多次不同进程读写会怎么样?

            如果多个进程对同一共享内存区域进行读写操作,而没有适当的同步机制,那么数据可能会被覆盖。例如,如果进程A和进程B都试图写入同一内存位置,而进程B在进程A写入之后立即写入相同的位置,那么进程A的写入可能会被进程B的写入覆盖。这可能导致数据不一致和竞态条件。
  3. 进程1 写入共享内存,如何通知进程2 读共享内存?

           为了在进程间进行有效的通信,通常需要使用同步机制来确保数据的一致性和同步。这将引出下面有关信号量集的内容。

二.信号量(Semaphore)

  • 目的:解决共享内存的临界资源访问问题。

  • 操作流程

    1. key值。
    2. 使用 semget 申请信号量集。
    3. 初始化信号量(P操作)。
    4. 进行PV操作。
    5. 删除信号量。

1.semget 函数

  • 函数原型int semget(key_t key, int nsems, int semflg);
  • 功能:通过唯一键值向内核提出信号量申请。

  • 参数:key 唯一键值。
               nsems 要申请的信号量个数。
               semflg 申请的信号量的访问权限。

  • 返回值:成功返回semid,失败为-1。

2.semop 函数

  • 函数原型int semop(int semid, struct sembuf *sops, unsigned nsops);

  • 功能:修改指定信号量集中的信号量的值。

  • 参数:1)semid 信号量集id。

               2)struct sembuf
              {
                  unsigned short sem_num;  ///信号量集中信号量的编号,默认以0开始
                  short             sem_op;   ///信号量的PV操作,如果改值等于-1则表示p        
                                                                                                       等于1 则表示v
                                                                                                       等于0 则表示阻塞

                  short          sem_flg;   ///信号量的操作方式 0 表示默认阻塞。
                  IPC_NOWAIT and SEM_UNDO.
              };

              3)nsops 信号量的设置值个数。

  • 返回值:成功返回0,失败为-1。

  • 自定义封装

    • 通常会将以上函数做自定义封装。
int my_sem_wait(int id,int sem)
	{
		struct sembuf mysem;
		mysem.sem_num = sem;
		mysem.sem_op  = -1;
		mysem.flg	  = 0;

		if(semop(id,&mysem,1) < 0)
			return -1;
		else
			return 0;
	}

int my_sem_post(int id,int sem)
	{
		struct sembuf mysem;
		mysem.sem_num = sem;
		mysem.sem_op  = 1;
		mysem.flg	  = 0;

		if(semop(id,&mysem,1) < 0)
			return -1;
		else
			return 0;
	}

3.semctl 函数:

  • 函数原型int semctl(int semid, int semnum, int cmd, ...);
  • 功能:根据semid删除指定的信号量集。

  • 参数:semid 要删除的信号量集。
               semnum 要删除的信号量集中的信号量的编号。
               cmd  IPC_RMID  删除对象宏。
               ...    可变长参数,可以不写。

  • 返回值:成功返回0,失败为-1。

三.消息队列(Message Queue)

  • 敬请期待。。。。。。

四.辅助命令

  • ipcs -a:查询共享内存、信号量集、消息队列。
  • ipcrm -s:删除信号量集。
  • ipcrm -m:删除共享内存。

五.代码示例

//shm_w.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <string.h>
int main(int argc, char *argv[])
{
    key_t key = ftok("./",'!');    
    if(-1 == key)
    {
        perror("ftok");
        exit(1);
    }
    printf("key is 0x%x\n",key);

    int shmid = shmget(key,4096,IPC_CREAT|0666);
    if(-1 == shmid)
    {
        perror("shmget");
        exit(1);
    }

    void* p =  shmat(shmid,NULL,!SHM_RDONLY);
    if((void *) -1 == p )
    {
        perror("shmat");
        exit(1);
    }

    strcpy((char*)p,"hello,this is shm test");

    shmdt(p);
    return 0;
}




//shm_r.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <string.h>
int main(int argc, char *argv[])
{
    key_t key = ftok("./",'!');    
    if(-1 == key)
    {
        perror("ftok");
        exit(1);
    }
    printf("key is 0x%x\n",key);

    int shmid = shmget(key,4096,IPC_CREAT|0666);
    if(-1 == shmid)
    {
        perror("shmget");
        exit(1);
    }

    void* p =  shmat(shmid,NULL,!SHM_RDONLY);
    if((void *) -1 == p )
    {
        perror("shmat");
        exit(1);
    }

   // strcpy((char*)p,"hello,this is shm test");
   printf("mem is %s\n",(char*)p);
    shmdt(p);
    //shmctl(shmid,IPC_RMID,NULL);
    return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值