学习笔记10-学习《精通UNIX下C语言编程及项目实践》

来源:互联网 发布:淘宝联盟手机能提现吗 编辑:程序博客网 时间:2024/06/05 20:51

十四章 共享内存

  管道, 消息队列和信号量都需要借助第三方对象进行通信; 而共享内存正好弥补了这些缺陷, 它是最快的IPC对象. 在本质上, 共享内存是一端物理内存.

  共享内存简介

  共享内存中最重要的属性是内存大小和内存地址, 进程在访问共享内存前必须先将共享内存映射到进程空间的一个虚拟地址中, 然后任何对该虚拟地址的数据操作都将直接作用到物理内存上.

  共享内存由进程创建, 但是进程结束时共享内存仍然保留, 除非该共享内存被显式地删除或者重启操作系统.

  UNIX的内核采用结构shmid_ds来管理消息队列, 它的数据成员与命令'ipcs -a -m'的结果一一对应

struct shmid_ds {

        struct ipc_perm         shm_perm;       /* operation perms */

        int                     shm_segsz;      /* size of segment (bytes) */

        __kernel_time_t         shm_atime;      /* last attach time */

        __kernel_time_t         shm_dtime;      /* last detach time */

        __kernel_time_t         shm_ctime;      /* last change time */

        __kernel_ipc_pid_t      shm_cpid;       /* pid of creator */

        __kernel_ipc_pid_t      shm_lpid;       /* pid of last operator */

        unsigned short          shm_nattch;     /* no. of current attaches */

        unsigned short          shm_unused;     /* compatibility */

        void                    *shm_unused2;   /* ditto - used by DIPC */

        void                    *shm_unused3;   /* unused */

};

  命令'ipcs -m'查询系统中共享内存的基本信息:

[bill@billstone bill]$ ipcs -m

 

------ Shared Memory Segments --------

key        shmid      owner      perms      bytes      nattch     status

 

[bill@billstone bill]$

   使用共享内存

  (1) 共享内存的创建

  UNIX, 函数shmget用来创建或获取共享内存, 原型如下:

#include <sys/ipc.h>

#include <sys/shm.h>

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

  同样的, 参数shm_flgIPC_CREATIPC_EXCL两种取值.

  (2) 共享内存的映射

  与消息队列和信号量不同, 共享内存在获取标志号后, 仍需要调用shmat函数将共享内存映射到进程的地址空间后才能访问, 原型如下

#include <sys/types.h>

#include <sys/shm.h>

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

  (3) 共享内存的释放

  当进程不再需要共享内存时, 可以使用函数shmdt释放共享内存内存映射, 原型如下

#include <sys/types.h>

#include <sys/shm.h>

int shmdt(const void *shmaddr);

  (4) 共享内存的控制

  与消息队列和信号量一样, 共享内存也有自给的控制函数shmctl, 原型如下:

#include <sys/ipc.h>

#include <sys/shm.h>

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

   这里设计一个类似于命令'ipcs'和命令'ipcrm'的程序ipcshm: 它从命令行参数中获取要执行的操作, 包括创建共享内存, 读取共享内存信息和删除共享内存等, 主体代码如下代码如下

#include <sys/shm.h>

#include <sys/types.h>

#include <sys/ipc.h>

#include <stdio.h>

#include <sys/stat.h>

 

#define VerifyErr(a, b) /

        if (a) fprintf(stderr, "%s failed./n", (b)); /

        else fprintf(stderr, "%s success./n", (b));

 

int main(int argc, char *argv[])

{

        int shmid, size;

 

        if(argc != 3)

                 exit(1);

        shmid = atoi(argv[1]);

        if(strcmp(argv[2], "v") == 0){

                 StatShm(shmid);

        }

        else if(strcmp(argv[2], "d") == 0){

                 VerifyErr(shmctl(shmid, IPC_RMID, NULL) < 0, "Delete Shm");

        }

        else{

                 size = atoi(argv[2]);

                 VerifyErr(shmget(shmid, size, 0666|IPC_CREAT|IPC_EXCL) < 0, "Create Shm");

        }

 

        return 0;

}

  其中用到了两个函数: 

char * GetFileMode(mode_t st_mode, char *resp){

        if(resp == NULL)

                return 0;

        memset(resp, '-', 9);

        if(st_mode & S_IRUSR)   resp[0] = 'r';

        if(st_mode & S_IWUSR)   resp[1] = 'w';

        if(st_mode & S_IXUSR)   resp[2] = 'x';

        if(st_mode & S_IRGRP)   resp[3] = 'r';

        if(st_mode & S_IWGRP)   resp[4] = 'w';

        if(st_mode & S_IXGRP)   resp[5] = 'x';

        if(st_mode & S_IROTH)   resp[6] = 'r';

        if(st_mode & S_IWOTH)   resp[7] = 'w';

        if(st_mode & S_IXOTH)   resp[8] = 'x';

                                                                               

        return resp;

}

 

