Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1483351
  • 博文数量: 148
  • 博客积分: 2234
  • 博客等级: 大尉
  • 技术积分: 3225
  • 用 户 组: 普通用户
  • 注册时间: 2012-05-17 21:34
个人简介

未来很长。

文章存档

2017年(7)

2016年(4)

2015年(1)

2014年(6)

2013年(31)

2012年(99)

分类: C/C++

2012-08-03 16:09:42

下面就来讨论Linux通信的另一个机制:共享内存。
共享内存就是分配一块能被其他进程访问的内存,和消息队列、信号量一样每个内存段在内核中维护着一个内部结构shmid_ds,该结构定义在头文件linux/shm.h中。
1、共享内存的创建与操作:
共享内存是存在于内核级别的一种资源,在shell中可以使用ipcs命令来查看当前系统IPC中的状态,在文件系统/proc目录下有对其描述的相应文件。函数shmget可以创建或打开一块共享内存区。函数原型如下:
#include
int shmget( key_t key, size_t size, int flag );
函数中参数key用来变换成一个标识符,而且每一个IPC对象与一个key相对应。当新建一个共享内存段时,size参数为要请求的内存长度(以字节为单位)。
注意:内核是以页为单位分配内存,当size参数的值不是系统内存页长的整数倍时,系统会分配给进程最小的可以满足size长的页数,但是最后一页的剩余部分内存是不可用的。
当打开一个内存段时,参数size的值为0。参数flag中的相应权限位初始化ipc_perm结构体中的mode域。同时参数flag是函数行为参数,它指定一些当函数遇到阻塞或其他情况时应做出的反应。

2、在使用共享内存区前,必须通过shmat函数将其附加到进程的地址空间。此时进程与共享内存就建立的链接,shmat调用成功就会返回一个指向共享内存区的指针,使用该指针就可以访问共享内存区了,如果失败则返回-1。
shmat
作用:共享内存区对象映射到调用进程的地址空间
核心处理函数: void *shmat( int shmid , char *shmaddr , int shmflag );shmat()是用来允许本进程访问一块共享内存的函数。
int shmid是那块共享内存的ID。
char *shmaddr是共享内存的起始地址
t shmflag是本进程对该内存的操作模式。如果是SHM_RDONLY的话,就是只读模式。其它的是读写模式
成功时,这个函数返回共享内存的起始地址。失败时返回-1.
3、共享内存区的控制:
Linux对共享内存区的控制是通过调用函数shmctl来完成的。原型如下:
#include
int shmctl(int shmid,int cmd,struct shmid_ds *buf);
函数在中,shmid为共享内存区的标识符,buf为指向shmid_ds结构体的指针,cmd为操作标志位,支持以下3种控制操作:
IPC_RMID:从系统中删除由shmid标识的共享内存区
IPC_SET:设置共享内存区的shmid_ds结构。
IPC_STAT:读取共享内存区shmid_ds结构,并将其存储到buf指向的地址中。
下面看一个读者写者问题来演示共享内存区跟信号量怎么配合使用。这里的读者写者问题要求一个进程读共享内存的时候,其他进程不能写内存,当一个进程写内存时,其他进程不能读内存。


