zoukankan      html  css  js  c++  java
  • 【linux高级程序设计】(第十一章)System V进程间通信 4

    共享内存

    共享内存主要用于实现进程间大量数据传输。

    共享内存的数据结构定义:

    系统对共享内存的限制:

    共享内存与管道的对比:

    可以看到,共享内存的优势:

    1.共享内存只需复制2次,而管道需要4次

    2.共享内存不需要切换内核态与用户态,而管道需要。

    共享内存效率高!

    int shmget (key_t __key, size_t __size, int __shmflg) :创建共享内存

    第一个参数:key值

    第二个参数:欲创建的共享内存段的大小(字节)

    第三个参数:shmflg创建标识,包括IPC_CREAT, IPC_EXCL, IPC_NOWAIT, SHM_R(可读), SHM_W(可写)

    int shmctl (int __shmid, int __cmd, struct shmid_ds *__buf) :共享内存控制

    第一个参数:要操作的共享内存标识符

    第二个参数:要执行的操作,IPC_RMID, iPC_SET, IPC_STAT, IPC_INFO, 超级用户还有 SHM_LOCK(锁定共享内存段), SHM_UNLOCK(解锁共享内存段)

    第三个参数:临时共享内存变量信息

    void *shmat (int __shmid, __const void * __shmaddr, int __shmflg) : 挂载共享内存到当前进程,返回共享内存首地址

    第一个参数:要操作的共享内存标识符

    第二个参数:指定共享内存映射地址,如果是0,系统选择。

    第三个参数:指定共享内存段的访问权限和映射条件,0表示可读可写

    int shmdt (__const void *__shmaddr) :把共享内存与当前进程分离,参数为共享内存映射地址

    测试,在只读共享内存中写信息:

    #include<unistd.h>
    #include<stdlib.h>
    #include<stdio.h>
    #include<sys/types.h>
    #include<sys/ipc.h>
    #include<sys/shm.h>
    #include<errno.h>
    
    int main(int argc, char * argv[])
    {
        key_t key;
        int shm_id;
        char *ptr;
        key = ftok("/", 10);
        shm_id = shmget(key, 100, IPC_CREAT|SHM_R); //创建shm
        printf("get the shm id is %d
    ", shm_id);  //打印id
        if((ptr = (char *)shmat(shm_id, NULL, SHM_RDONLY)) == NULL)  //只读方式挂载
        {
            if(shmctl(shm_id, IPC_RMID, NULL) == -1)  //如果失败则删除
                perror("Failed to remove memory segment");
            exit(EXIT_FAILURE);
        }
        //打印挂载地址
        printf("the attach add is %p
    ", ptr);
        printf("now try to write the memory
    ");
        *ptr = 'd';
        printf("*ptr =%c
    ", *ptr);
        shmdt(ptr);
        shmctl(shm_id, IPC_RMID, 0);
    }

    发生段错误:

    父子进程间对共享内存的约定:

    • fork()的子进程继承父进程挂载的共享内存。
    • 调用exec执行新程序,则共享内存被自动卸载。
    • 如果调用了exit(),挂载的共享内存与当前进程脱离关系。

    下面是一个应用的例子

    实现两个没有亲缘关系进程的通信,一个负责写,另一个负责接收。用信号量实现同步,即写的时候不可读,读的时候不可写。用一元信号量实现,0表示可写,1表示可读

    注意:在代码实现中,实际上是读写轮流操作的,即写一次,读一次。并没有达到真正多进程的效果。

    代码经验证,可以使用

    发送端代码:

    #include<unistd.h>
    #include<stdlib.h>
    #include<stdio.h>
    #include<string.h>
    #include<sys/types.h>
    #include<sys/ipc.h>
    #include<sys/shm.h>
    #include<sys/sem.h>
    #include<errno.h>
    
    int main(int argc, char *argv[])
    {
        int running = 1;
        int shid;
        int semid;
        int value;
        void *sharem = NULL;
        struct sembuf sem_b;
        sem_b.sem_num = 0;
        sem_b.sem_flg = SEM_UNDO;
        //创建信号量
        if((semid = semget((key_t)123456, 1, 0666|IPC_CREAT)) == -1) 
        {
            perror("semget");
            exit(EXIT_FAILURE);
        }
        //初始化信号量为0
        if(semctl(semid,0,SETVAL,0) == -1)
        {
            printf("sem init error");
            if(semctl(semid, 0, IPC_RMID, 0) != 0)
            {
                perror("semctl");
                exit(EXIT_FAILURE);
            }
            exit(EXIT_FAILURE);
        }
        //创建共享内存
        shid = shmget((key_t)654321, (size_t)2048, 0600|IPC_CREAT);  //创建共享内存
        if(shid == -1)
        {
            perror("shmget");
            exit(EXIT_FAILURE);
        }
        //挂载共享内存到当前进程
        sharem = shmat(shid, NULL, 0);
        if(sharem == NULL)
        {
            perror("shmat");
            exit(EXIT_FAILURE);
        }
        while(running)
        {
            //测试信号量值,如果为0则可写
            if((value = semctl(semid, 0, GETVAL)) == 0)
            {
                printf("write data operate
    ");
                printf("please input something:");
                scanf("%s", sharem);
                sem_b.sem_op = 1;
                //执行信号量加1操作,允许读
                if(semop(semid, &sem_b, 1) == -1)
                {
                    fprintf(stderr, "semaphore_p failed
    ");
                    exit(EXIT_FAILURE);
                }
            }
            //比较是否是结束符号
            if(strcmp(sharem, "end") == 0)
                    running--;
        }
        shmdt(sharem);
            return 0;
    }

    接收端代码:

    #include<unistd.h>
    #include<stdlib.h>
    #include<stdio.h>
    #include<string.h>
    #include<sys/types.h>
    #include<sys/ipc.h>
    #include<sys/shm.h>
    #include<sys/sem.h>
    #include<errno.h>
    
    int main(int argc, char *argv[])
    {
        int running = 1;
        int shid;
        int semid;
        int value;
        void *sharem = NULL;
        struct sembuf sem_b;
        sem_b.sem_num = 0;
        sem_b.sem_flg = SEM_UNDO;
        //创建信号量
        if((semid = semget((key_t)123456, 1, 0666|IPC_CREAT)) == -1) 
        {
            perror("semget");
            exit(EXIT_FAILURE);
        }
        //创建共享内存
        shid = shmget((key_t)654321, (size_t)2048, 0600|IPC_CREAT);  //创建共享内存
        if(shid == -1)
        {
            perror("shmget");
            exit(EXIT_FAILURE);
        }
        //挂载共享内存到当前进程
        sharem = shmat(shid, NULL, 0);
        if(sharem == NULL)
        {
            perror("shmat");
            exit(EXIT_FAILURE);
        }
        while(running)
        {
            //测试信号量值,如果为1则可读
            if((value = semctl(semid, 0, GETVAL)) == 1)
            {
                printf("read data operate
    ");
                sem_b.sem_op = -1;
                //执行信号量减1操作,允许写
                if(semop(semid, &sem_b, 1) == -1)
                {
                    fprintf(stderr, "semaphore_p failed
    ");
                    exit(EXIT_FAILURE);
                }
                printf("%s
    ", sharem);
            }
            //比较是否是结束符号
            if(strcmp(sharem, "end") == 0)
                    running--;
        }
        shmdt(sharem);
        //删除共享内存
        if(shmctl(shid, IPC_RMID, 0) != 0)
        {
            perror("shmctl");
            exit(EXIT_FAILURE);
        }
        //删除信号量
        if(semctl(semid, 0, IPC_RMID, 0) != 0)
        {
            perror("semctl");
            exit(EXIT_FAILURE);
        }
        return 0;
    }
  • 相关阅读:
    Linux sed命令
    Linux之read命令使用
    Linux shell之数组
    Linux Shell脚本攻略:shell中各种括号()、(())、[]、[[]]、{}的作用
    shell中的${},##, %% , :- ,:+, ? 的使用
    hostname命令
    进度条的制作-python
    33 Python 详解命令解析
    Python 学习笔记 多进程 multiprocessing--转载
    Spring注解(生命周期)
  • 原文地址:https://www.cnblogs.com/dplearning/p/4687747.html
Copyright © 2011-2022 走看看