• ipc----共享内存


    回顾消息队列

    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #define MSG_FIFE "./msgque"
    #define MSG_LENGTH 200
    int msgfid=0;//全局变量
    int create_or_get_msgque(void){
        key_t key=0;
        int msgfid=0;
        /*创建文件目录*/
        open(MSG_FIFE,O_RDWR|O_CREAT,0664);
        /*生成唯一key*/
        key=ftok(MSG_FIFE,'a');
        /*get:消息队列*/
        msgfid=msgget(key,0664|IPC_CREAT);
        return msgfid;
    }
    struct msgbuf {
                   long mtype;       /* message type, must be > 0 */
                   char mtext[MSG_LENGTH];    /* message data */
    };
    void signal_fun(int signo){
        msgctl(msgfid,IPC_RMID,NULL);//不需要存放属性的结构体
        exit(-1);
    }
    int main(int argc,char** argv){
        msgfid=create_or_get_msgque();
        if(argc!=2){ exit(-1);}
        long msg_type=atol(argv[1]);
        signal(SIGINT,signal_fun);
        /*创建父子进程进行通信*/
        pid_t ret=0;
        ret=fork();
        if(ret>0){//父进程发消息
            struct msgbuf msg_buf={0};
            while(1){
                memset(&msg_buf,0,sizeof(msg_buf));
                printf("输入要发送的消息:\n");
                scanf("%s",msg_buf.mtext);
                printf("要通信的对象\n");
                scanf("%ld",&msg_buf.mtype);
                //第四个参数:0阻塞接收,IPC_NOWAIT非阻塞接收
                msgsnd(msgfid,&msg_buf,MSG_LENGTH,0);
            }
        }
        else if(ret==0){//子进程接收消息
               struct msgbuf msg_buf={0};
           while(1){
                 //msgrcv(msqid, 结构体指针, 结构体长度,\
                                  编号类型,是否阻塞接收);
                bzero(&msg_buf,sizeof(msg_buf));
                msgrcv(msgfid,&msg_buf,MSG_LENGTH,msg_type,0);
                write(1,msg_buf.mtext,sizeof(msg_buf.mtext));
           }
        }
        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

    共享内存

    • 消息队列是OS创建的链表
    • 链表的所有节点都是保存在物理内存上的

    消息队列这个链表其实也是OS在物理内存上所开辟的缓存

    ​ 共享内存就是OS在物理内存中开辟一大段缓存空间,与系统API来读写所不同的是,使用共享内存通信时,进程是直接使用地址来共享读写的

    • 直接使用地址来读写缓存时,效率会更高

    • 调用API来读写的话:

      中间必须经过重重的OS函数调用之后,直到调用到最后一个函数时,该函数才会通过地址去读写共享的缓存.

      • 中间的调用过程会降低效率
    只要能够共享操作同一段缓存,就都可以实现进程间的通信
    
    • 1
    共享内存原理
    ------每个进程的虚拟内存严格对应自己的那片物理内存空间
    ------虚拟空间的虚拟地址,只和自己的那片物理内存空间的物理地址建立映射关系,和其它进程的物理内存空间没有任何的交集
    ------进程空间之间完全独立
    
    • 1
    • 2
    • 3

    共享内存的思路:让进程空间之间有交集

    实现方法

    以两个进程使用共享内存来通信为例

    1. 调用API,让OS在物理内存上开辟出一大段缓存空间
    2. 让各自进程空间与开辟出的缓存空间建立映射关系


    虚拟地址和实际物理地址建立一对一的对应关系,使用虚拟地址读写缓存时,虚拟地址最终是要转为物理地址的,转换时就必须参考这个映射关系。

    1.当多个进程映射并共享同一个空间时,在写数据的时候可能会出现相互干扰
    2.这时往往需要加保护措施,让每个进程在没有操作时不要被别人干扰
    -----文件锁或信号量
    
    • 1
    • 2
    • 3
    使用步骤

    shm是share memory的缩写

    1. 进程调用shmget函数创建新的或获取已有共享内存
    2. 进程调用shmat函数,将物理内存映射到自己的进程空间
      • 说白了就是让虚拟地址和真实物理地址建议一一对应的映射关系
      • 建立映射后,就可以直接使用虚拟地址来读写共享的内存空间了
    3. shmdt函数,取消映射
    4. 调用shmctl函数释放开辟的那片物理内存空间
    • 多个进程使用共享内存通信时,创建者只需要一个

    • 同样的,一般都是谁先运行谁创建,其它后运行的进程直接获取共享使用

      大家共享操作同一个内存,即可实现通信。

    共享内存函数

    shmget()
      #include 
      #include 
    //功能:创建新的,或者获取已有的共享内存
    int shmget(key_t key, size_t size, int shmflg);
    /*
       返回值
    			(a)成功:返回共享内存的标识符,以后续操作
    			(b)失败:返回-1,并且errno被设置。
    */
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    创建的过程其实就是os在物理内存上划出(开辟出)一段物理内存空间出来

    参数:

    key:用于生成共享内存的标识符

    • IPC_PRIVATE:指定这个后,每次调用shmget时都会创建一个新共享内存。
    • 自己指定一个长整型数
    • 使用ftok函数,通过路径名和一个8位的整形数来生成key值

    size:指定共享内存的大小,我们一般要求size是虚拟页大小的整数倍

    • 一般来说虚拟页大小是4k(4096字节)

      如果你指定的大小不是虚拟页的整数倍,也会自动补成整数倍

    semflg:与消息队列一样,指定原始权限和IPC_CREAT

    比如0664|IPC_CREAT

    总结

    shmget(ftok生成的key,指定shm的大小,0664|IPC_CREAT);

    使用ipcrm命令删除共享内存

    • 按照标识符删除 ipcrm -m msgid
    • 按照key值删除 ipcrm -M key
    shmat()
    #include 
    #include 
    /*功能:
    将shmid所指向的共享内存空间映射到进程空间(虚拟内存空间),并返回映射后的起始地址(虚拟地址)*/
    //有了这个地址后,就可以通过这个地址对共享内存进行读写操作
    void *shmat(int shmid, const void *shmaddr, int shmflg);
    /*返回值
    			a)成功:则返回映射地址
    			b)失败:返回(void *)-1,并且errno被设置*/
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    参数

    (a)shmid:共享内存标识符

    (b)shmaddr:指定映射的起始地址

    • 自己指定映射的起始地址(虚拟地址)

    一般不会这么做,因为我们自己都搞不清哪些虚拟地址被用了,哪些没被用

    • NULL:表示由内核自己来选择映射的起始地址(虚拟地址)

    (c)shmflg:指定映射条件

    • 0:以可读可写的方式映射共享内存
    • SHM_RDONLY:以只读方式映射共享内存
    shmdt()
    #include 
    #include 
    //功能:取消建立的映射
     int shmdt(const void *shmaddr);
    //返回值:调用成功返回0,失败返回-1,且errno被设置
    
    • 1
    • 2
    • 3
    • 4
    • 5

    参数shmaddr:映射的起始地址(虚拟地址)

    shmctl()
    #include 
    #include 
    //功能:根据cmd的要求,对共享内存进行相应控制
    //返回值:调用成功0,失败则返回-1,并且errno被设置
     int shmctl(int shmid, int cmd, struct shmid_ds *buf);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    参数
    • shmid:标识符
    • cmd:控制选项
    IPC_STAT:从内核获取共享内存属性信息到第三个参数(应用缓存)。
    IPC_SET: 修改共享内存的属性。
    IPC_RMID:删除共享内存,不过前提是只有当所有的映射取消后,才能删除共享内存
    		  删除时,用不着第三个参数,所以设置为NULL
    
    • 1
    • 2
    • 3
    • 4
    • buf的类型为struct shmid_ds
    1:cmd为IPC_STAT时: buf用于存储原有的共享内存属性,以供查看。
    2:cmd为IPC_SET时:  buf中放的是新的属性设置,用于修改共享内存的属性。
    
    • 1
    • 2
    	struct shmid_ds {
    			struct ipc_perm shm_perm;    /* Ownership and permissions:权限 */
    			size_t shm_segsz;   /* Size of segment (bytes):共享内存大小 */
    			time_t shm_atime;   /* Last attach time:最后一次映射的时间 */
    			time_t shm_dtime;   /* Last detach time:最后一次取消映射的时间 */
    			time_t shm_ctime;   /* Last change time:最后一次修改属性信息的时间 */
    			pid_t shm_cpid;    /* PID of creator:创建进程的PID */
    			pid_t shm_lpid;    /* PID of last shmat(2)/shmdt(2) :当前正在使用进程的PID*/   shmatt_t shm_nattch;  /* No. of current attaches:映射数量*/
    					..};
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    程序1

    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #define SHMFILE "./shmfile"
    #define SHMSIZE 4096//4K
    int shmId=-1;
    void share_memory_get(){
        //int shmget(key_t key, size_t size, int shmflg);
        open(SHMFILE,O_RDWR|O_CREAT,0664);
        key_t key=ftok(SHMFILE,'a');
        shmId=shmget(key,SHMSIZE,0664|IPC_CREAT);
    }
    char buf[300]={"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
    bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\
    cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc\
    ddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd"};
    int main(void){
        void* shmaddr=NULL;
        share_memory_get();
        //建立映射
        shmaddr=shmat(shmId,NULL,0);//可读可写
        if(shmaddr==(void*)-1){
            perror("shmat fails\n");
            exit(-1);
        }
        while(1){
            //参数:目的地址,源地址,内容大小
            memcpy(shmaddr,buf,sizeof(buf));
            sleep(2);
        }
        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
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #define SHMFILE "./shmfile"
    #define SHMSIZE 4096//4K
    int shmId=-1;
    void share_memory_get(){
        //int shmget(key_t key, size_t size, int shmflg);
        open(SHMFILE,O_RDWR|O_CREAT,0664);
        key_t key=ftok(SHMFILE,'a');
        shmId=shmget(key,SHMSIZE,0664|IPC_CREAT);
    }
    int main(void){
        void* shmaddr=NULL;
        share_memory_get();
        //建立映射
        shmaddr=shmat(shmId,NULL,0);//可读可写
        if(shmaddr==(void*)-1){
            perror("shmat fails\n");
            exit(-1);
        }
        while(1){
            if(strlen((char *)shmaddr)!=0){//有数据
                  printf("%s\n",(char*)shmaddr);
                  bzero(shmaddr,SHMSIZE);
            }
        }
        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

    删除共享内存

    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include
    #define SHMFILE "./shmfile"
    #define SHMSIZE 4096//4K
    int shmId=-1;
    void* shmaddr=NULL;
    void share_memory_get(){
        //int shmget(key_t key, size_t size, int shmflg);
        open(SHMFILE,O_RDWR|O_CREAT,0664);
        key_t key=ftok(SHMFILE,'a');
        shmId=shmget(key,SHMSIZE,0664|IPC_CREAT);
    }
    char buf[300]={"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
    bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\
    cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc\
    ddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd"};
    /*信号捕获,取消映射*/
    void signal_fun(int signo){
        //删除前必须取消映射
        shmdt(shmaddr);
        //删除映射(所有映射全删除,内存生效)
        shmctl(shmId,IPC_RMID,NULL);
        exit(-1);
    }
    int main(void){
        share_memory_get();
        signal(SIGINT,signal_fun);
        //建立映射
        shmaddr=shmat(shmId,NULL,0);//可读可写
        if(shmaddr==(void*)-1){
            perror("shmat fails\n");
            exit(-1);
        }
        while(1){
            //参数:目的地址,源地址,内容大小
            memcpy(shmaddr,buf,sizeof(buf));
            sleep(2);
        }
        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
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #define SHMFILE "./shmfile"
    #define SHMSIZE 4096//4K
    int shmId=-1;
    void* shmaddr=NULL;
    void share_memory_get(){
        //int shmget(key_t key, size_t size, int shmflg);
        open(SHMFILE,O_RDWR|O_CREAT,0664);
        key_t key=ftok(SHMFILE,'a');
        shmId=shmget(key,SHMSIZE,0664|IPC_CREAT);
    }
    /*信号捕获,取消映射*/
    void signal_fun(int signo){
        //删除前必须取消映射
        shmdt(shmaddr);
        //删除映射(所有映射全删除,内存生效)
        shmctl(shmId,IPC_RMID,NULL);
        exit(-1);
    }
    int main(void){
        share_memory_get();
        signal(SIGINT,signal_fun);
        //建立映射
        shmaddr=shmat(shmId,NULL,0);//可读可写
        if(shmaddr==(void*)-1){
            perror("shmat fails\n");
            exit(-1);
        }
        while(1){
            if(strlen((char *)shmaddr)!=0){//有数据
                  printf("%s\n",(char*)shmaddr);
                  bzero(shmaddr,SHMSIZE);
            }
        }
        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

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-l2Qbqaha-1669187824066)(/home/guojiawei/.config/Typora/typora-user-images/image-20221122224158285.png)]

    状态数清零后,才可以把共享内存删了,也就是取消映射只会取消自己的

    代码优化

    while(1){
            if(strlen((char *)shmaddr)!=0){//有数据
                  printf("%s\n",(char*)shmaddr);
                  bzero(shmaddr,SHMSIZE);
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    读共享内存的代码:

    没有数据时,cpu会一直循环的判断,非常浪费cpu资源。

    strlen函数只能用于判断字符串
    如果对方通过共享内存发送不是字符串,而是结构体、整形、浮点型数据,strlen将无法正确判断。
    
    • 1
    • 2

    保证写完后再读数据,当共享内存没有数据时,读进程休眠;

    当写进程把数据写完后,将读进程唤醒

    多个进程在操作时,涉及到一个谁先谁后的问题,其实就是同步问题

    用信号实现

    这里利用有名管道,来实现读取进程符号的目的

    有名管道通信过程代码(发送和接收进程号)
    1. 发送进程号
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    void print_error(char* str){
        perror(str);
        exit(-1);
    }
    /*利用有名管道发送进程号*/
    void send_pid_mkfifo(void){
        //创建有名管道
        int ret=0;
        ret =mkfifo("./fifo",0664);
        if(ret==-1&& errno != EEXIST) print_error("mkfifo fails\n");
        //以只写方式打开有名管道
        int fd=0;
        fd=open("./fifo",O_WRONLY);
        if(fd==-1) print_error("open fifo fails\n");
        //在管道里写自己的进程号
        int my_pid=getpid();
        ret=write(fd,&my_pid,sizeof(my_pid));
        if(ret==-1) print_error("write fails\n");
    }
    int main(void){
        send_pid_mkfifo();
        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
    1. 接收进程号
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    void print_error(char* str){
        perror(str);
        exit(-1);
    }
    /*利用有名管道发送进程号*/
    int get_pid_mkfifo(void){
        //创建有名管道
        int ret=0;
        ret =mkfifo("./fifo",0664);
        if(ret==-1&& errno != EEXIST) print_error("mkfifo fails\n");
        //以只读方式打开有名管道
        int fd=0;
        fd=open("./fifo",O_RDONLY);
        if(fd==-1) print_error("open fifo fails\n");
        //在管道里写自己的进程号
        int pid_in_fifo;
        ret=read(fd,&pid_in_fifo,sizeof(pid_in_fifo));
        if(ret==-1) print_error("read fails\n");
        return pid_in_fifo;
    }
    int main(void){
        int pid=get_pid_mkfifo();
        printf("%d\n",pid);
        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

    优化后的代码

    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #define SHMFILE "./shmfile"
    #define SHMSIZE 4096//4K
    //全局变量
    int shmId=-1;
    void* shmaddr=NULL;
    char buf[300]={"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
    bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\
    cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc\
    ddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd"};
    //创建共享内存
    void share_memory_get(){
        //int shmget(key_t key, size_t size, int shmflg);
        open(SHMFILE,O_RDWR|O_CREAT,0664);
        key_t key=ftok(SHMFILE,'a');
        shmId=shmget(key,SHMSIZE,0664|IPC_CREAT);
    }
    /*信号捕获,取消映射*/
    void signal_fun(int signo){
        //删除前必须取消映射
        shmdt(shmaddr);
        //删除映射(所有映射全删除,内存生效)
        shmctl(shmId,IPC_RMID,NULL);
        //删除管道文件
        remove("./fifo");
        //删除共享内存
        remove(SHMFILE);
        exit(-1);
    }
    void print_error(char* str){
        perror(str);
        exit(-1);
    }
    /*利用有名管道接收进程号*/
    int get_pid_mkfifo(void){
        //创建有名管道
        int ret=0;
        ret =mkfifo("./fifo",0664);
        if(ret==-1&& errno != EEXIST) print_error("mkfifo fails\n");
        //以只读方式打开有名管道
        int fd=0;
        fd=open("./fifo",O_RDONLY);
        if(fd==-1) print_error("open fifo fails\n");
        //在管道里写自己的进程号
        int pid_in_fifo;
        ret=read(fd,&pid_in_fifo,sizeof(pid_in_fifo));
        if(ret==-1) print_error("read fails\n");
        return pid_in_fifo;
    }
    int main(void){
        int get_pid=get_pid_mkfifo();
        share_memory_get();
        signal(SIGINT,signal_fun);
        //建立映射
        shmaddr=shmat(shmId,NULL,0);//可读可写
        if(shmaddr==(void*)-1) print_error("shmaddr fails\n");
        while(1){
            //参数:目的地址,源地址,内容大小
            memcpy(shmaddr,buf,sizeof(buf));
            kill(get_pid,SIGUSR1);//唤醒读进程
            sleep(1);
        }
        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
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #define SHMFILE "./shmfile"
    #define SHMSIZE 4096//4K
    int shmId=-1;
    void* shmaddr=NULL;
    void share_memory_get(){
        //int shmget(key_t key, size_t size, int shmflg);
        open(SHMFILE,O_RDWR|O_CREAT,0664);
        key_t key=ftok(SHMFILE,'a');
        shmId=shmget(key,SHMSIZE,0664|IPC_CREAT);
    }
    /*信号捕获,取消映射*/
    void signal_fun(int signo){
        //删除前必须取消映射
        shmdt(shmaddr);
        //删除映射(所有映射全删除,内存生效)
        shmctl(shmId,IPC_RMID,NULL);
        //删除管道文件
        remove("./fifo");
        //删除共享内存文件
        remove(SHMFILE);
        exit(-1);
    }
    void signal_fun_user(int signo){
    }
    void print_error(char* str){
        perror(str);
        exit(-1);
    }
    /*利用有名管道发送进程号*/
    void send_pid_mkfifo(void){
        //创建有名管道
        int ret=0;
        ret =mkfifo("./fifo",0664);
        if(ret==-1&& errno != EEXIST) print_error("mkfifo fails\n");
        //以只写方式打开有名管道
        int fd=0;
        fd=open("./fifo",O_WRONLY);
        if(fd==-1) print_error("open fifo fails\n");
        //在管道里写自己的进程号
        int my_pid=getpid();
        ret=write(fd,&my_pid,sizeof(my_pid));
        if(ret==-1) print_error("write fails\n");
    }
    int main(void){
        signal(SIGINT,signal_fun);
        signal(SIGUSR1,signal_fun_user);
        send_pid_mkfifo();
        share_memory_get();
        //建立映射
        shmaddr=shmat(shmId,NULL,0);//可读可写
        if(shmaddr==(void*)-1) print_error("shamaddr fails\n");
        while(1){
                  pause();//读之前就休眠
                  printf("%s\n",(char*)shmaddr);
                  bzero(shmaddr,SHMSIZE);
        }
        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
  • 相关阅读:
    React@16.x(40)路由v5.x(5)常见应用场景(2)- 实现类似 vue 的路由模式
    将3D MAX设计模型导入NX1988
    智能投影:坚果、当贝前攻后防
    漏洞复现--奇安信360天擎未授权访问
    左叶子之和、二叉树的所有路径。
    Windows环境下的ELK——logstash日志(2)
    《从菜鸟到大师之路 Redis 篇》
    C++特性——auto关键字、范围for、指针空值nullptr
    vue脚手架搭建2022年6月版本(保姆级)
    数学建模学习笔记(9):主成分分析法
  • 原文地址:https://blog.csdn.net/weixin_47173597/article/details/128001034