




已阅读5页,还剩25页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
钢筋混凝土的UNIX C编程技巧(一、内存映射表)前言: 大学毕业后从事unix上的银行综合业务系统开发工作已有一年半的时间,向众多前辈高手学习了很多经验和技巧,自己也创新了些好的开发技术,特写出来与奋斗在一线的unix程序员们共享。本人大学时专注于windows平台应用开发,工作后才转入unix平台,故沿袭了不少windows编码风格。 - 正文: 在一个带有数据库的unix系统中进行E-SQL嵌入式开发,必然用到很多混合式编程方式。当系统对表的SELECT操作频繁时,会使数据库效率大幅下降。于是我们很当然的这样设计:当应用开始运行时把数据库中需要频繁查询的表装载入共享内存,通过编写一批共享内存查询函数实现对表数据的快速查询、定位。这里借用windows的一些名词把这一技术命名为“内存映射表”技术。 内存映射表的格式设计有很多方式,下面介绍一下我设计的一种格式,该格式已经应用于某省级银行信用卡全省大前置系统,取得非常好的效果。 | | | | | | 内存映射表记录条数 | 第一条记录结构单元 | 第二条记录结构单元 | . | | 10个字节 | 记录结构的大小 | 记录结构的大小 | | | | | | | 共享内存数据存放格式如上图所示。开头的10个字节存放内存映射表的记录条数数值,由于标准c的有符号长整数类型最大值约为21亿,所以预留10个字节存放ASCII编码的记录条数数值已绰绰有余且取得最大限度值了。第11个字节开始存放数据库表第一条记录对应的c语言结构体,称为一个结构单元。后面依次存放所有数据库表记录形成结构体数组。 一张数据库表装载入一块共享内存,可以通过表名给共享内存的ipckey取名。比如“公共系统参数表”对应的内存映射表的ipckey在头文件里这样添加#define SHMY_KEY_GGXTCS 0x00001138 /* 4408 */,以便于在程序里引用。 内存映射表共占用共享内存大小为该表记录对应的数据结构体大小乘以记录条数加上10个字节。比如“公共系统参数表”记录条数为10条,表定义如下。那么总占用共享内存大小=(20+30+40)*10+10=910个字节。 字段名 字段属性 长度 空值标志 备注 包括中文注释和取值范围 csxh char 20 N.N 参数序号 csz char 30 N.N 参数值 cssm char 40 参数说明 索引1 unique csxh 内存映射表的操作大致有装载和查询两种操作,其它还可以有简单的更新操作。考虑到每个内存映射表的操作大致一样以及以某个关键字段查询、更新操作的相似性,再以“公共系统参数表”我这样设计内存映射表的操作函数原形: int LoadMapGGXTCS(); int FetchMapGGXTCS ( void *pvCondValue , struct REPLACE_STRUCT_TYPE *pREPLACE_STRUCT_ARG , int (* REPLACE_FUNCNAME_COMPARE_PROC) ( void *pvCondValue , struct REPLACE_STRUCT_TYPE *pREPLACE_STRUCT_ARG ) ); int UpdateMapGGXTCS ( void *pvCondValue , void *pvUpdateValue , int (* REPLACE_FUNCNAME_UPDATE_PROC) ( void *pvCondValue , void *pvUpdateValue , struct REPLACE_STRUCT_TYPE *pREPLACE_STRUCT_ARG ) ); 两个函数内所有涉及到具体表名、结构体名、回调函数名我都已宏的方式替换掉,这样做的好处是可以形成代码模板,如果以后要添加一张表的映射只要复制代码模板到实现文件的最后面,把代码模板最前面的宏定义成具体的值。代码模板最后面把所有用过的宏都反定义掉,不妨碍后面的程序使用。 装载表函数我不用多说了,即把表数据装载入共享内存,不需要参数。 查询函数第一个参数为关键字段值,与REPLACE_FUNCNAME_COMPARE_PROC回调函数配合使用。参数类型为void*类型,这样就可以兼容所有类型的数据甚至是结构体、共用体,额外麻烦的只是把变量传入前强制传换成void*,在回调函数里再转换回具体的变量类型。第二个参数是结构体宏,用于函数成功返回时把符合要求的记录结构体返回。第三个参数是指向回调函数的指针,其作用是针对某一关键字段,分别取出共享内存里的每条记录进行比较,当条件符合时,回调函数返回0,否则返回1,这样可以不改变外层遍历函数的条件下,使用不同判断方式、不同的判断值对内存映射表中所有记录进行遍历。 本文最后附件中附有装载、查询和更新三个内存映射表的代码模板,由于完全采用参数化宏替换方式设计,甚至可以不加修改的立即应用到您的系统中去。 下面跳跃的介绍一下查询函数极其回调函数的操作原理。更新函数原理与之相似,结构稍稍复杂一些,重点是回调函数。 查询函数 . 遍历内存映射表中所有结构记录 调用遍历函数最后一个参数即回调函数指针(条件值,当前结构记录); 如果回调函数返回0,即条件符合且更新成功 复制内存映射表中当前结构记录内容到pREPLACE_STRUCT_ARG指针空间里,输出给用户 跳出遍历; 偏移到内存映射表中下一条结构记录; . 返回回调函数的返回值 查询回调函数(条件值,内存映射表中当前结构记录) 把便利函数传入的条件值强制转换成char*类型; 与内存映射表中当前结构记录的csxh进行比较,如果相等即找到 返回0; 否则 返回1; 查询函数的使用示例 . /* 获取 对帐场次 */ iRt = FetchMapGGXTCS( (void *)dzcc , &stGgxtcs , &CompareKeyFromGGXTCSWhereCSXHProc ); if( iRt != 0 ) printf( 获取对帐场次 失败 ); else printf( 当前对帐场次为%s , stGgxtcs.csz ); . 使用该设计通过创建与数据库表映射的共享内存数据提高对数据库静态表(运行时数据内容不改变或者只做少量更新的表)的查询访问速度,而不需要额外占用数据库宝贵的效率,尤其在一个对数据库操作频繁的系统中能很大程度的提高整个系统的运行效率。本设计也有不足之处,主要是只能代替数据库简单的SELECT操作和UPDATE操作,不支持INSERT、DELETE操作,不过对于一些静态表的查询使用已经足够了,不是吗 _ - 附件一、内存映射表 代码模块(以“公共系统参数表”为例) /* * 获取 公共系统参数表 内存映射表 相应记录 * */ #define REPLACE_STRUCT_TYPE ggxtcs #define REPLACE_STRUCT_ARG stGgxtcs #define REPLACE_SHEKEY SHMY_KEY_GGXTCS #define REPLACE_TABLENAME ggxtcs #define REPLACE_TABLEDESC 公共系统参数表 #define REPLACE_CURSORNAME curGGXTCS #define REPLACE_DBVAR R_GGXTCS #define REPLACE_DBVARFUNC pubVtoSGgxtcs #define REPLACE_FUNCNAME_FETCH FetchMapGGXTCS #define REPLACE_FUNCNAME_LOAD LoadMapGGXTCS #define REPLACE_FUNCNAME_COMPARE_PROC CompareKeyFromGGXTCSProc #define REPLACE_FUNCNAME_UPDATE_PROC UpdateValueFromGGXTCSProc int FetchMapGGXTCS ( void *pvCondValue , struct REPLACE_STRUCT_TYPE *pREPLACE_STRUCT_ARG , int (* REPLACE_FUNCNAME_COMPARE_PROC) ( void *pvCondValue , struct REPLACE_STRUCT_TYPE *pREPLACE_STRUCT_ARG ) ); int iReturnValue; struct REPLACE_STRUCT_TYPE *pmpREPLACE_STRUCT_ARG; char *pmp; char acRecordAmount11; long lRecordAmount; long l; _IPC_ID_T ipcid; /* 判断 内存映射表 是否存在 */ iReturnValue = IPCIsShareMemoryExist( REPLACE_SHEKEY ); if( iReturnValue = IPC_SHAREMEMORY_RETURN_ISNT_EXIST ) /* 若不存在 则创建之 */ iReturnValue = LoadMapGGXTCS(); if( iReturnValue != 0 ) WriteLog( gacLogFilename, %s | REPLACE_FUNCNAME_FETCH | LOG_LINELEN | 创建 内存映射表 REPLACE_TABLEDESC 失败 错误码%d errno%d,请重启应用n, GetLocalTimeString( gacTimeStringBuffer , 256 , %Y-%m-%d %H:%M:%S ), _LINE_, iReturnValue, errno ); return -1; /* 打开 内存映射表 */ ipcid = IPCOpenShareMemory( REPLACE_SHEKEY ); if( ipcid 0 ) /* 打开失败,写出错日志,函数返回 */ WriteLog( gacLogFilename, %s | REPLACE_FUNCNAME_FETCH | LOG_LINELEN | 打开 内存映射表 REPLACE_TABLEDESC 失败 errno%d,请重启应用n, GetLocalTimeString( gacTimeStringBuffer , 256 , %Y-%m-%d %H:%M:%S ), _LINE_, errno ); return -2; /* 连接 内存映射表 地址 */ pmp = IPCAttachShareMemory( ipcid ); if( pmp = NULL ) /* 连接失败,写出错日志,函数返回 */ WriteLog( gacLogFilename, %s | REPLACE_FUNCNAME_FETCH | LOG_LINELEN | 连接 内存映射表 REPLACE_TABLEDESC 失败 errno%d,请重启应用n, GetLocalTimeString( gacTimeStringBuffer , 256 , %Y-%m-%d %H:%M:%S ), _LINE_, errno ); return -3; memset( acRecordAmount , 0x00 , sizeof( acRecordAmount ) ); strncpy( acRecordAmount , pmp , 10 ); lRecordAmount = atol( acRecordAmount ); pmpREPLACE_STRUCT_ARG = (struct REPLACE_STRUCT_TYPE *)( pmp + 10 ) ; /* 搜寻 内存映射表 */ for( l=0 ; llRecordAmount ; l+ ) /* 调用搜寻回调函数 */ iReturnValue = REPLACE_FUNCNAME_COMPARE_PROC( pvCondValue , pmpREPLACE_STRUCT_ARG ) ; if( iReturnValue != 1 ) memset( pREPLACE_STRUCT_ARG , 0x00 , sizeof( struct REPLACE_STRUCT_TYPE ) ); memcpy( pREPLACE_STRUCT_ARG , pmpREPLACE_STRUCT_ARG , sizeof(struct REPLACE_STRUCT_TYPE) ); break; pmpREPLACE_STRUCT_ARG + ; /* 断开 内存映射表 地址连接 */ IPCDetachShareMemory( pmp ); return iReturnValue; /* * 更新 公共系统参数表 内存映射表 * */ int UpdateMapGGXTCS ( void *pvCondValue , void *pvUpdateValue , int (* REPLACE_FUNCNAME_UPDATE_PROC) ( void *pvCondValue , void *pvUpdateValue , struct REPLACE_STRUCT_TYPE *pREPLACE_STRUCT_ARG ) ); int iReturnValue; struct REPLACE_STRUCT_TYPE *pmpREPLACE_STRUCT_ARG; char *pmp; char acRecordAmount11; long lRecordAmount; long l; _IPC_ID_T ipcid; /* 判断 内存映射表 是否存在 */ iReturnValue = IPCIsShareMemoryExist( REPLACE_SHEKEY ); if( iReturnValue = IPC_SHAREMEMORY_RETURN_ISNT_EXIST ) /* 若不存在 则创建之 */ iReturnValue = LoadMapGGXTCS(); if( iReturnValue != 0 ) WriteLog( gacLogFilename, %s | REPLACE_FUNCNAME_FETCH | LOG_LINELEN | 创建 内存映射表 REPLACE_TABLEDESC 失败 错误码%d errno%d,请重启应用n, GetLocalTimeString( gacTimeStringBuffer , 256 , %Y-%m-%d %H:%M:%S ), _LINE_, iReturnValue, errno ); return -1; /* 打开 内存映射表 */ ipcid = IPCOpenShareMemory( REPLACE_SHEKEY ); if( ipcid 0 ) /* 打开失败,写出错日志,函数返回 */ WriteLog( gacLogFilename, %s | REPLACE_FUNCNAME_FETCH | LOG_LINELEN | 打开 内存映射表 REPLACE_TABLEDESC 失败 errno%d,请重启应用n, GetLocalTimeString( gacTimeStringBuffer , 256 , %Y-%m-%d %H:%M:%S ), _LINE_, errno ); return -2; /* 连接 内存映射表 地址 */ pmp = IPCAttachShareMemory( ipcid ); if( pmp = NULL ) /* 连接失败,写出错日志,函数返回 */ WriteLog( gacLogFilename, %s | REPLACE_FUNCNAME_FETCH | LOG_LINELEN | 连接 内存映射表 REPLACE_TABLEDESC 失败 errno%d,请重启应用n, GetLocalTimeString( gacTimeStringBuffer , 256 , %Y-%m-%d %H:%M:%S ), _LINE_, errno ); return -3; memset( acRecordAmount , 0x00 , sizeof( acRecordAmount ) ); strncpy( acRecordAmount , pmp , 10 ); lRecordAmount = atol( acRecordAmount ); pmpREPLACE_STRUCT_ARG = (struct REPLACE_STRUCT_TYPE *)( pmp + 10 ) ; /* 搜寻 内存映射表 */ for( l=0 ; llRecordAmount ; l+ ) /* 调用搜寻回调函数 */ iReturnValue = REPLACE_FUNCNAME_UPDATE_PROC( pvCondValue , pvUpdateValue , pmpREPLACE_STRUCT_ARG ) ; if( iReturnValue != 1 ) break; pmpREPLACE_STRUCT_ARG + ; /* 断开 内存映射表 地址连接 */ IPCDetachShareMemory( pmp ); return iReturnValue; /* * 装载 公共系统参数表 内存映射表 * */ int LoadMapGGXTCS() int iReturnValue; struct REPLACE_STRUCT_TYPE REPLACE_STRUCT_ARG; struct REPLACE_STRUCT_TYPE *pmpREPLACE_STRUCT_ARG; char *pmp; _IPC_ID_T ipcid; long lMapSize; memset( &REPLACE_STRUCT_ARG , 0x00 , sizeof( struct REPLACE_STRUCT_TYPE ) ); /* 获取表记录总条数 */ EXEC SQL SELECT count(*) INTO :dlRecordAmount FROM REPLACE_TABLENAME ; if( sqlca.sqlcode ) WriteLog( gacLogFilename, %s | REPLACE_FUNCNAME_LOAD | LOG_LINELEN | 获取 REPLACE_TABLEDESC 总记录数 失败 sqlcode%dn, GetLocalTimeString( gacTimeStringBuffer , 256 , %Y-%m-%d %H:%M:%S ), _LINE_, sqlca.sqlcode ); return -1; /* 计算内存映射表大小 */ /* 前十个字节为储存头,存放储存单元的个数 */ lMapSize = dlRecordAmount * sizeof( struct REPLACE_STRUCT_TYPE ) + 10 ; /* 创建 内存映射表 */ ipcid = IPCCreateShareMemory( REPLACE_SHEKEY , lMapSize ); if( ipcid 0 ) if( errno = EEXIST ) return 0; WriteLog( gacLogFilename, %s | REPLACE_FUNCNAME_LOAD | LOG_LINELEN | 创建 内存映射表 REPLACE_TABLEDESC 失败 errno%d,请重启应用n, GetLocalTimeString( gacTimeStringBuffer , 256 , %Y-%m-%d %H:%M:%S ), _LINE_, errno ); return -2; /* 定义游标 */ EXEC SQL DECLARE REPLACE_CURSORNAME CURSOR FOR SELECT * FROM REPLACE_TABLENAME ; /* 打开游标 */ EXEC SQL OPEN REPLACE_CURSORNAME ; /* 打开游标 失败 */ if( sqlca.sqlcode ) WriteLog( gacLogFilename, %s | REPLACE_FUNCNAME_LOAD | LOG_LINELEN | 打开 游标 REPLACE_TABLEDESC 失败 sqlcode%dn, GetLocalTimeString( gacTimeStringBuffer , 256 , %Y-%m-%d %H:%M:%S ), _LINE_, sqlca.sqlcode ); return -3; /* 连接 内存映射表 地址 */ pmp = IPCAttachShareMemory( ipcid ); if( pmp = NULL ) /* 连接失败,写出错日志,函数返回 */ WriteLog( gacLogFilename, %s | REPLACE_FUNCNAME_LOAD | LOG_LINELEN | 连接 内存映射表 REPLACE_TABLEDESC 失败 errno%d,请重启应用n, GetLocalTimeString( gacTimeStringBuffer , 256 , %Y-%m-%d %H:%M:%S ), _LINE_, errno ); return -4; sprintf( pmp , %-010d , dlRecordAmount ); pmpREPLACE_STRUCT_ARG = (struct REPLACE_STRUCT_TYPE *)( pmp + 10 ) ; while(1) /* 获取游标 */ EXEC SQL FETCH REPLACE_CURSORNAME INTO REPLACE_DBVAR; /* 如果记录已取完,跳出循环 */ if( sqlca.sqlcode = 100 ) break; /* 获取游标 失败 */ if( sqlca.sqlcode != 0 ) WriteLog( gacLogFilename, %s | REPLACE_FUNCNAME_LOAD | LOG_LINELEN | 获取游标 REPLACE_TABLEDESC 失败 sqlcode%d,请重启应用n, GetLocalTimeString( gacTimeStringBuffer , 256 , %Y-%m-%d %H:%M:%S ), _LINE_, sqlca.sqlcode ); /* 断开 内存映射表 地址连接 */ IPCDetachShareMemory( pmp ); /* 关闭游标 */ EXEC SQL CLOSE REPLACE_CURSORNAME; return -5; REPLACE_DBVARFUNC( &REPLACE_STRUCT_ARG ); memcpy( pmpREPLACE_STRUCT_ARG , &REPLACE_STRUCT_ARG , sizeof( struct REPLACE_STRUCT_TYPE ) ); pmpREPLACE_STRUCT_ARG + ; /* 断开 内存映射表 地址连接 */ IPCDetachShareMemory( pmp ); /* 关闭游标 */ EXEC SQL CLOSE REPLACE_CURSORNAME; WriteLog( gacLogFilename, %s | REPLACE_FUNCNAME_LOAD | LOG_LINELEN | %ld条记录进入 内存映射表 REPLACE_TABLEDESC n, GetLocalTimeString( gacTimeStringBuffer , 256 , %Y-%m-%d %H:%M:%S ), _LINE_, dlRecordAmount ); return 0; #undef REPLACE_STRUCT_TYPE #undef REPLACE_STRUCT_ARG #undef REPLACE_SHEKEY #undef REPLACE_TABLENAME #undef REPLACE_TABLEDESC #undef REPLACE_CURSORNAME #undef REPLACE_DBVAR #undef REPLACE_DBVARFUNC #undef REPLACE_FUNCNAME_FETCH #undef REPLACE_FUNCNAME_LOAD #undef REPLACE_FUNCNAME_COMPARE_PROC #undef REPLACE_FUNCNAME_UPDATE_PROC 附件二、下面这些函数被上面的程序调用过,代码存放在本人设计的iIPC、iLibX基础函数库中。 /* * 函数名 : IPCIsShareMemoryExist * 函数描述 : 判断共享存储块存在 * 输入参数说明 : _IPC_ID_T ipckey 共享存储块的id * 返回值 : 存在 返回 IPC_SHAREMEMORY_RETURN_EXIST * 不存在 返回 IPC_SHAREMEMORY_RETURN_ISNT_EXIST */ int IPCIsShareMemoryExist( _IPC_KEY_T ipckey ) _IPC_ID_T ipcid; ipcid=shmget( ipckey , 1 , IPC_CREAT | IPC_EXCL | _giIPCPermission ); if( ipcid = -1 ) return IPC_SHAREMEMORY_RETURN_EXIST; IPCDestroyShareMemory( ipcid ); return IPC_SHAREMEMORY_RETURN_ISNT_EXIST; /* * 函数名 : IPCOpenShareMemory * 函数描述 : 打开共享存储块 * 输入参数说明 : _IPC_KEY_T ipckey 共享存储块的key * 返回值 : 成功,返回 共享存储块的id * 失败,返回 错误代码 */ _IPC_ID_T IPCOpenShareMemory( _IPC_KEY_T ipckey ) int iReturnValue; _IPC_ID_T ipcid; iReturnValue = IPCIsShareMemoryExist( ipckey ) ; if( iReturnValue = IPC_SHAREMEMORY_RETURN_ISNT_EXIST ) return IPC_SHAREMEMORY_RETURN_ISNT_EXIST; ipcid = shmget( ipckey , 0 , IPC_CREAT | _giIPCPermission ); if( ipcid = -1 ) return IPC_SHAREMEMORY_ERROR_CANT_OPEN; return ipcid; /* * 函数名 : IPCAttachShareMemory * 函数描述 : 连接共享存储块首地址 * 输入参数说明 : _IPC_ID_T ipcid 共享存储块的id * 返回值 : 存在 返回 首地址 * 不存在 返回 NULL */ void *IPCAttachShareMemory( _IPC_ID_T ipcid ) void *pvAttach; pvAttach = shmat( ipcid , NULL , 0 ); if( pvAttach = (void *)-1 ) return NULL; else return pvAttach; /* * 函数名 : IPCDetachShareMemory * 函数描述 : 断开共享存储块首地址 * 输入参数说明 : void *pvDetach 共享存储块连接首地址 * 返回值 : 存在 返回 IPC_SHAREMEMORY_RETURN_SUCCESS * 不存在 返回 IPC_SHAREMEMORY_ERROR_CANT_DETACH */ int IPCDetachShareMemory( void *pvDetach ) int i; i = shmdt( pvDetach ); if( i = -1 ) return IPC_SHAREMEMORY_ERROR_CANT_DETACH; else return IPC_SHAREMEMORY_RETURN_SUCCESS; /* * 函数名 : WriteLog * 函数描述 : 正常写日志函数 * 输入参数说明 : char *pcLogFileName 日志文件名 * char *pcFormatString 日志格式串 * . 日志参数列表 * 返回值 : 成功,返回 TRUE * 失败,返回 FALSE * 更新日志 : 2003/10/18 创建 */ BOOL WriteLog( char *pcLogFileName, char *pcFormatString, . ) va_list valist; BOOL ret; va_start( valist, pcFormatString ); ret=DoLog(pcLogFileName, LOG_WRITE_APPEND, LOG_MODE_RETURN, pcFormatString, valist ); va_end( valist ); return ret; /* * 函数名 : DoLog * 函数描述 : 日志记录原
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2025年教师招聘之《幼儿教师招聘》测试卷附有答案详解带答案详解(研优卷)
- 视网膜脱离手术护理查房
- 2025年教师招聘之《小学教师招聘》试卷含答案详解(满分必刷)
- 为教育机构提供营销方案
- 课件参赛作品申报
- 线上活动策划方案案例小学
- 课件加密教学课件
- 课件倒计时小组件
- 工业大数据平台在工业大数据人才培养中的应用可行性研究报告
- 国网冀北公司招聘考试真题2024
- 营造清朗空间+课件-2025-2026学年(统编版2024)道德与法治八年级上册
- saas货运管理办法
- excel操作考试题及答案
- 机动车维修行业危险废物管理制度范文六篇
- 喷雾干燥课件
- 《网页设计与制作Dreamweaver-cs6》教学课件(全)
- DBJ51T 196-2022 四川省智慧工地建设技术标准
- 审核检查表(ISO13485、GMP、体考指南、QSR820)
- 宿舍教室报修维修登记表
- 剪映入门教程PPT
- 律师事务所合同纠纷法律诉讼服务方案
评论
0/150
提交评论