Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1279303
  • 博文数量: 213
  • 博客积分: 7590
  • 博客等级: 少将
  • 技术积分: 2185
  • 用 户 组: 普通用户
  • 注册时间: 2008-08-31 17:31
个人简介

热爱开源,热爱linux

文章分类

全部博文(213)

文章存档

2018年(4)

2017年(1)

2015年(1)

2014年(5)

2013年(2)

2012年(2)

2011年(21)

2010年(82)

2009年(72)

2008年(23)

分类: LINUX

2010-08-03 16:10:28


1.共享内存区的数据结构
每个共享内存段在内核中维护着一个内部结构shmid_ds,该结构定义在linux/shm.h中,代码如下所示:
struct shmid_ds {
struct ipc_perm  shm_perm;/* 超作许可权数据结构指针 */
    int              shm_segsz;     /* 共享存储段大小 (bytes) */
    time_t          shm_atime;    /* 最后调用shmat时间 */
    time_t          shm_dtime;    /* 最后调用shmdt的时间 */
    time_t          shm_ctime;    /* 最后调用shmctl的改变的时间 */
    unsigned short  shm_cpid;   /*创建者的进程ID */
    unsigned short  shm_lpid;   /* 最后对共享存储段进行操作的进程ID */
short           shm_nattch;   /* 当前连接数 */
};
2.共享内存的创建和操作
2.1共享内存区的创建

linux下使用函数shmget来创建一个共享内存区,或者访问一个已经存在的共享内存区,该函数定义如下:

#include
#include
int shmget(key_t key, size_t size, int shmflg);
返回:失败-1, 成功返回非负的共享存储段id
第一个参数key是共享存储关键字。它有特殊值IPC_PRIVATE表示总是创建一个进程私有的共享存储段。
当key值不等于IPC_PRIVATE时,shmget动作取决于最后一个参数shmflg标志:
1. IPC_CREAT 单独设置此标志,当系统中不存在相同key时,创建一个新的共享存储段,否则返回已存在的共享存储段。
2. IPC_EXCL 单独设置不起作用。与 IPC_CREAT同时设置时,当系统中存在相同key时,错误返回。保证不会打开一个已存在的共享存储段。
如果没有指定IPC_CREATE并且系统中不存在相同key值的共享存储段,将失败返回。
第三个参数也可以设置共享存储段的访问权限,用或于上面的值操作。
第二个参数size指明要求的共享存储段的大小。当key指定的共享存储段已存在时,取值范围为0和已存在共享段大小。或简单指定为0。
成功后此函数返回共享存储段id,同时创建于参数key相连的数据结构shmid_ds。此节构为系统内部使用。
2.2共享内存区的操作
2.2.1在使用共享内存区之前,必须通过shmat函数将其附加到进程的地址空间,进程和地址空间就建立了连接。shmat调用成功后就返回一个共享内存区指针,使用该指针就可以访问共享内存区了。
#include
#include
void *shmat(int shmid, const void *shmaddr, int shmflg);
返回:失败-1并置errno, 成功返回连接的实际地址
第一个参数,必须由shmget返回的存储段的id
第二个参数指明共享存储段要连接到的地址。0,系统为我们创建一个适当的地址值。否则自己指定
第三个参数shmflg可以设成:SHM_RND, SHM_RDONLY。SHM_RND为自己指定连接地址时用。SHM_RDONLY说明此存储段只读。
2.2.2当进程结束使用共享内存区时,就要通过函数shmdt断开于共享内存区的连接。函数介绍如下:
#include
#include
int shmdt(const void * shmaddr);
返回:失败-1并置errno, 成功返回0
分离由shmaddr指定的存储段。此值应该由shmat返回的值。
此函数不是删除共享存储段,而是从当前进程分离存储段。
当进程推出时,系统会自动分离它连接的所有共享段。
3共享内存区的控制
 #include
#include
存储段控制函数。可获得shmid_ds全部内容
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
返回:失败-1并置errno, 成功返回0
第一个参数,必须由shmget返回的存储段的id。cmd为指定要求的操作

CMD    说明    参数
IPC_STAT    放置与shmid相连的shmid_ds结构当前值于buf所指定用户区    buf
IPC_SET    用buf指定的结构值代替与shmid相连的shmid_ds结构值    buf
IPC_RMID    删除制定的信号量集合     
SHM_LOCK    锁住共享存储段。只能由超级管理员使用     
SHM_UNLOCK    unlock共享存储段。只能由超级管理员使用   

下面是《linux实战编程》上面的里一个例子:
sharemem.h

#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 error1\n");
        return -1;
    }

    if ((sid = semget (msgkey,members,IPC_CREAT |0666)) == -1) {
        perror("semget call failed!");
        return -1;
    }

    /*初始化操作*/
    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 error1\n");
        return -1;
    }

    if ((sid = semget (msgkey,0,IPC_CREAT |0666)) == -1) {
        perror("semget call failed!");
        return -1;
    }

    return (sid);
}

/*P操作函数*/
int sem_p(int semid,int index)
{
    struct sembuf buf = {0,-1,IPC_NOWAIT};
    if (index < 0) {
        perror("index of array can not equals a minus value!");
        return -1;
    }
    buf.sem_num = index;
    if (semop(semid ,&buf,1) == -1) {
        perror("a wrong operations to semaphore occured!");
        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 can not equals a minus value!");
        return -1;
    }
    buf.sem_num = index;
    if (semop(semid ,&buf,1) == -1) {
        perror("a wrong operations to semaphore occured!");
        return -1;
    }
    return 0;
}

/*删除信号集函数*/
int sem_delete(int semid)
{
    return (semctl(semid,0,IPC_RMID));
}

/*等待信号集函数*/
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("shmkey ftok error! \n");
        return -1;
    }

    if ((sid = shmget(shmkey,size,IPC_CREAT | 0666)) == -1) {
        perror("shmget call failed!\n");
        return -1;
    }
    return (sid);
}


writer.c

#include <string.h>
#include "sharemem.h"
int main(void)
{
    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("attach shared memory error!\n");
        exit (1);
    }

    if ((semid = createsem(".",'s',1,1)) == -1) {
        exit(1);
    }

    while(1) {
        wait_sem(semid,0);
        sem_p(semid,0);

        printf("write:");
        fgets(write_str,1024,stdin);
    int    len = strlen(write_str) -1;
        write_str[len] = '\0';
        strcpy(shmaddr,write_str);
        sleep(2);/*使reader处于阻塞状态*/
        sem_v(semid,0); /*v操作*/
        sleep(2);/*等待reader进行读操作*/
    }
    return 0;
}


reader.c

#include "sharemem.h"
int main(void)
{
    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("attach shared memory error!\n");
        exit (1);
    }

    if ((semid = opensem(".",'s')) == -1) {
        exit(1);
    }

    while(1) {
        printf("reader:");
        wait_sem(semid,0); /*等待信号值为1*/
        sem_p(semid,0);

        printf("%s\n",shmaddr);
        sleep(2);/*使writer处于阻塞状态*/
        sem_v(semid,0); /*v操作*/
        sleep(2);/*等待write进行写操作*/
    }
    return 0;
}


程序运行如下:
gcc -o writer wrtter.c
gcc -o reader reader.c
然后在两个终端中运行reader和writer就可以看见writer和reader的同步。
阅读(2998) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~