并发网络编程学习之路(二):多进程与进程池(续)

转自:http://book.51cto.com/art/200903/114895.htm

3.3共享内存

3.3.1共享内存的概念

        在系统内核为一个进程分配内存地址进,通过分布机制可以让一个进程的物理地址不连续,同时也可以让一段内存同时分配给不同的进程。共享机制就是通过该原理来实现的,共享内存机制只是提供数据的传送,如何控制服务器端和客户端的读写操作互斥,这就需要一些其他的辅助工具,例如记录锁。

        对于每一个共享存储段,内核分为其维护一个shmid_ds类型的结构体(shmid_ds结构定义在头文件<sys/shm.h>中)。shmid_ds结构体定义如下:

 

struct shmid_ds{
    struct ipc_perm shm_pem;
    size_t   shm_segsz;
    pid_T shm_lpid;
    pid_t shm_cpid;
    shmatt_t shm_nattch;
    time_t shm_atime;
    time_t shm_dtime;
    time_t shm_ctime;
    ....
};


 

       结构体shmid_ds会根据不同的系统内核版本而略有不同,并且在不同的系统中会对共享存储段的大小有限制,在应用时请查询相应系统手册。

3.3.2 共享内存的创建

      共享内存是存在于内核级别的一种资源,在shell中可以使用ipcs命令来查看当前系统IPC中的状态,在文件系统/proc目录下有对其描述的相应文件。函数shmget可以创建或打开一块共享内存区。函数原型如下:

#include <sys/shm.h>
 
int shmget(key_t key, size_t size, int flag);

        函数中参数key用来变换成一个标识符,而且每一个IPC对象与一个Key相对应。当新建一个共享内存段时,size参数为要请求的内存长度(以字节为单位)。

      注意:内核是以页为单位分配内存,当size参数的值不是系统内存页长的整数倍时,系统会分配给进程最小的可以满足size长的页数,但是最后一页的剩余部分是不可用的。

      当打开一个内存段时,参数size的值为0。参数flag中的相应权限位初始化ipc_perm结构体中的mode域。同时参数flag是函数行为参数,它指定一些当函数遇到阻塞或其他情况时应做出的反应。shmid_ds结构初始化如下:

shmid_ds结构数据

初 值

shmid_ds结构数据

初 值

shm_lpid

0

shm_dtime

0

shm_nattach

0

shm_ctime

系统当前值

shm_atime

0

shm_segsz

参数 size

        下面实例演示了使用shmget函数创建一块共享内存。程序中在调用shmget函数时指定key参数值IPC_PRIVATE,这个参数的意义是创建一个机关报的共享内存区,当创建成功后使用shell命令ipcs来显示目前系统下共享内存的状态。命令参数-m为显示共享内存的状态。

?
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdlib.h>
#include <stdio.h>
 
#define BUFSZ 4096
 
int main ( void )
{
    int shm_id;    /*共享内存标识符*/
 
    shm_id=shmget(IPC_PRIVATE, BUFSZ, 0666 ) ;
    if (shm_id < 0 ) {  /*创建共享内存*/
       perror( "shmget" ) ;
       exit ( 1 );
    }
 
    printf ( "successfully created segment : %d \n", shm_id ) ;
    system( "ipcs -m");     /*调用ipcs命令查看IPC*/
 
    exit( 0 );
}

3.3.3共享内存的操作

      由于共享内存这一特殊资源类型,使它不同于普通的文件,因此,系统需要为其提供专有的操作函数,而这无疑增加了程序员开发的难度(需要记忆额外的专有函数)。使用函数shmctl可以对共享内存进行多种操作,其函数原型如下:

 

#include <sys/shm.h>
 
int shmctl(int shm_id, int cmd, struct shmid_ds *buf);

     函数中参数shm_id为所要操作的共享内存的标识符,struct shmid_ds型指针参数buf的作用与参数cmd的值相关,参数cmd指明了所要进行的操作,其解释如下:

表14-5  shmctl函数中参数 cmd详解

cmd的值

意    义

IPC_STAT

取shm_id所指向内存共享段的shmid_ds

