共享内存,信号量

本文详细介绍了Linux环境下,如何使用共享内存和信号量进行进程间的通信。首先,文章阐述了共享内存的概念及申请、使用、撤销映射的步骤,包括shmget、shmat和shmdt等关键函数的使用。接着,讨论了信号量作为同步互斥机制,讲解了信号量的申请、设置、删除和P/V操作,涉及semget、semctl和semop等函数。最后,文章通过实例展示了如何结合共享内存和信号量解决进程通信中的同步问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、进程之间的通信。--- 共享内存。
1、什么是共享内存?机制如何?
共享内存也是属于IPC对象,所以在使用之前,必须为共享内存申请key值。
共享内存由于存在于运行内存上,运行内存在linux下所有的进程都可以访问到,结论就是任意的两个进程都可以通过共享内存进行通信。
实现步骤: key值  -> ID号  -> 共享内存地址  -> 数据交换  -> 撤销映射。

2、 共享内存函数接口?
1)先申请key值。
   key = ftok(".",10);

2)根据申请到的key值去申请共享内存的ID号。  -> shmget()  -> man 2 shmget
功能: allocates a System V shared memory segment
    //允许在系统中申请一块共享内存   -> 你的住房证明

头文件:
    #include <sys/ipc.h>
        #include <sys/shm.h>

原型:
    int shmget(key_t key, size_t size, int shmflg);

参数:
    key: 共享内存的key值。
    size:共享内存的总字节数,必须是PAGE_SIZE的倍数    #define PAGE_SIZE 1024
    shmflg:IPC_CREAT|0666   -> 不存在则创建。

返回值:
    成功:共享内存的ID号
    失败:-1

3)根据共享内存ID号去运行内存上申请对应的空间。  -> shmat()  -> man 2 shmat
头文件:
    #include <sys/types.h>
        #include <sys/shm.h>

原型:
    void *shmat(int shmid, const void *shmaddr, int shmflg);

参数:
    shmid:共享内存的ID号。    
    shmaddr:NULL   -> 系统自动分配空间给你   99.99999%
        不为NULL-> 用户自己选择地址       0.000001%
    shmflg:普通属性 0

返回值:    
    成功:共享内存起始地址。
    失败:(void *)-1

4)撤销映射。  -> shmdt()  -> man 2 shmdt
头文件:
    #include <sys/types.h>
        #include <sys/shm.h>

原型:
    int shmdt(const void *shmaddr);


参数:
    shmaddr: 共享内存起始地址。

返回值:
    成功:0
    失败:-1

5)删除共享内存IPC对象。  -> shmctl()  -> man 2 shmctl
头文件:
    #include <sys/ipc.h>
        #include <sys/shm.h>

原型:
    int shmctl(int shmid, int cmd, struct shmid_ds *buf);


参数:
    shmid:共享内存的ID号。    
    cmd:IPC_RMID  -> 删除共享内存IPC对象
    buf:如果是删除,填NULL就行了。

返回值:
    成功:0    
    失败:-1

3、 案例  --  使用共享内存通信机制,实现jack.c发送数据给rose.c。
          rose.c每隔2S就打印一次共享内存的数据。

发送端:
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <stdio.h>
#include <string.h>

int main(int argc,char *argv[])
{
    //1、 申请共享内存的key值。
    key_t key = ftok(".",10);
    
    //2、 根据key值去申请共享内存ID号。
    int shmid = shmget(key,2048,IPC_CREAT|0666);
    
    //3、 根据ID号去运行内存中申请对应的空间。
    char *p = (char *)shmat(shmid,NULL,0);
    
    //4、  不断将数据写入到共享内存上。
    while(1)
    {
        fgets(p,2048,stdin);
        
        //5、  如果把"quit"放在共享内存上,则进程退出。
        if(strncmp(p,"quit",4) == 0)
        {
            break;
        }    
    }
    
    return 0;
}

接收端:
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>

int main(int argc,char *argv[])
{
    //1、 申请共享内存的key值。
    key_t key = ftok(".",10);
    
    //2、 根据key值去申请共享内存ID号。
    int shmid = shmget(key,2048,IPC_CREAT|0666);
    
    //3、 根据ID号去运行内存中申请对应的空间。
    char *p = (char *)shmat(shmid,NULL,0);
    
    //4、 不断地打印共享内存上的数据出来
    while(1)
    {
        printf("from shm:%s",p);
        
        sleep(1);
        
        if(strncmp(p,"quit",4) == 0)
        {
            break;
        }
    }
    
    //5. 撤销映射,删除IPC对象
    shmdt(p);
    shmctl(shmid,IPC_RMID,NULL);
    
    return 0;    
}

结果:
接受端每隔2S就会打印一次共享内存上的数据,即使数据没有修改,也会打印,不会阻塞。
最终目标: 
jack  -> 输入一次数据  -> 发送一次  -> rose  -> 接收一次数据  -> 打印一次

二、处理同步互斥的方式。  ---  信号量。
1、 什么是信号量?
信号量也是属于IPC对象,所以使用信号量之前必须先申请key值。
信号量不是用于进程之间的通信,只是作用与进程之间的通信。

2、学习信号量的函数接口?
1)申请key值。
   key = ftok(".",10);

2)根据key值来申请信号量ID号。  -> semget()  -> man 2 semget
功能: get a System V semaphore set identifier
    //获取信号量的ID号

头文件:
    #include <sys/types.h>
        #include <sys/ipc.h>
        #include <sys/sem.h>

原型:
    int semget(key_t key, int nsems, int semflg); 

参数:
    key: key值。
    nsems: 信号量中元素的个数。
    semflg:IPC_CREAT|0666

返回值:
    成功:信号量的ID号
    失败:-1

3)设置信号量元素的起始值/删除信号量的IPC对象。  -> semctl()  -> man 2 semctl
头文件:
    #include <sys/types.h>
        #include <sys/ipc.h>
        #include <sys/sem.h>
原型:
       int semctl(int semid, int semnum, int cmd, ...);

参数:
    semid:信号量的ID号
    semnum:需要操作的成员的下标   空间:0   数据:1
    cmd:  SETVAL   -> 用于设置信号元素的起始值
          IPC_RMID -> 删除信号量的ID号
    ...:  如果cmd填SETVAL,那么最后一个参数就是起始值
          如果cmd填IPC_RMID,最后一个参数就会忽略。

例如:我想设置空间为1,数据为0,那么怎么做?
semctl(semid,0,SETVAL,1);  -> 设置空间起始值为1
semctl(semid,1,SETVAL,0);  -> 设置数据起始值为0

如果想删除信号量ID号。
semctl(semid,0,IPC_RMID);

4)如何实现信号量的P/V操作?  (p操作(减1操作): 1变成0, v操作(加1操作): 0变成1)
                 semop()   -> man 2 semop

头文件:
    #include <sys/types.h>
        #include <sys/ipc.h>
        #include <sys/sem.h>

原型:
    int semop(int semid, struct sembuf *sops, size_t nsops);

参数:
    semid:信号量的ID号
    sops:进行P/V操作的结构体
struct sembuf{
    unsigned short sem_num;  需要操作的成员的下标   空间:0  数据:1        
    short          sem_op;   P操作/V操作           p操作:-1  v操作:1    
    short          sem_flg;  普通属性,填0。
}
    nsops: 信号量操作结构体的个数: -> 1

返回值:
    成功:0
    失败:-1

3、案例: 
   我们要将信号量加入到刚才的代码中,处理同步互斥。

参考: shm+sem/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值