当前位置:Linux教程 - Linux - Unix下信号灯和共享内存的使用方法(之二)

Unix下信号灯和共享内存的使用方法(之二)



         共享内存的使用 3.1 共享内存的概念 进程间通信(IPC)的几种方法中,管道、命名管道和消息队列传递数据时都需要经过内核缓冲区。而使用共享内存的方法,不同进程之间拷贝数据可以直接从指定内存存取。事实上,共享内存是几种IPC方法中效率最高的一种。 当某个进程正在使用一段共享内存时,其它进程必须等待该操作结束才能使用。这种协调机制可以用信号灯来实现。下面是用信号灯实现共享内存的示意图:
    内核为每个共享内存维护一个如下的数据结构: struct shmid_ds{ struct ipc_perm shm_perm; /* 操作权限 */ int shm_segsz; /* 内存段大小 */ struct tagStruType shm_Struct; /* 内存结构和地址 */ unsigned short shm_lpid; /* 最后一次操作内存的进程号 */ unsigned short shm_cpid; /* 创建该内存的进程号 */ unsigned short shm_nattach; /* 当前连接上内存的进程号 */ usnigned short shm_cnattach; /* 内核维护的内存连接号 */ time_t shm_atime; /* 最后一次连接时间 */ time_t shm_dtime; /* 最后一次脱离时间 */ time_t shm_ctime; /* 最后一次改变时间 */ }; 其中shm_perm与第2节中信号灯的权限结构相同。结构tagStruType是指内核用来指向该共享内存段的指针,它与硬件结构和内核的具体实现有关。 3.2 关于共享内存操作的系统调用 系统为共享内存操作提供了4个调用:shmget, shmat, shmdt, shmctl。这些调用都使用了以下头文件: #include #include #include 下面是系统调用的详细说明。 3.2.1 系统调用shmget 作用:创建或打开一个共享内存段,但不访问该段。 形式:int shmget(key_t key, size_t size, int shmflg); 返回值:成功时返回共享内存段的shmid,错误是返回-1; 描述: key是该共享内存的键值。size确定其大小。shmflg指明该内存段的创建方式,其含义与信号灯的创建方式相同,见表2-1。注意shmget只是创建或打开一个共享内存段,但并不能够访问该段。 3.2.2系统调用shmat 作用:连接(attach)共享内存段。 形式:char* shmat(int shmid, const void *shmaddr, int shmflg); 返回值:成功时返回共享内存首地址,失败返回-1; 描述: shmid是共享内存号。shmaddr确定内存地址。shmflg确定连接方式,有如下2个标志:SHM_RND和SHM_RDONLY。连接规则如下: 1. 如果shmflg=0则内核选择当前第一个可用地址作为共享内存地址; 2. 如果shmflg!=0且设置了SHM_RND,共享内存地址为shmaddr; 3. 如果shmflg!=0且没有设置SHM_RND,共享内存地址为shmaddr且使用SHMLBA表示地址循环。 在实际应用中,由于直接指定内存地址存在许多问题,一般由系统自动分配,即指定shmaddr=0。 共享内存创建后缺省是可读写的,如果置shmflg的SHM_RDONLY位,则该段共享内存就是只读的。 shmat的返回值是指向某个地址的指针,因此我们可以强制它返回我们需要的结构的数据地址,例如我们可以定义一个数据结构struMesg,在连接内存时返回该结构的地址: struMesg *pMesg; pMesg=(struMesg*)shmat(shmid, (const void*)0, 0); 也就是说共享内存操作的可以是结构化的数据,不仅仅限于一个字符串。 一旦连接上了共享内存,就可以通过返回的地址指针对它进行任何操作,就象使用本进程自己的数据一样。 3.2.3 系统调用shmdt 作用:脱离(detach)共享内存段。 形式:int shmdt( const void *shmaddr); 返回值:成功时返回0,失败返回-1; 描述: 使该进程脱离该共享内存段,但并不删除该段共享内存。 3.2.4 系统调用shmctl 作用:提供了对共享内存段的多种控制。 形式:int shmctl(int shmid, int cmd, struct shmid_ds *buf); 返回值:成功时返回0,失败返回-1; 描述: 将cmd和buf结合使用,对共享内存的操作根据cmd的以下值而定: IPC_RMID:删除共享内存shmid。操作权限是超级用户、shm_perm.cuid或shm_per.uid。 IPC_SET:将buf中以下成员的值赋给共享内存的shmid_ds: shm_perm.uid shm_perm.gid shm_perm.mode /* 低9位有效 */ 操作权限是超级用户、shm_perm.cuid或shm_per.uid。 IPC_STAT:获取共享内存的shmid_ds值赋给buf指向的数据结构。 SHM_LOCK:锁住共享内存shmid。操作权限是超级用户。 SHM_UNLOCK:解锁共享内存shmid。操作权限是超级用户。 4 例子 下面我们通过一套程序来演示信号灯对操作共享内存的作用。create.out用于创建信号灯和共享内存。delete.out用于删除信号灯和共享内存。write.out向共享内存写两次数据,中间停顿10秒。read.out从共享内存读数据并显示出来。 我们先以后台方式运行write.out,在其停顿期间运行read.out。结果是read.out立即挂起,直到write.out停顿结束并写入第二次数据后read.out被唤醒,接着read.out读取并显示了第二次数据。 为了验证信号灯的作用,我们可以将程序中所有涉及到信号灯的操作注释掉并重新编译。我们仍然先以后台方式运行write.out,在其停顿期间运行read.out。结果显示read.out立即执行并显示第一次数据;等到write.out停顿结束并写入第二次数据以后,再次运行read.out,显示了第二次数据。 该例子的源文件如下所示,包括:shm_sem.h, semoper.h, create.c, delete.c, read.c, write.c。 该套程序在SCO Unix 5.0.4上编译并运行通过。 /**************************************************************************** * * 文件名: shm_sem.h * 说明: 包含信号灯和共享内存需要的头文件和自定义宏、类型。 * ****************************************************************************/ #include #include #include #include

    #define sem_key 6998l #define shm_key 6999l #define SEM_PERM 0600 #define SHM_PERM 0600

    typedef struct { int iMesgLen; char* MesgTxt[30]; }struMesg;

    /**************************************************************************** * * 文件名:semoper.h * 说明: 定义对信号灯操作的基本函数。 * ****************************************************************************/ void LockSem(); void UnlkSem();

    static struct sembuf LockOp[2]= { 0,0,0, 0,1,SEM_UNDO }; static struct sembuf UnlckOp[1]= { 0,-1,(IPC_NOWAIT|SEM_UNDO) }; struMesg *pMesg; int shmid,semid;

    /* 加锁信号灯 */ void LockSem() { if((semid=semget(sem_key,1,SEM_PERM|IPC_CREAT))==-1) { perror(“LockSem() Get semaphore error“); } if(semop(semid,&LockOp[0],2)==-1) { perror(“LockSem() Lock semaphore error“); } } /* 解锁信号灯 */ void UnlkSem() { if((semid=semget(sem_key,1,SEM_PERM|IPC_CREAT))==-1) { perror(“UnlkSem() Get semaphore error“); } if(semop(semid,&UnlckOp[0],1)==-1) { perror(“UnlkSem() Unlock semaphore error“); } } /**************************************************************************** * * 文件名:create.c * 说明: 创建信号灯和共享内存。 * ****************************************************************************/ #include“shm_sem.h“

    main() { struMesg *pMesg;

    /* 创建信号灯 */ if(semget(sem_key,1,SEM_PERM|IPC_CREAT|IPC_EXCL)==-1) { perror(“Create semaphore error“); } /* 创建共享内存 */ if(shmget(sem_key,sizeof(struMesg),SEM_PERM|IPC_CREAT|IPC_EXCL)==-1) { perror(“Create share memory error“); } }

    /**************************************************************************** * * 文件名: delete.c * 说明: 删除指定信号灯和共享内存。 * ****************************************************************************/ #include“shm_sem.h“

    main() { struMesg *pMesg; int shmid,semid;

    /* 获取共享内存shmid */ if((shmid=shmget(sem_key,sizeof(struMesg),SEM_PERM|IPC_CREAT))==-1) { perror(“Get share memory error“); } /* 删除共享内存shmid if(shmctl(shmid,IPC_RMID,0)==-1) { perror(“Remove share memory error“); }

    /* 获取信号灯semid */ if((semid=semget(sem_key,1,SEM_PERM|IPC_CREAT))==-1) { perror(“Get semaphore error“); } /* 删除信号灯semid */ if(semctl(semid,0,IPC_RMID,0)==-1) { perror(“Remove semaphore memory error“); } } /**************************************************************************** * * 文件名:write.c * 说明: 申请信号灯并向共享内存写两次数据,中间间隔10秒。 * ****************************************************************************/ #include #include“shm_sem.h“ #include“semoper.h“

    main() { /* 获取共享内存shmid */ if((shmid=shmget(sem_key,sizeof(struMesg),SEM_PERM|IPC_CREAT))==-1) { perror(“write.out Get share memory error“); } /* 连接共享内存shmid */ if((pMesg=(struMesg*)shmat(shmid,(const void*)0,0))==(struMesg*)-1) { perror(“write.out Attach memory error“); }

    /* 加锁信号灯 */ LockSem();

    /* 往共享内存写第一次数据。 */ memcpy(pMesg->MesgTxt,“This is the first copy.“,30); pMesg->iMesgLen=23; printf(”First data written.n”)

    sleep(10); /* 停顿10秒 */

    /* 往共享内存写第一次数据。 */ memcpy(pMesg->MesgTxt,“This is the second copy.“,30); pMesg->iMesgLen=24; printf(”Second data written.n”)

    /* 解锁信号灯 */ UnlkSem();

    /* 脱离共享内存 */ if(shmdt(pMesg)==-1) { perror(“write.out detach memory error“); } }



    /**************************************************************************** * * 文件名:read.c * 说明: 申请信号灯并从共享内存读取数据。 * ****************************************************************************/ #include #include“shm_sem.h“ #include“semoper.h“

    main() { /* 获取共享内存shmid */ if((shmid=shmget(sem_key,sizeof(struMesg),SEM_PERM|IPC_CREAT))==-1) { perror(“write.out Get share memory error“); } /* 连接共享内存 */ if((pMesg=(struMesg*)shmat(shmid,(const void*)0,0))==(struMesg*)-1) { perror(“write.out Attach memory error“); }

    /* 加锁信号灯 */ LockSem();

    printf(“read.out get from memory:n“); printf(“---MesgTxt is:%sn“,pMesg->MesgTxt); printf(“---iMesgLen is %dn“,pMesg->iMesgLen);

    /* 解锁信号灯 */ UnlkSem();

    /* 脱离共享内存 */ if(shmdt(pMesg)==-1) { perror(“write.out detach memory error“); } }
    发布人:Crystal 来自:信息产业部数据通信技