结构,对参数buf指向的结构赋值

IPC_SET

使用buf指向的结构对sh_mid段的相关结

构赋值,只对以下几个域有作用,shm_perm.

uid shm_perm.gid以及shm_perm.mode

注意此命令只有具备以下条件的进程才可以请求:

1.进程的用户ID等于shm_perm.cuid或者

等于shm_perm.uid

2.超级用户特权进程

IPC_RMID

删除shm_id所指向的共享内存段,只有当

shmid_ds结构的shm_nattch域为零时,才

会真正执行删除命令,否则不会删除该段

注意此命令的请求规则与IPC_SET命令相同

SHM_LOCK

锁定共享内存段在内存,此命令只能由超级用户请求

SHM_UNLOCK

对共享内存段解锁,此命令只能由超级用户请求

 

 

       使用函数shmat将一个存在的共享内存段连接到本进程空间,其函数原型如下:

 

#include <sys/shm.h>
 
void *shmat(int shm_id, const void *add, int flag);

      函数中参数shm_id指定要引入的共享内存,参数addr与flag组合说明要引入的地址值,通常只有2种用法:addr为0 ,表明让内核来决定第1个可以引入的位置;非零,并且 flag中指定SHM_RND,则此段引入到addr所指向的位置(此操作不推荐使用,因为不会只对一种硬件上运行应用程序,为了程序的可移植性推荐使用前一种方法)。在flag参数中可以指定要引入的方式(读写方式指定)。

      说明:函数成功执行返回值为实际引入的地址,失败返回-1。shmat函数成功执行会将shm_id段的shmid_ds结构的shm_nattch计数器的值加1。

     当对共享内存段操作结束时,应调用shmdt函数,作用是将指定的共享内存段从当前进程空间中脱离出去。函数原型如下:

 

#include <sys/shm.h>
 
int shmdt(void *addr);

      参数addr是调用shmat函数的返回值,函数执行成功返回0,并将该共享内存的shmid_ds结构的shm_nattch计数器减1,失败返回-1。

      下面实例演示了操作共享内存段的流程。程序的开始部分检测用户是否有输入,如出错则打印该命令的使用帮助。接下来从命令行读取将要引入的共享内存ID,使用shmat函数引入该共享内存,并在分离内存之前睡眠3秒以方便查看系统IPC状态:

 

?
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdlib.h>
#include <stdio.h>
 
int main ( int argc, char *argv[] )
{
int shm_id ;
char * shm_buf;
 
if ( argc != 2 ){ /* 命令行参数错误 */
printf ( "USAGE: atshm <identifier>" );   /*打印帮助消息*/
exit (1 );
}
 
shm_id = atoi(argv[1]);         /*得到要引入的共享内存段*/
 
/*引入共享内存段,由内核选择要引入的位置*/
if ( (shm_buf = shmat( shm_id, 0, 0)) < (char *) 0 ){
perror ( "shmat" );
exit (1);
}
 
printf ( " segment attached at %p\n", shm_buf );   /*输出导入的位置*/
system("ipcs -m");
 
sleep(3);      /* 休眠 */
 
 if ( (shmdt(shm_buf)) < 0 ) { /*与导入的共享内存段分离*/
perror ( "shmdt");
exit(1);
}
 
printf ( "segment detached \n" );
system ( "ipcs -m " );   /*再次查看系统IPC状态*/
 
exit ( 0 );
}

 

3.3.4共享内存使用注意事项

      共享内存相比其他几种方式有着更方便的数据控制能力,数据在读写过程中会更透明。当成功导入一块共享内存后,它只是相当于一个字符串指针来指向一块内存, 有进程下用户可以随意的访问。缺点是数据写入进程或数据读出进程中,需要附加的数据结构控制,在共享内存段中都是以字符串的默认结束符为一条信息的的结尾。每个进程在读写时都遵守这个规则,就不会破坏数据的完整性。

 

 二、进程池

       由于网上进程池资料不多,只找到两个,本人也没有弄明白,现将链接贴出,留作备份:

              Linux下多进程设计模式

              一个简单的进程池实例


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值