点击(此处)折叠或打开

  1. #include<stdio.h>
  2. #include<stdlib.h>
  3. #include<unistd.h>
  4. #include<sys/types.h>
  5. #include<sys/ipc.h>
  6. #include<sys/sem.h>
  7. #include<sys/shm.h>
  8. #include<errno.h>
  9. #define SHM_SIZE 1024
  10. union semun{

  11.     int val;
  12.     struct semid_ds *buf;
  13.     unsigned short *array;
  14. };
  15. //创建信号量函数
  16. int createsem(const char *pathname,int proj_id,int members,int init_val)
  17. {
  18.     key_t msgkey;
  19.     int index,sid;
  20.     union semun semopts;
  21.     if((msgkey=ftok(pathname,proj_id))==-1)
  22.     {
  23.         perror("ftok error!\n");
  24.         exit(0);
  25.     }
  26.     if((sid=semget(msgkey,members,IPC_CREAT|0666))==-1)
  27.     {
  28.         perror("msgget error !\n");
  29.         exit(0);
  30.     }
  31.     //初始化操作
  32.     semopts.val=init_val;
  33.     for(index=0;index<members;index++)
  34.     {
  35.         semctl(sid,index,SETVAL,semopts);
  36.     }
  37.     return (sid);
  38. }
  39. //打开信号量函数
  40. int opensem(const char *pathname,int proj_id)
  41. {
  42.     key_t msgkey;
  43.     int sid;
  44.     if((msgkey=ftok(pathname,proj_id))==-1)
  45.     {
  46.         perror("ftok error!\n");
  47.         exit(0);
  48.     }
  49.     if((sid=semget(msgkey,0,IPC_CREAT|0666))==-1)
  50.     {
  51.         perror("msgget error!\n");
  52.         exit(0);
  53.     }


  54. }
  55. //p操作函数
  56. int sem_p(int semid,int index)
  57. {
  58.     struct sembuf buf={0,-1,IPC_NOWAIT};
  59.     if(index<0)
  60.     {
  61.         perror("index of array cannot equals a minus value!\n");
  62.         return -1;
  63.     }
  64.     buf.sem_num=index;
  65.     if(semop(semid,&buf,1)==-1)
  66.     {
  67.         perror("a wrong operation to semaphore occurred\n");
  68.         return -1;
  69.     }
  70.     return 0;
  71. }
  72. //v操作函数
  73. int sem_v( int semid,int index)
  74. {
  75.     struct sembuf buf={0,+1,IPC_NOWAIT};
  76.     if(index<0)
  77.     {
  78.          perror("index of array cannot equals a minus value!\n");
  79.          return -1;
  80.     }
  81.     buf.sem_num=index;
  82.     if(semop(semid,&buf,1)==-1)
  83.     {
  84.         perror("a wrong operation to semaphore occurred\n");
  85.         return -1;
  86.     }
  87.     return 0;
  88. }
  89. //删除信号集函数
  90. int sem_delete(int semid)
  91. {
  92.     return (semctl(semid,0,IPC_RMID));
  93. }
  94. //等待信号为1
  95. int wait_sem(int semid,int index)
  96. {
  97.     while(semctl(semid,index,GETVAL,0)==0)
  98.     {
  99.         sleep(1);
  100.     }
  101.     return 1;
  102. }
  103. //创建共享内存函数
  104. int createshm(char *pathname,int proj_id,size_t size)
  105. {
  106.     key_t shmkey;
  107.     int sid;
  108.     //获取键值
  109.     if((shmkey=ftok(pathname,proj_id))==-1)
  110.     {
  111.         perror("ftok error!\n");
  112.         return -1;
  113.     }
  114.     if((sid=shmget(shmkey,size,IPC_CREAT|0666))==-1)
  115.     {
  116.         perror("shmget error!\n");
  117.         return -1;
  118.     }
  119.     return (sid);
  120. }


writer.c程序

点击(此处)折叠或打开

  1. #include<string.h>
  2. #include"sharemem.h"
  3. int main()
  4. {
  5.     int semid,shmid;
  6.     char *shmaddr;
  7.     char write_str[SHM_SIZE];
  8.     if((shmid=createshm(".",'m',SHM_SIZE))==-1)
  9.     {
  10.         exit(1);
  11.     }
  12.     if((shmaddr=shmat(shmid,(char *)0,0))==(char *)-1)
  13.     {
  14.         perror("shmat error!\n");
  15.         exit(1);
  16.     }
  17.     if((semid=createsem(".",'s',1,1))==-1)
  18.     {
  19.         exit(1);
  20.     }
  21.     while(1)
  22.     {
  23.         wait_sem(semid,0);
  24.         sem_p(semid,0);
  25.         printf("writer: ");
  26.         fgets(write_str,1024,stdin);
  27.         int len=strlen(write_str)-1;
  28.         strcpy(shmaddr,write_str);

  29.         if(strncmp(write_str,"exit",4)==0)
  30.         {
  31.             exit(1);
  32.         }
  33.     
  34.         sleep(10);
  35.         sem_v(semid,0);
  36.         sleep(10);
  37.     }
  38. }
reader.c程序

点击(此处)折叠或打开

  1. #include<string.h>
  2. #include"sharemem.h"
  3. int main()
  4. {
  5.     int semid,shmid;
  6.     char *shmaddr;

  7.     if((shmid=createshm(".",'m',SHM_SIZE))==-1)
  8.     {
  9.         exit(1);
  10.     }
  11.     if((shmaddr=shmat(shmid,(char *)0,0))==(char *)-1)
  12.     {
  13.         perror("shmat error!\n");
  14.         exit(1);
  15.     }
  16.     if((semid=opensem(".",'s'))==-1)
  17.     {
  18.         exit(1);
  19.     }
  20.     while(1)
  21.     {
  22.         printf("reader: ");
  23.         wait_sem(semid,0);
  24.         sem_p(semid,0);
  25.     
  26.         sleep(10);
  27.         printf("%s\n",shmaddr);

  28.         if(strncmp(shmaddr,"exit",4)==0)
  29.         {
  30.             exit(0);
  31.         }

  32.         sem_v(semid,0);
  33.         sleep(10);
  34.     }

其中的reader.c和writer.c两个程序,在进入共享区之前,首先检查信号集中的值是否为1(相当于是否能进入共享区),如果不为1,调用sleep()函数进入睡眠态直到信号的值变为1,进入共享区之后,将信号的值减1(相当于加锁),这样就实现了互斥访问共享资源,在退出共享内存区时,将信号值加1(相当于解锁)。
分别在两个终端运行两个程序。














阅读(1195) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~