int StatShm(int shmid){

        char resp[10];

        struct shmid_ds buf;

 

        memset(&buf, 0, sizeof(buf));

        memset(resp, 0, sizeof(resp));

        shmctl(shmid, IPC_STAT, &buf);

        fprintf(stderr, "T    ID    KEY    MODE    OWNER    GROUP    NATTCH    SEGSZ    CPID    LPID/n");

    fprintf(stderr, "m %6d %#6x %s %6d %6d %6d %10d %10d %10d/n", shmid, buf.shm_perm.__key,

                 GetFileMode(buf.shm_perm.mode, resp), buf.shm_perm.uid, buf.shm_perm.gid,

                 buf.shm_nattch, buf.shm_segsz, buf.shm_cpid, buf.shm_lpid);

 

        return 0;

}

  执行情况如下:

[bill@billstone Unix_study]$ gcc -o ipcshm ipcshm.c

[bill@billstone Unix_study]$ ./ipcshm 2000 100

Create Shm success.

[bill@billstone Unix_study]$ ipcs -m

 

------ Shared Memory Segments --------

key        shmid      owner      perms      bytes      nattch     status

0x000007d0 229377     bill      666        100        0

 

[bill@billstone Unix_study]$ ./ipcshm 229377 v

T    ID    KEY    MODE    OWNER    GROUP    NATTCH    SEGSZ    CPID    LPID

m 229377  0x7d0 rw-rw-rw-    500    500      0        100       1796          0

[bill@billstone Unix_study]$ ./ipcshm 229377 d

Delete Shm success.

[bill@billstone Unix_study]$ ipcs -m

 

------ Shared Memory Segments --------

key        shmid      owner      perms      bytes      nattch     status

 

[bill@billstone Unix_study]$

  (5) 共享内存实例

  共享内存的应用可以分为打开(创建)共享内存, 映射共享内存, 读写共享内存和释放共享内存映射等四个步骤.

  程序shm1想共享内存中指定位置写入数据.

#include <sys/shm.h>

#include <sys/types.h>

#include <sys/ipc.h>

#include <stdio.h>

#include <sys/stat.h>

 

#define VerifyErr(a, b) /

        if (a) { fprintf(stderr, "%s failed./n", (b)); return; } /

        else fprintf(stderr, "%s success./n", (b));

 

int main(void)

{

        int shmid, no;

        char *pmat = NULL, buf[1024];

 

        VerifyErr((shmid = shmget(0x1234, 10*1024, 0666|IPC_CREAT)) == -1, "Open(Create) Shm");

        VerifyErr((pmat = (char *)shmat(shmid, 0, 0)) == 0, "Link Shm");

        printf("Please input NO.(0-9): ");

        scanf("%d", &no);

        VerifyErr(no<0 || no>9, "Input No.");

        printf("Please input data: ");

        memset(buf, 0, sizeof(buf));

        getchar();                // 读入'/n'回车符

        fgets(buf, sizeof(buf), stdin);

        memcpy(pmat+no*1024, buf, 1024);

        shmdt(pmat);

 

        return 0;

}

  程序shm2从共享内存指定位置读取数据

#include <sys/shm.h>

#include <sys/types.h>

#include <sys/ipc.h>

#include <stdio.h>

#include <sys/stat.h>

 

#define VerifyErr(a, b) /

        if (a) { fprintf(stderr, "%s failed./n", (b)); return; } /

        else fprintf(stderr, "%s success./n", (b));

 

int main(void)

{

        int shmid, no;

        char *pmat = NULL, buf[1024];

 

        VerifyErr((shmid = shmget(0x1234, 10*1024, 0666|IPC_CREAT)) == -1, "Open(Create) Shm");

        VerifyErr((pmat = (char *)shmat(shmid, 0, 0)) == 0, "Link Shm");

        printf("Please input NO.(0-9): ");

        scanf("%d", &no);

        VerifyErr(no<0 || no>9, "Input No.");

        memcpy(buf, pmat+no*1024, 1024);

        printf("Data: [%s]/n", buf);

        shmdt(pmat);

 

        return 0;

}

  运行结果如下

[bill@billstone Unix_study]$ make shm1

cc     shm1.c   -o shm1

[bill@billstone Unix_study]$ make shm2

cc     shm2.c   -o shm2

[bill@billstone Unix_study]$ ./shm1

Open(Create) Shm success.

Link Shm success.

Please input NO.(0-9): 1

Input No. success.

Please input data: this is a test!

[bill@billstone Unix_study]$ ./shm2

Open(Create) Shm success.

Link Shm success.

Please input NO.(0-9): 1

Input No. success.

Data: [this is a test!

]

[bill@billstone Unix_study]$

 

原创粉丝点击