进程间通讯--共享内存、信号量

什么是信号量Semaphore?

信号量就是一个计数器,准确的说,是一个具有原子性的计数器。当使用资源时,计数器减一,表示外界的可用资源减少一个;当释放资源时,计数器加一,表示使用完后将资源还回去,使得外界可用资源增加一个。

  • a.概念:用来同步进程的特殊变量;一个特殊的计数器,大于0时记录资源的数量,小于0时,记录等待资源的进程的数量。当信号量的值大于0时,进程总是可以获取到资源并使用,小于0时,进程必须阻塞等待有其他进程释放资源。
	s>0  表示可用资源的个数
	s=0: 表示没有资源可以使用,也没有进程等待该资源
	s<0 表示没有资源可以使用,有|S|个进程在等待该资源
  • b.操作方式:对信号量进行操作使用p v操作,p v操作都是原子操作;
*p操作:获取资源,信号量的值减1*v操作:释放资源,信号量的值加1
相关函数:
semget(): 创建或者获取信号量的内核对象
semop(): 完成对信号量的p操作或v操作
semctl(): 设置信号量属性

注意: 操作系统对进程间通讯用的信号量在内核中都是以信号量集管理的,即就是通过semget函数获取到的是信号量集的标识符。其他函数操作时,必须指明操作的是那个信号量集中的那个信号量。类似于数组的下标。

信号量是用来干什么的呢?
在创建子进程时,你是如何保证父子进程执行的先后顺序的呢?(假设让子进程先执行)
但是这样会存在一个问题,我们只能保证子进程先执行,并不能保证子进程执行完了以后就执行父进程。于是便引入了信号量的概念,信号量可以达到这一目的,信号量是一种解决进程间同步与互斥的机制。

进程间通讯–共享内存

  • 共享存储SharedMemory:共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的 IPC 方式,它是针对其他进程间通信方式运行效率低而专门设计的。它往往与其他通信机制,如信号量,配合使用,来实现进程间的同步和通信。

在Linux中,每个进程都有属于自己的进程控制块(PCB)和地址空间(Addr Space),并且都有一个与之对应的页表,负责将进程的虚拟地址与物理地址进行映射,通过内存管理单元(MMU)进行管理。两个不同的虚拟地址通过页表映射到物理空间的同一区域,它们所指向的这块区域即共享内存。
在这里插入图片描述
注意:

  • 共享内存并未提供同步机制,也就是说,在第一个进程结束对共享内存的写操作之前,并无自动机制可以阻止第二个进程开始对它进行读取,所以我们通常需要用其他的机制来同步对共享内存的访问,例如信号量。

  • 当两个进程通过页表将虚拟地址映射到物理地址时,在物理地址中有一块共同的内存区,即共享内存,这块内存可以被两个进程同时看到。这样当一个进程进行写操作,另一个进程读操作就可以实现进程间通信。但是,我们要确保一个进程在写的时候不能被读,因此我们使用信号量来实现同步与互斥

  • 对于一个共享内存,实现采用的是引用计数的原理,当进程脱离共享存储区后,计数器减一,挂架成功时,计数器加一,只有当计数器变为零时,才能被删除。当进程终止时,它所附加的共享存储区都会自动脱离。

为什么共享内存的速度最快?

Proc A 进程给内存中写数据, Proc B 进程从内存中读取数据,在此期间一共发生了两次复制

(1)Proc A 到共享内存       (2)共享内存到 Proc B

因为直接在内存上操作,所以共享内存的速度也就提高了。

共享内存的接口函数以及指令

头文件:

    #include <sys/ipc.h>
    #include <sys/shm.h>

shmget

	查看系统中的共享存储段ipcs -m
	删除系统中的共享存储段ipcrm -m [shmid]
	
	shmget ( ):创建共享内存
	int shmget(key_t key, size_t size, int shmflg);
  • [参数key]:由ftok生成的key标识,标识系统的唯一IPC资源。

  • [参数size]:需要申请共享内存的大小。在操作系统中,申请内存的最小单位为页,一页是4k字节,为了避免内存碎片,我们一般申请的内存大小为页的整数倍。

  • [参数shmflg]:如果要创建新的共享内存,需要使用IPC_CREAT,IPC_EXCL,如果是已经存在的,可以使用IPC_CREAT或直接传0。

  • [返回值]:成功时返回一个新建或已经存在的的共享内存标识符,取决于shmflg的参数。失败返回-1并设置错误码。

shmat:将进程中的虚拟地址映射到共享的内存区域

void *shmat(int shmid, const void *shmaddr, int shmflg);
  • [参数shmid]:共享存储段的标识符。

  • [参数*shmaddr]:shmaddr = 0,则存储段连接到由内核选择的第一个可以地址上(推荐使用)。

  • [参数shmflg]:若指定了SHM_RDONLY位,则以只读方式连接此段,否则以读写方式连接此段。

  • [返回值]:成功返回共享存储段的指针(虚拟地址),并且内核将使其与该共享存储段相关的shmid_ds结构中的shm_nattch计数器加1(类似于引用计数);出错返回-1。

shmdt:断开连接(当一个进程不需要共享内存的时候,就需要去关联。该函数并不删除所指定的共享内存区,而是将之前用shmat函数连接好的共享内存区脱离目前的进程。)

int shmdt(const void *shmaddr);
  • [参数*shmaddr]:连接以后返回的地址。

  • [返回值]:成功返回0,并将shmid_ds结构体中的 shm_nattch计数器减1;出错返回-1。

shmctl :销毁共享内存

int shmctl(int shmid, int cmd, struct shmid_ds *buf);
  • [参数shmid]:共享存储段标识符。

  • [参数cmd]:指定的执行操作,设置为IPC_RMID时表示可以删除共享内存。

  • [参数*buf]:设置为NULL即可。[返回值]:成功返回0,失败返回-1。

()1)优点:我们可以看到使用共享内存进行进程之间的通信是非常方便的,而且函数的接口也比较简单,数据的共享还使进程间的数据不用传送,而是直接访问内存,加快了程序的效率
(2)缺点:共享内存没有提供同步机制,这使得我们在使用共享内存进行进程之间的通信时,往往需要借助其他手段来保证进程之间的同步工作。(信号)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值