linux环境高级C语言知识整理:共享内存

四、共享内存

1.基本特点
(1) 两个或者更多进程,共享同一块由系统内核负责维护的内存区域,其地址空间通常被映射到堆和栈之间。
(2) 无需复制信息,最快的一种IPC机制。
(3) 需要考虑同步访问的问题。
(4) 内核为每个共享内存,维护一个shmid_ds结构体形式的共享内存对象。

2.常用函数

 #include <sys/shm.h>

(1) 创建/获取共享内存
int shmget (key_t key, size_t size, int shmflg);
A. 该函数以key参数为键值创建共享内存,或获取已有的共享内存。
B. size参数为共享内存的字节数, 建议取内存页字节数(4096)的整数倍。
若希望创建共享内存,则必需指定size参数。
若只为获取已有的共享内存,则size参数可取0。
C. shmflg取值:
0 - 获取,不存在即失败。
IPC_CREAT - 创建,不存在即创建,
已存在即获取,除非…
IPC_EXCL - 排斥,已存在即失败。
D. 成功返回共享内存标识,失败返回-1。

(2) 加载共享内存
void* shmat (int shmid, const void* shmaddr,int shmflg);
A. 将shmid参数所标识的共享内存,映射到调用进程的地址空间。
B. 可通过shmaddr参数人为指定映射地址,也可将该参数置NULL,由系统自动选择。
C. shmflg取值:
0 - 以读写方式使用共享内存。
SHM_RDONLY - 以只读方式使用共享内存。
SHM_RND - 只在shmaddr参数非NULL时起作用。
表示对该参数向下取内存页的整数倍,作为映射地址。
D. 成功返回映射地址,失败返回-1。
E. 内核将该共享内存的加载计数加1。

(3) 卸载共享内存
int shmdt (const void* shmaddr);
A. 从调用进程的地址空间中,取消由shmaddr参数所指向的,共享内存映射区域。
B. 成功返回0,失败返回-1。
C. 内核将该共享内存的加载计数减1。

(4) 销毁/控制共享内存
int shmctl (int shmid, int cmd, struct shmid_ds* buf);
A. cmd取值:
IPC_STAT
获取共享内存的属性,通过buf参数输出。
IPC_SET
设置共享内存的属性,通过buf参数输入,仅以下三个属性可设置:
shmid_ds::shm_perm.uid
shmid_ds::shm_perm.gid
shmid_ds::shm_perm.mode
IPC_RMID
标记删除共享内存。
并非真正删除共享内存,只是做一个删除标记,禁止其被继续加载,但已有加载依然保留。
只有当该共享内存的加载计数为0时,才真正被删除。
B. 成功返回0,失败返回-1。

struct shmid_ds 
{
    
    
   struct ipc_perm shm_perm;   // 所有者及其权限
   size_t          shm_segsz;  // 大小(以字节为单位)
   time_t          shm_atime;  // 最后加载时间
   time_t          shm_dtime;  // 最后卸载时间
   time_t          shm_ctime;  // 最后改变时间
   pid_t           shm_cpid;   // 创建进程PID
   pid_t           shm_lpid;   // 最后加载/卸载进程PID
   shmatt_t        shm_nattch; // 当前加载计数
    ...
};
struct ipc_perm 
{
    
    
   key_t          __key; // 键值
   uid_t          uid;   // 有效属主ID
   gid_t          gid;   // 有效属组ID
   uid_t          cuid;  // 有效创建者ID
   gid_t          cgid;  // 有效创建组ID
   unsigned short mode;  // 权限字
   unsigned short __seq; // 序列号
};
  1. 编程模型

范例:wshm.c、rshm.c
wshm.c

#include <stdio.h>
#include <unistd.h>
#include <sys/shm.h>

