• 进程间通信IPC-共享内存(System V)


    共享内存

    如果要实现两个进程之间进行通信 两个进程之间是不能直接通信的 必须借助内核
    如进程1和进程2进行通信的话 进程1可能会借助内核中的某一块内存空间 这块内存空间要是两个进程都能访问的 然后进程1将要通信的内容写入(write 本质上copy)到这块内存空间中去
    然后进程2再从这块内存空间中去读取(read 其本质也是copy) 这样就实现了进程间的通信。
    问题:
    假设我们有三个进程 进程1 进程2 进程3 我们如果假设进程1中某块内存作为进程2 进程3的共享内存 也就是说进程2 和进程3通信其实是通过进程1中的某块内存进程通信的 这种方式是否可行?
    不行 因为我们讲进程之间的不能直接访问的 而且另一个问题是 如果进程1死掉了 那么进程2和进程3可能也会跟着出问题。

    共享内存的实现方式:

    在内核中开辟一块共享内存,其他进程通过“映射”方式获取这段共享内存的引用(指针)

    System V共享内存
    通过ftok()函数获取System V IPC对象的key —> 通过key创建/打开这个IPC的设施(msg/shm/sem) —>通过System V IPC提供的读/写函数接口交换数据 —>关闭设施

    相关函数

    ftok用来创建一个System V IPC对象的key

    #include 
    #include 
    
    key_t ftok(const char *pathname, int proj_id);
    		
    函数功能:ftok用来创建一个System V IPC对象的key 
    参数列表: 
    						pathname: 一个文件系统中的路径名(必须要存在的并且有权限读取的)
    						
    						proj_id: 就是一个整数 这个参数存在的意思在于在一个文件也能生成多个IPC key键值。 
    								 
    								 ftok利用同一个文件最多可得到IPC key键值256个  
    								 因为ftok只取proj_id值二进制后8位 及16进制后2位 与文件信息合成IPC key键值
    		
    								 也就是ftok("/home/china",257)==ftok("/home/china",1) 生成的键值是一样的
    		
    		
    				返回值: 
    						成功生成一个唯一的System V IPC对象的key(类型key_t --->int)
    						失败返回-1 同时errno被设置 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    shmget 创建/打开一个System V的共享内存

    #include 
    #include 
    
    					   int shmget(key_t key, size_t size, int shmflg);
    
    					函数功能:创建/打开一个System V的共享内存 
    					
    					
    					参数列表: 
    								
    								key :System V  IPC对象的key  一般由ftok的返回值获得 
    								
    								size:以字节为单位指定共享内存区的大小  
    									  当实际操作为创建一个新的共享内存区时 必须指定一个不为0的size值(page_size的整数倍:4096)
    									  如果实际操作为访问一个已经存在的共享内存去 那么size一般为0  
    									  
    								shmflg:标志位 
    										
    										如果是创建共享内存 : IPC_CREAT | 权限位   
    										
    										如果是打开共享内存 :  0 
    										
    										其实你完全按照创建的方式指定也可以 因为在创建共享内存的时候 如果发现key对应的共享内存
    										已经存在了 那么他会自动忽略后面的选项直接打开
    				
    					返回值: 
    								成功返回创建地共享内存的id (shmid)
    								失败返回-1
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    shmat 映射操作

    #include 
    #include 
    
    				   void *shmat(int shmid, const void *shmaddr, int shmflg);
    				   
    				函数功能:将shmid指定的共享内存 映射到进程的地址空间 
    
    								
    				参数列表: 
    						shmid: 表示你要映射哪块共享内存 (shmget的返回值)
    						
    						shmaddr: 表示你要映射到进程的地址空间的具体哪个地方 
    								 一般为NULL 表示让系统自行分配 
    						shmflg: 标志位权限 
    								1. SHM_RDONLY 只读 
    								2. 0   读写 
    								
    				返回值:
    						成功返回映射空间的首地址(指针)
    						失败返回(void *-1 并且errno被设置
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    int shmdt(const void *shmaddr);
    
    				函数功能:解除映射 
    				
    				参数列表: 
    						shmaddr: 映射空间的首地址  
    						
    				返回值: 
    						成功返回0 
    						失败返回-1 并且errno被设置
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    设置共享内存 比如删除共享内存

    #include 
    				   #include 
    
    				   int shmctl(int shmid, int cmd, struct shmid_ds *buf);
    
    				函数功能: 对共享内存的一些控制操作 
    				
    				头文件:如上 
    				
    				参数列表: 
    				
    						shmid : 你要对哪块共享内存进行操作 
    						
    						cmd: 操作命令   不同的命令第三个参数不一样 
    							 cmd == IPC_RMID 	删除对应的共享内存 
    							 
    						buf: 如果cmd == IPC_RMID buf就为NULL 
    						
    				返回值: 
    						成功返回0 
    						失败返回-1 并且errno被设置
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    使用共享内存实现父子进程通信

    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    int main()
    {
        pid_t pid;
        pid=fork();
    
        if(pid ==0)
        {
            //创建钥匙
            key_t key=ftok("/home/china",250);
            if(key ==-1)
            {
                perror("key error\n");
                return -1;
            }
            
            //创建共享内存
            int shmid=shmget(key,4096,IPC_CREAT |0666);
            if(shmid ==-1)
            {
                perror("shmid erorr\n");
                return -1;
            }
    
            //映射
            char * p=shmat(shmid,NULL,0);
            if(p==(char *)-1)
            {
                perror("shmat error\n");
                return -1;
            }
    
            //写数据
            strcpy(p,"Hello share memory");
    
            //解映射
            shmdt(p);
        }
        else if(pid >0)
        {
            //创建钥匙
            key_t key=ftok("/home/china",250);
            if(key ==-1)
            {
                perror("key error\n");
                return -1;
            }
            
            
            //创建共享内存
            int shmid=shmget(key,4096,IPC_CREAT |0666);
            if(shmid ==-1)
            {
                perror("shmid erorr\n");
                return -1;
            }
    
            //映射
            char * p=shmat(shmid,NULL,0);
            if(p==NULL)
            {
                perror("shmat error\n");
                return -1;
            }
    
            //读数据
            char buf[1024]={0};
            strcpy(buf,p);
            printf("%s\n",buf);
    
            //解映射
            shmdt(p);
            //删除共享内存
            //shmctl(shmid,IPC_RMID,NULL);
            //wait(NULL);
        }
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
  • 相关阅读:
    (附源码)计算机毕业设计Java大学生学科竞赛报名管理系统
    vue中展示json数据的方法
    天宇优配|机构动向 港股强势上涨 机构看好反弹持续性
    java实现用户登录
    SQLAlchemy使用教程
    微服务(基础篇-007-RabbitMQ部署指南)
    红菜苔炒腊肉
    Java标识符指什么呢?
    实现内网穿透-netapp
    2.4G无线麦克风领夹麦一拖二_全双工_杰理JL6976M单芯片方案
  • 原文地址:https://blog.csdn.net/weixin_46836491/article/details/126876837