IPC-----共享内存和信号量

时间:2021-02-21 20:42:36

共享内存
共享内存允许两个或者多个进程共享一个给定的存储区。因为数据不需要在客户进程和服务进程之间复制,所以这是最快的IPC形式(省略了两次内核拷贝),一旦这样的内存映射到共享它的进程的地址空间,这些进程间数据传递不在涉及到内核。信号量(后面有详解)用于同步共享存储访问。
IPC-----共享内存和信号量
它允许两个不相关的进程访问同一个逻辑内存。共享内存是在两个正在进行的进程之间传递数据的一种非常有效的方式。大多数的共享内存的实现,都把由不同进程之间共享的内存安排为同一段物理内存.
共享内存数据结构:
IPC-----共享内存和信号量
相关函数接口:
(1)shmget
功能:用来创建共享内存

IPC-----共享内存和信号量

int shmget(key_t key, size_t size, int shmflg)
//key:共享内存段名字
//size:共享内存大小,按页为基本单位
//shmflg:由九个权限标志构成
//返回值:成功返回一个非负整数,即
该段共享内存段的标识码;失败返回-1

(2)shmat
IPC-----共享内存和信号量
功能:将共享内存段连接到进程地址空间

void *shmat(int shmid, const void *shmaddr, int shmflg)
//shmid:共享内存标识
//shmadder:指定连接的地址,一般为NULL
//shmflg:两个可能取值是SHM_RND和SHM_RDONLY
//返回值:成功返回一个指针,指向共享内存的第一个节,失败返回-1.

(3)shmdt
功能:将共享内存段与当前进程脱离

int shmdt(const void *shmaddr)

注:将共享内存段与当前进程脱离不等于删除共享内存。
(4)shmctl
功能:用于(控制)删除共享内存

IPC-----共享内存和信号量

int shmctl(int shmid, int cmd, struct shmid_ds *buf)
//shmid:由shmget返回的共享内存标识码
//cmd:将要采取的动作

cmd的三个选项:
(1) IPC_STAT:把shmid_ds结构中的数据设置为共享内存的当前关联值。
(2) IPC_SET:在进程有足够权限的前提下,把共享内存的当前关联值设置为shmid_ds结构中给出的值
(3) IPC_RMID:删除共享内存段

共享内存的查看ipcs -m
共享内存的删除ipcrm -m shmid

IPC-----共享内存和信号量

共享内存没有提供任何保护机制,容易引起二义性问题,需要引入信号量。

信号量:
有些同学可能会疑惑,信号量怎么会是进程间通信的一部分,信号量需要同时被两个进程看到,计数器的增减过程也是数据通信的过程.
本质:
信号量与管道,消息队列不同,它是一个是用来描述临界资源中资源数目的一个计数器,用于为多个进程提供对共享数据对象的访问。

//本质为计数器
struct semaphore
{
    int value;
    point_PCB queue;
}
当计数器值减至为0时,创建一个等待队列

信号量的本质是一种数据操作锁,它本省不具有数据交换功能,而是通过控制其他的通信资源来实现进程间通信,它本身只是一种外部资源的标识。信号量在此过程中负责数据操作的互斥,同步等功能。
信号量生命周期随内核。
工作原理:
系统中某些资源一次只允许一个进程使用,称这样的资源为临界资源或互斥资源。
多个进程要申请临界资源首先要申请信号量,信号量本身也是临界资源。
P V 操作:
由于信号量只能进行两种操作等待和发送信号,即P(sv)和V(sv),他们的行为是这样的:
P(sv):如果sv的值大于0,就给他减1;如果值等于0,就挂起该进程的执行
V(sv):如果有其他进程因等待sv而被挂起,就让他恢复运行,如果没有进程因等待sv而挂起,就给他加1。
相关函数:
(1)创建和访问一个信号量
IPC-----共享内存和信号量

key:信号量的名字
names:信号集中信号量序号
semflg:九个权限构成
返回值:成功返回该信号集的标识符,失败返回-1

(2)用于控制信号量集
IPC-----共享内存和信号量

semid:由semget返回的信号量集标识符
semnum:信号集中信号量序号
cmd:将要采取的动作
返回值:成功返回0,失败返回-1.

(3)创建和访问一个信号量集
IPC-----共享内存和信号量

semid:是该信号量的标识码。
sops:是指向一个结构数值的指针。
nsops:信号量的个数

查看信号量:ipcs -s
删除信号量:ipcrm -s semid号
IPC-----共享内存和信号量
实例:创建一个子进程让子进程连续输出A,然后父进程连续输出B。
IPC-----共享内存和信号量

本希望AA BB可以连续依次出现在屏幕上,即就是子进程在打印的时候父进程在一旁等待,父进程在打印的时候子进程在一旁等待。但是结果杂乱无章。
添加信号量后, AA BB就可以成对的规律出现,接下来看看改进后的代码:
IPC-----共享内存和信号量
comm.h
IPC-----共享内存和信号量
接下来就是对前面详细介绍的信号量相关函数的具体应用。
comm.c

#include"comm.h"
static int commsemset(int nums,int flags)
{
        key_t _k = ftok(PATHNAME,PROJ_ID);
        if(_k<0){
          perror("ftok");
          return -1;
        }
        int semid = semget(_k,nums,flags);
        if(semid < 0)
        { 
          perror("semget");
          return -2;
        }
        return semid;
}
int createsemset(int nums)
{     
        return commsemset(nums,IPC_CREAT|IPC_EXCL|0666);
}
int getsemset(int nums)
{   
         return commsemset(nums,IPC_CREAT);
}
static int commPV(int semid, int nums, int op)
{   
    struct sembuf _sf;  
    _sf.sem_num = nums;  
    _sf.sem_op = op;  
    _sf.sem_flg = 0;

    if (semop(semid, &_sf, 1) != 0){
        perror("semop");
        return -1;
    }
    return 0;
}
#include"comm.h"
static int commsemset(int nums,int flags)
{
        key_t _k = ftok(PATHNAME,PROJ_ID);
        if(_k<0){
          perror("ftok");
          return -1;
        }
        int semid = semget(_k,nums,flags);
        if(semid < 0)
        {
          perror("semget");
          return -2;
        }
        return semid;
}
int createsemset(int nums)
{
        return commsemset(nums,IPC_CREAT|IPC_EXCL|0666);
}
int getsemset(int nums)
{
         return commsemset(nums,IPC_CREAT);
}
static int commPV(int semid, int nums, int op)
{
    struct sembuf _sf;
    _sf.sem_num = nums;
    _sf.sem_op = op;
    _sf.sem_flg = 0;

    if (semop(semid, &_sf, 1) != 0){
        perror("semop");
        return -1;
    }
    return 0;
}

int p(int semid, int num)
{
    return commPV(semid, num, -1);
}

int v(int semid, int num)
{
    return commPV(semid, num, 1);
}


int initsemset(int semid, int which, int val)
{
    union semun _un;
    _un.val = val;
    if (semctl(semid, which, SETVAL, _un) != 0)
    {
        perror("semctl");
        return -1;
    }
    return 0;
}
int destroysemset(int semid)
{
        if(semctl(semid,0,IPC_RMID)<0){
         perror("semctl");return -1;
        }
        return 0;
}

运行结果:
IPC-----共享内存和信号量