Linux教程Linux
首页
基础知识
Linux业界
Linux系统
Linux人物
Linux文化
Linux资讯
Linux综合
当前位置:
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 来自:信息产业部数据通信技
学习Linux网络编程(4)
关於 DDoS 攻击事件的探讨
Linux的桌面时代来了吗?
Linux优势探寻
oss安装声卡实战
Linux下基于web的邮件服务IMP的简介与安装
Linux网络管理员手册(6)
Linux系统中OpenSSH的安装和配置之三
Linux网络驱动程序编写(二)
LVS集群系统网络核心原理分析
Linux开机程序内幕(1)
GYUM 2.0 for Fedora Core 3
特洛依防范八招
高级Linux安全管理技巧
从程序员的角度看Linux和windows的对比
站点导航
Linux教程
Php
Linux
非技术类
指令大全
Shell
安装启动
Xwindow
Kde
Gnome
输入法类
美化汉化
网络配置
存储备份
杂项工具
编程技术
网络安全
内核技术
速度优化
Apache
Email
Ftp服务
Cvs服务
代理服务
Samba
域名服务
网络过滤
其他服务
Nfs
Oracle
Dhcp
Mysql
Ldap
RedHat
赞助商链接