下面就来讨论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指向的地址中。
下面看一个读者写者问题来演示共享内存区跟信号量怎么配合使用。这里的读者写者问题要求一个进程读共享内存的时候,其他进程不能写内存,当一个进程写内存时,其他进程不能读内存。
- #include<stdio.h>
- #include<stdlib.h>
- #include<unistd.h>
- #include<sys/types.h>
- #include<sys/ipc.h>
- #include<sys/sem.h>
- #include<sys/shm.h>
- #include<errno.h>
- #define SHM_SIZE 1024
- union semun{
- int val;
- struct semid_ds *buf;
- unsigned short *array;
- };
- //创建信号量函数
- int createsem(const char *pathname,int proj_id,int members,int init_val)
- {
- key_t msgkey;
- int index,sid;
- union semun semopts;
- if((msgkey=ftok(pathname,proj_id))==-1)
- {
- perror("ftok error!\n");
- exit(0);
- }
- if((sid=semget(msgkey,members,IPC_CREAT|0666))==-1)
- {
- perror("msgget error !\n");
- exit(0);
- }
- //初始化操作
- semopts.val=init_val;
- for(index=0;index<members;index++)
- {
- semctl(sid,index,SETVAL,semopts);
- }
- return (sid);
- }
- //打开信号量函数
- int opensem(const char *pathname,int proj_id)
- {
- key_t msgkey;
- int sid;
- if((msgkey=ftok(pathname,proj_id))==-1)
- {
- perror("ftok error!\n");
- exit(0);
- }
- if((sid=semget(msgkey,0,IPC_CREAT|0666))==-1)
- {
- perror("msgget error!\n");
- exit(0);
- }
- }
- //p操作函数
- int sem_p(int semid,int index)
- {
- struct sembuf buf={0,-1,IPC_NOWAIT};
- if(index<0)
- {
- perror("index of array cannot equals a minus value!\n");
- return -1;
- }
- buf.sem_num=index;
- if(semop(semid,&buf,1)==-1)
- {
- perror("a wrong operation to semaphore occurred\n");
- return -1;
- }
- return 0;
- }
- //v操作函数
- int sem_v( int semid,int index)
- {
- struct sembuf buf={0,+1,IPC_NOWAIT};
- if(index<0)
- {
- perror("index of array cannot equals a minus value!\n");
- return -1;
- }
- buf.sem_num=index;
- if(semop(semid,&buf,1)==-1)
- {
- perror("a wrong operation to semaphore occurred\n");
- return -1;
- }
- return 0;
- }
- //删除信号集函数
- int sem_delete(int semid)
- {
- return (semctl(semid,0,IPC_RMID));
- }
- //等待信号为1
- int wait_sem(int semid,int index)
- {
- while(semctl(semid,index,GETVAL,0)==0)
- {
- sleep(1);
- }
- return 1;
- }
- //创建共享内存函数
- int createshm(char *pathname,int proj_id,size_t size)
- {
- key_t shmkey;
- int sid;
- //获取键值
- if((shmkey=ftok(pathname,proj_id))==-1)
- {
- perror("ftok error!\n");
- return -1;
- }
- if((sid=shmget(shmkey,size,IPC_CREAT|0666))==-1)
- {
- perror("shmget error!\n");
- return -1;
- }
- return (sid);
- }
writer.c程序
- #include<string.h>
- #include"sharemem.h"
- int main()
- {
- int semid,shmid;
- char *shmaddr;
- char write_str[SHM_SIZE];
- if((shmid=createshm(".",'m',SHM_SIZE))==-1)
- {
- exit(1);
- }
- if((shmaddr=shmat(shmid,(char *)0,0))==(char *)-1)
- {
- perror("shmat error!\n");
- exit(1);
- }
- if((semid=createsem(".",'s',1,1))==-1)
- {
- exit(1);
- }
- while(1)
- {
- wait_sem(semid,0);
- sem_p(semid,0);
- printf("writer: ");
- fgets(write_str,1024,stdin);
- int len=strlen(write_str)-1;
- strcpy(shmaddr,write_str);
- if(strncmp(write_str,"exit",4)==0)
- {
- exit(1);
- }
-
- sleep(10);
- sem_v(semid,0);
- sleep(10);
- }
- }
reader.c程序
- #include<string.h>
- #include"sharemem.h"
- int main()
- {
- int semid,shmid;
- char *shmaddr;
- if((shmid=createshm(".",'m',SHM_SIZE))==-1)
- {
- exit(1);
- }
- if((shmaddr=shmat(shmid,(char *)0,0))==(char *)-1)
- {
- perror("shmat error!\n");
- exit(1);
- }
- if((semid=opensem(".",'s'))==-1)
- {
- exit(1);
- }
- while(1)
- {
- printf("reader: ");
- wait_sem(semid,0);
- sem_p(semid,0);
-
- sleep(10);
- printf("%s\n",shmaddr);
- if(strncmp(shmaddr,"exit",4)==0)
- {
- exit(0);
- }
- sem_v(semid,0);
- sleep(10);
- }
其中的reader.c和writer.c两个程序,在进入共享区之前,首先检查信号集中的值是否为1(相当于是否能进入共享区),如果不为1,调用sleep()函数进入睡眠态直到信号的值变为1,进入共享区之后,将信号的值减1(相当于加锁),这样就实现了互斥访问共享资源,在退出共享内存区时,将信号值加1(相当于解锁)。
分别在两个终端运行两个程序。
阅读(1232) | 评论(0) | 转发(0) |