• linux 共享内存


    共享内存是最高效的IPC机制,因为它不涉及进程之间的任何数据传输。这种高效带来的问题是,我们必须用其他手段来同步进程对共享内存的访问,否则会产生竞态条件。所以,共享内存通常和其他进程间通信方式一起使用。

    linux 共享内存有四个系统调用:shmget, shmat, shmdt, shmctl

    shmget

    创建一段新的共享内存,或者获取一段已经存在的共享内存:

           #include <sys/ipc.h>
           #include <sys/shm.h>
    
           int shmget(key_t key, size_t size, int shmflg);

    key 是一个键值,用来标识一段全局唯一的共享内存。size 指定内存的大小,单位是字节。如果是创建内存,size必须指定,如果是获取已经存在的内存,size 可以设置为0

    shmflg 和 semget的 semflg 参数相同。

    shmget成功时返回一个正整数,它是共享内存的标识符。失败时返回-1,并设置errno.

    shmat

    共享内存被创建/获取之后,并不能立即访问它,而是需要先将它关联到进程的地址空间中。使用shmat函数:

    void *shmat(int shmid, const void *shmaddr, int shmflg)

    shmid 参数是有shmget函数返回的标识符。shmaddr指定将共享内存关联到进程的哪块地址空间,如果为NULL,则被关联地址由操作系统选择。

    成功时,返回共享内存被关联到的地址,失败则返回(void*)-1,并设置errno

    shmdt

    int shmdt(const void *shmaddr);

    将关联的共享内存从进程分离。

    成功返回0,失败返回-1,并设置errno

    shmctl

    int shmctl(int shmid, int cmd, struct shmid_ds *buf)

    shmid 使用shmget返回的共享内存标识符。cmd指定执行的命令,常用的 IPC_RMID,表示将共享内存打上删除的标记,当最后一个使用它的进程调用shmdt将它从进程中分离时,该共享内存就被删除了。

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <errno.h>
    #include <unistd.h>
    #include <sys/wait.h>
    #include <sys/shm.h>
    #include <sys/ipc.h>
    #include <sys/types.h>
    #include <sys/sem.h>
    
    union semun
    {
        int              val;    /* Value for SETVAL */
        struct semid_ds *buf;    /* Buffer for IPC_STAT, IPC_SET */
        unsigned short  *array;  /* Array for GETALL, SETALL */
        struct seminfo  *__buf;  /* Buffer for IPC_INFO
                                   (Linux-specific) */
    };
    
    void ErrExit(const char* reason)
    {
        fprintf(stderr, "%s: %d, %s
    ", reason, errno, strerror(errno));
        exit(1);
    }
    
    int initsem(int key = 0)
    {
        int semid = -1;
    
        if (-1 == (semid = semget(key, 1, 0666 | IPC_CREAT)))
        {
            ErrExit("semget");
        }
    
        union semun un;
        un.val = 2;
        if (-1 == semctl(semid, 0, SETVAL, un))
        {
            ErrExit("semctl");
        }
    
        return semid;
    }
    
    void destroysem(int semid)
    {
        if (-1 == semctl(semid, 0, IPC_RMID))
        {
            ErrExit("semctl del");
        }
    }
    
    void P(int semid)
    {
        struct sembuf op;
        op.sem_num = 0;
        op.sem_op = -1;
        op.sem_flg = SEM_UNDO;
        if (-1 == semop(semid, &op, 1))
        {
            ErrExit("semop p");
        }
    }
    
    void V(int semid)
    {
        struct sembuf op;
        op.sem_num = 0;
        op.sem_op = 1;
        op.sem_flg = SEM_UNDO;
        if (-1 == semop(semid, &op, 1))
        {
            ErrExit("semop v");
        }
    }
    
    void waitfor0(int semid)
    {
        struct sembuf op;
        op.sem_num = 0;
        op.sem_op = 0;
        op.sem_flg = SEM_UNDO;
        if (-1 == semop(semid, &op, 1))
        {
            ErrExit("semop wait");
        }
    }
    
    
    
    int main(int argc, char const *argv[])
    {
        int semid = initsem();
        int shmid = -1;
    
        int pipefd[2];
    
        // 管道用来传递 共享内存id
        pipe(pipefd);
    
        pid_t pid = fork();
        if (pid > 0)
        {
            P(semid);
            close(pipefd[0]);
            printf("parent process...
    ");
    
            shmid = shmget(0, 1024, 0666 | IPC_CREAT);
            if (-1 == shmid)
            {
                ErrExit("shmget");
            }
    
            write(pipefd[1], &shmid, 4);
    
            void* p = shmat(shmid, NULL, 0);
            if ((void*)-1 == p)
            {
                ErrExit("shmat");
            }
    
            char buf[60] = "data from parent";
            memcpy(p, buf, strlen(buf));
    
            printf("exit parent process...
    ");
            P(semid);
            // P(semid);
            waitpid(pid, NULL, 0);
    
            destroysem(semid);
            if (-1 == shmctl(shmid, IPC_RMID, NULL))
            {
                ErrExit("shmctl");
            }
            if (-1 == shmdt(p))
            {
                ErrExit("shmdt in parent");
            }
        }
        else if (0 == pid)
        {
            // 等待共享内存集的值变为0
            waitfor0(semid);
            close(pipefd[1]);
            printf("child process...
    ");
    
            read(pipefd[0], &shmid, 4);
            
            void* p = shmat(shmid, NULL, 0);
            if ((void*)-1 == p)
            {
                ErrExit("shmat");
            }
    
            char buf[60] = {0};
            memcpy(buf, p, 60);
            printf("%s
    ", buf);
    
            if (-1 == shmdt(p))
            {
                ErrExit("shmdt in parent");
            }
    
            // V(semid);
        }
    
    
        return 0;
    }
  • 相关阅读:
    Rock Pi开发笔记(二):入手Rock Pi 4B plus(基于瑞星微RK3399)板子并制作系统运行
    麒麟系统开发笔记(一):国产麒麟系统搭建开发环境之虚拟机安装
    linux实用技巧:在虚拟机vmware16软件上安装CentOs8.2虚拟机,重置可用源和安装输入法
    CentOS8安装Geant4笔记(三):Geant4介绍、编译、安装支持Qt5界面并运行exampleB1例程显示Qt界面
    CentOS8安装Geant4笔记(二):CentOS8安装Qt5.15.2并测试运行环境
    【图片+代码】:GCC 链接过程中的【重定位】过程分析
    嵌入式软件开发书籍推荐
    Commit Message 规范
    Android 12 适配指南——SplashScreen
    Android12 新特性及适配指南
  • 原文地址:https://www.cnblogs.com/zuofaqi/p/9644204.html
Copyright © 2020-2023  润新知