int main () 
{
    
    
    printf ("创建共享内存...\n");

    key_t key = ftok (".", 100);
    if (key == -1) 
    {
    
    
        perror ("ftok");
        return -1;
    }

    int shmid = shmget (key, 4096, 0644 | IPC_CREAT | IPC_EXCL);
    if (shmid == -1) 
    {
    
    
        perror ("shmget");
        return -1;
    }

    printf ("加载共享内存...\n");

    void* shmaddr = shmat (shmid, NULL, 0);
    if (shmaddr == (void*)-1) 
    {
    
    
        perror ("shmat");
        return -1;
    }

    printf ("写入共享内存...\n");

    sprintf (shmaddr, "我是%u进程写入的数据。", getpid ());

    printf ("按<回车>卸载共享内存(0x%08x/%d)...", key, shmid);
    getchar ();

    if (shmdt (shmaddr) == -1) 
    {
    
    
        perror ("shmdt");
        return -1;
    }

    printf ("按<回车>销毁共享内存(0x%08x/%d)...", key, shmid);
    getchar ();

    if (shmctl (shmid, IPC_RMID, NULL) == -1) 
    {
    
    
        perror ("shmctl");
        return -1;
    }

    printf ("大功告成!\n");

    return 0;
}

rshm.c

#include <stdio.h>
#include <time.h>
#include <sys/shm.h>

int shmstat (int shmid) 
{
    
    
    struct shmid_ds shm;
    if (shmctl (shmid, IPC_STAT, &shm) == -1) 
    {
    
    
        perror ("shmctl");
        return -1;
    }

    printf ("------------------------------------------------\n");
    printf ("                  共享内存信息\n");
    printf ("----+----------------+--------------------------\n");
    printf (" 所 | 键值           | 0x%08x\n", shm.shm_perm.__key);
    printf (" 有 | 有效属主ID     | %u\n", shm.shm_perm.uid);
    printf (" 者 | 有效属组ID     | %u\n", shm.shm_perm.gid);
    printf (" 及 | 有效创建者ID   | %u\n", shm.shm_perm.cuid);
    printf (" 其 | 有效创建组ID   | %u\n", shm.shm_perm.cgid);
    printf (" 权 | 权限字         | %#o\n", shm.shm_perm.mode);
    printf (" 限 | 序列号         | %u\n", shm.shm_perm.__seq);
    printf ("----+----------------+--------------------------\n");
    printf (" 大小(字节)          | %u\n", shm.shm_segsz);
    printf (" 最后加载时间        | %s", ctime (&shm.shm_atime));
    printf (" 最后卸载时间        | %s", ctime (&shm.shm_dtime));
    printf (" 最后改变时间        | %s", ctime (&shm.shm_ctime));
    printf (" 创建进程ID          | %u\n", shm.shm_cpid);
    printf (" 最后加载/卸载进程ID | %u\n", shm.shm_lpid);
    printf (" 当前加载计数        | %ld\n", shm.shm_nattch);
    printf ("---------------------+--------------------------\n");

    return 0;
}

int shmset (int shmid) 
{
    
    
    struct shmid_ds shm;
    if (shmctl (shmid, IPC_STAT, &shm) == -1) 
    {
    
    
        perror ("shmctl");
        return -1;
    }

    shm.shm_perm.mode = 0600;
    shm.shm_segsz = 8192;

    if (shmctl (shmid, IPC_SET, &shm) == -1) 
    {
    
    
        perror ("shmctl");
        return -1;
    }

    return 0;
}

int main () 
{
    
    
    printf ("获取共享内存...\n");

    key_t key = ftok (".", 100);
    if (key == -1) 
    {
    
    
        perror ("ftok");
        return -1;
    }

    int shmid = shmget (key, 0, 0);
    if (shmid == -1) 
    {
    
    
        perror ("shmget");
        return -1;
    }

    printf ("加载共享内存...\n");

    void* shmaddr = shmat (shmid, NULL, 0);
    if (shmaddr == (void*)-1) 
    {
    
    
        perror ("shmat");
        return -1;
    }

    shmstat (shmid);

    printf ("读取共享内存...\n");

    printf ("共享内存(0x%08x/%d):%s\n", key, shmid, (char*)shmaddr);

    printf ("卸载共享内存...\n");

    if (shmdt (shmaddr) == -1) 
    {
    
    
        perror ("shmdt");
        return -1;
    }

    shmstat (shmid);

    printf ("设置共享内存...\n");

    shmset (shmid);

    shmstat (shmid);

    printf ("大功告成!\n");

    return 0;
}

猜你喜欢

转载自blog.csdn.net/LUCAS004/article/details/108430883