• 【进程】进程通信-共享内存


    共享内存

      共享内存是系统出于多个进程之间通讯的考虑,而预留的的一块内存区。在/proc/sys/kernel/目录下,记录着共享内存的一些限制,如一 个共享内存区的最大字节数shmmax,系统范围内最大共享内存区标识符数shmmni等,可以手工对其调整,但不推荐这样做。

    应用

      共享内存的使用,主要有以下几个API:ftok()、shmget()、shmat()、shmdt()及shmctl()。

    ftok()

      用ftok()函数获得一个ID号.

      应用说明:

      在IPC中,我们经常用用key_t的值来创建或者打开信号量,共享内存和消息队列。

      函数原型:

    key_t ftok(const char *pathname, int proj_id);

       Keys:

    1. pathname一定要在系统中存在并且进程能够访问的
    2. proj_id是一个1-255之间的一个整数值,典型的值是一个ASCII值。

      当成功执行的时候,一个key_t值将会被返回,否则-1被返回。我们可以使用strerror(errno)来确定具体的错误信息。

      考虑到应用系统可能在不同的主机上应用,可以直接定义一个key,而不用ftok获得:

    #define IPCKEY 0x344378

     

    shmget()

      shmget()用来开辟/指向一块共享内存的函数

      应用说明:
      shmget()用来获得共享内存区域的ID,如果不存在指定的共享区域就创建相应的区域。

      函数原型:

    int shmget(key_t key, size_t size, int shmflg);

      key_t key 是这块共享内存的标识符。

    如果是父子关系的进程间通信的话,这个标识符用IPC_PRIVATE来代替。

    如果两个进程没有任何关系,所以就用ftok()算出来一个标识符(或者自己定义一个)使用了。

    int size 是这块内存的大小.
    int flag 是这块内存的模式(mode)以及权限标识。

      模式可取如下值:        

    • IPC_CREAT 新建(如果已创建则返回目前共享内存的id)
    • IPC_EXCL   与IPC_CREAT结合使用,如果已创建则则返回错误

    然后将“模式” 和“权限标识”进行“或”运算,做为第三个参数。
    如:    IPC_CREAT | IPC_EXCL | 0640   

      例子中的0666为权限标识,4/2/1 分别表示读/写/执行3种权限,第一个0是UID,第一个6(4+2)表示拥有者的权限,第二个4表示同组权限,第3个0表示他人的权限。
      这个函数成功时返回共享内存的ID,失败时返回-1。

      注意事项:

      创建共享内存时,shmflg参数至少需要 IPC_CREAT | IPC_EXCL权限标识,如果只有IPC_CREAT 则申请的地址都是k=0xffffffff,不能使用;
      获取已创建的共享内存时,shmflg不要用IPC_CREAT(只能用创建共享内存时的权限标识,如0640),否则在某些情况下,比如用ipcrm删除 共享内存后,用该函数并用IPC_CREAT参数获取一次共享内存(当然,获取失败),则即使再次创建共享内存也不能成功,此时必须更改key来重建共享 内存。

     shmat()

      shmat()将这个内存区映射到本进程的虚拟地址空间。

      函数原型:

    void    *shmat( int shmid , char *shmaddr , int shmflag );

      

      shmat()是用来允许本进程访问一块共享内存的函数。

    • int shmid是那块共享内存的ID。
    • char *shmaddr是共享内存的起始地址,如果shmaddr为0,内核会把共享内存映像到调用进程的地址空间中选定位置;如果shmaddr不为0,内核会把共享内存映像到shmaddr指定的位置。所以一般把shmaddr设为0。
    • int shmflag是本进程对该内存的操作模式。如果是SHM_RDONLY的话,就是只读模式。其它的是读写模式

      成功时,这个函数返回共享内存的起始地址。失败时返回-1。

     

     shmdt()

       shmdt()函数删除本进程对这块内存的使用,shmdt()与shmat()相反,是用来禁止本进程访问一块共享内存的函数。

    函数原型:

    int shmdt( char *shmaddr );

      参数char *shmaddr是那块共享内存的起始地址。
      成功时返回0。失败时返回-1。

    shmctl()

      shmctl() 控制对这块共享内存的使用

      函数原型:

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

      int shmid是共享内存的ID。
      int cmd是控制命令,可取值如下:

    •   IPC_STAT        得到共享内存的状态
    •   IPC_SET         改变共享内存的状态
    •   IPC_RMID        删除共享内存

      struct shmid_ds *buf是一个结构体指针。IPC_STAT的时候,取得的状态放在这个结构体中。如果要改变共享内存的状态,用这个结构体指定。
      返回值:   成功:0
                    失败:-1

    共享内存示例

     1 #include <sys/ipc.h>
     2 #include <sys/shm.h>
     3 #include <string.h>
     4 #include <stdio.h>
     5 #include <stdlib.h>
     6 
     7 #define IPCKEY 0x366378
     8 
     9 typedef struct
    10 {
    11     char agen[10];
    12     unsigned char file_no;
    13 } st_setting;
    14 
    15 int main(int argc, char** argv)
    16 { 
    17     int shm_id;
    18     key_t key;
    19     st_setting *p_setting;
    20 
    21     //首先检查共享内存是否存在,存在则先删除
    22     shm_id = shmget(IPCKEY ,1028,0640); //得到共享内存,内存大小,权限位 
    23     if(shm_id != -1)//共享内存存在
    24     {
    25         p_setting = (st_setting*)shmat(shm_id,NULL,0);//映射共享内存,自动完成映射
    26         if ( p_setting != (void *)-1)
    27         {
    28             shmdt(p_setting);
    29             shmctl(shm_id,IPC_RMID,0) ;
    30         }
    31     }
    32 
    33     shm_id=shmget(IPCKEY,1028,0640|IPC_CREAT|IPC_EXCL); 
    34     if(shm_id==-1)
    35     {
    36         printf("shmget error
    ");
    37         return -1;
    38     }
    39     //将这块共享内存区附加到自己的内存段
    40     p_setting=(st_setting*)shmat(shm_id,NULL,0);
    41 
    42     strncpy(p_setting->agen,"成鹏",10); 
    43     printf( "agen:%s
    ",p_setting->agen );
    44 
    45     p_setting->file_no = 1;
    46     printf( "file_no:%d
    ",p_setting->file_no );
    47 
    48     system("ipcs -m");//此时可看到有进程关联到共享内存的信息,nattch为1
    49 
    50     //将这块共享内存区从自己的内存段删除出去
    51     if(shmdt(p_setting) == -1)
    52         perror(" detach error ");
    53 
    54     system("ipcs -m");//此时可看到有进程关联到共享内存的信息,nattch为0
    55 
    56     //删除共享内存
    57     if (shmctl( shm_id , IPC_RMID , NULL ) == -1)
    58         perror(" delete error ");
    59 
    60     //exit(0);
    61 
    62 }
    View Code

      

    注意事项

      在使用共享内存,结束程序退出后。如果你没在程序中用shmctl()删除共享内存的话,一定要在命令行下用ipcrm命令删除这块共享内存。你要是不管的话,它就一直在那儿放着了。

    ipcs命令和ipcrm命令

     

    取得ipc信息:
    ipcs [-m|-q|-s]
    -m     输出有关共享内存(shared memory)的信息
    -q      输出有关信息队列(message queue)的信息
    -s      输出有关“遮断器”(semaphore)的信息
    %ipcs -m

    删除ipc
    ipcrm -m|-q|-s shm_id
    %ipcrm -m 105

    ftok陷阱 

      采用ftok来生成key的情况下,如果ftok的参数pathname指定文件被删除后重建,则文件系统会赋予这个同名文件(或目录)新的i节点信息,于是这些进程所调用的ftok虽然都能正常返回,但得到的键值却并不能保证相同。

  • 相关阅读:
    到底如何设置 Java 线程池的大小?
    面试一个 3 年 Java 程序员,一个问题都不会!
    Spring Boot 集成 Ehcache 缓存,三步搞定!
    牛逼哄哄的 "零拷贝" 是什么?
    一个 Java 字符串到底有多少个字符?
    不用找了,300 分钟帮你搞定 Spring Cloud!
    五分钟搞懂 Linux 重点知识,傻瓜都能学会!
    如何设计一个完美的权限管理模块?
    Redis基础都不会,好意思出去面试?
    .net c# MVC提交表单的4种方法
  • 原文地址:https://www.cnblogs.com/lcw/p/3236338.html
Copyright © 2020-2023  润新知