malloc申请内存失败之后再进行free,free的内存空间一定被OS回收了吗

十问 Linux 虚拟内存管理 (glibc) (二) 精
核心提示:接上篇: 十问 Linux 虚拟内存管理 (glibc) (一) 五.free 的内存真的释放了吗(还给 OS ) ?前面所有例子都有一个很严重的问题,就是分配的内存都没有释放,即导致内存泄露。原则上所有 malloc/new 分配的内存,都需 free/delete 来释放。但是, free 了的内
五.free 的内存真的释放了吗(还给 OS ) ?
前面所有例子都有一个很严重的问题,就是分配的内存都没有释放,即导致内存泄露。原则上所有 malloc/new 分配的内存,都需 free/delete 来释放。但是, free 了的内存真的释放了吗?
要说清楚这个问题,可通过下面例子来说明。
初始状态:如图 (1) 所示,系统已分配 ABCD 四块内存,其中 ABD 在堆内分配, C 使用 mmap 分配。为简单起见,图中忽略了如共享库等文件映射区域的地址空间。
E=malloc(100k) :分配 100k 内存,小于 128k ,从堆内分配,堆内剩余空间不足,扩展堆顶 (brk) 指针。
free(A) :释放 A 的内存,在 glibc 中,仅仅是标记为可用,形成一个内存空洞 ( 碎片 ) ,并没有真正释放。如果此时需要分配 40k 以内的空间,可重用此空间,剩余空间形成新的小碎片。
free(C) : C 空间大于 128K ,使用 mmap 分配,如果释放 C ,会调用 munmap 系统调用来释放,并会真正释放该空间,还给 OS ,如图 (4) 所示。
free(D) :与释放 A 类似,释放 D 同样会导致一个空洞,获得空闲空间,但并不会还给 OS 。此时,空闲总空间为 100K ,但由于虚拟地址不连续,无法合并,空闲空间无法满足大于 60k 的分配请求。
free(E) :释放 E ,由于与 D 连续,两者将进行合并,得到 160k 连续空闲空间。同时 E 是最靠近堆顶的空间, glibc 的 free 实现中,只要堆顶附近释放总空间(包括合并的空间)超过 128k ,即会调用 sbrk(-SIZE) 来回溯堆顶指针,将原堆顶空间还给 OS ,如图 (6) 所示。而堆内的空闲空间还是不会归还 OS 的。
由此可见:
malloc 使用 mmap 分配的内存 ( 大于 128k) , free 会调用 munmap 系统调用马上还给 OS ,实现真正释放。
堆内的内存,只有释放堆顶的空间,同时堆顶总连续空闲空间大于 128k 才使用 sbrk(-SIZE) 回收内存,真正归还 OS 。
堆内的空闲空间,是不会归还给 OS 的。
六. 程序代码中 malloc 的内存都有相应的 free ,就不会出现内存泄露了吗?
狭义上的内存泄露是指 malloc 的内存,没有 free ,导致内存浪费,直到程序结束。而广义上的内存泄露就是进程使用内存量不断增加,或大大超出系统原设计的上限。
上一节说到, free 了的内存并不会马上归还 OS ,并且堆内的空洞(碎片)更是很难真正释放,除非空洞成为了新的堆顶。所以,如上一例子情况 (5) ,释放了 40k 和 60k 两片内存,但如果此时需要申请大于 60k (如 70k ),没有可用碎片,必须向 OS 申请,实际使用内存仍然增大。
因此,随着系统频繁地 malloc 和 free ,尤其对于小块内存,堆内将产生越来越多不可用的碎片,导致“内存泄露”。而这种“泄露”现象使用 valgrind 是无法检测出来的。
下图是 MySQL 存在大量分区表时的内存使用情况 (RSS 和 VSZ) ,疑似“内存泄露”。
因此,当我们写程序时,不能完全依赖 glibc 的 malloc 和 free 的实现。更好方式是建立属于进程的内存池,即一次分配 (malloc) 大块内存,小内存从内存池中获得,当进程结束或该块内存不可用时,一次释放 (free) ,可大大减少碎片的产生。
七. 既然堆内内存不能直接释放,为什么不全部使用 mmap 来分配?
由于堆内碎片不能直接释放,而问题 5 中说到 mmap 分配的内存可以会通过 munmap 进行 free ,实现真正释放。既然堆内碎片不能直接释放,导致疑似“内存泄露”问题,为什么 malloc 不全部使用 mmap 来实现呢?而仅仅对于大于 128k 的大块内存才使用 mmap ?
其实,进程向 OS 申请和释放地址空间的接口 sbrk/mmap/munmap 都是系统调用,频繁调用系统调用都比较消耗系统资源的。并且, mmap 申请的内存被 munmap 后,重新申请会产生更多的缺页中断。例如使用 mmap 分配 1M 空间,第一次调用产生了大量缺页中断 (1M/4K 次 ) ,当 munmap 后再次分配 1M 空间,会再次产生大量缺页中断。缺页中断是内核行为,会导致内核态 CPU 消耗较大。另外,如果使用 mmap 分配小内存,会导致地址空间的分片更多,内核的管理负担更大。
而堆是一个连续空间,并且堆内碎片由于没有归还 OS ,如果可重用碎片,再次访问该内存很可能不需产生任何系统调用和缺页中断,这将大大降低 CPU 的消耗。
因此, glibc 的 malloc 实现中,充分考虑了 sbrk 和 mmap 行为上的差异及优缺点,默认分配大块内存 (128k) 才使用 mmap 获得地址空间,也可通过 mallopt(M_MMAP_THRESHOLD, &SIZE&) 来修改这个临界值。
八. 如何查看进程的缺页中断信息?
可通过以下命令查看缺页中断信息
ps -o majflt,minflt -C &program_name&
ps -o majflt,minflt -p &pid&
其中, majflt 代表 major fault ,指大错误, minflt 代表 minor fault ,指小错误。这两个数值表示一个进程自启动以来所发生的缺页中断的次数。其中 majflt 与 minflt 的不同是, majflt 表示需要读写磁盘,可能是内存对应页面在磁盘中需要 load 到物理内存中,也可能是此时物理内存不足,需要淘汰部分物理页面至磁盘中。
例如,下面是 mysqld 的一个例子。
mysql@ TLOG_590_591:~& ps -o majflt,minflt -C mysqld
MAJFLT MINFLT
如果进程的内核态 CPU 使用过多,其中一个原因就可能是单位时间的缺页中断次数多个,可通过以上命令来查看。
如果 MAJFLT 过大,很可能是内存不足。
如果 MINFLT 过大,很可能是频繁分配 / 释放大块内存 (128k) , malloc 使用 mmap 来分配。对于这种情况,可通过 mallopt(M_MMAP_THRESHOLD, &SIZE&) 增大临界值,或程序实现内存池。
九. 如何查看堆内内存的碎片情况?
glibc 提供了以下结构和接口来查看堆内内存和 mmap 的使用情况。
struct mallinfo {
/* non-mmapped space allocated from system */
/* number of free chunks */
/* number of fastbin blocks */
/* number of mmapped regions */
/* space in mmapped regions */
/* maximum total allocated space */
/* space available in freed fastbin blocks */
/* total allocated space */
/* total free space */
/* top-most, releasable (via malloc_trim) space */
/* 返回 heap(main_arena) 的内存使用情况,以 mallinfo 结构返回 */
struct mallinfo mallinfo();
/* 将 heap 和 mmap 的使用情况输出到 stderr
void malloc_stats();
可通过以下例子来验证 mallinfo 和 malloc_stats 输出结果。
#include &stdlib.h&
#include &stdio.h&
#include &string.h&
#include &unistd.h&
#include &sys/mman.h&
#include &malloc.h&
heap_malloc_total, heap_free_total,
mmap_total, mmap_
void print_info()
struct mallinfo mi = mallinfo();
printf(&count by itself:\n&);
printf(&\theap_malloc_total=%lu heap_free_total=%lu heap_in_use=%lu\n\
\tmmap_total=%lu mmap_count=%lu\n&,
heap_malloc_total*1024, heap_free_total*1024, heap_malloc_total*1024 - heap_free_total*1024,
mmap_total*1024, mmap_count);
printf(&count by mallinfo:\n&);
printf(&\theap_malloc_total=%lu heap_free_total=%lu heap_in_use=%lu\n\
\tmmap_total=%lu mmap_count=%lu\n&,
mi.arena, mi.fordblks, mi.uordblks,
mi.hblkhd, mi.hblks);
printf(&from malloc_stats:\n&);
malloc_stats();
#define ARRAY_SIZE 200
int main(int argc, char** argv)
char** ptr_arr[ARRAY_SIZE];
for( i = 0; i & ARRAY_SIZE; i++) {
ptr_arr[i] = malloc(i * 1024);
if ( i & 128)
heap_malloc_total +=
mmap_total +=
mmap_count++;
print_info();
for( i = 0; i & ARRAY_SIZE; i++) {
if ( i % 2 == 0)
free(ptr_arr[i]);
if ( i & 128)
heap_free_total +=
mmap_total -=
mmap_count--;
printf(&\nafter free\n&);
print_info();
该例子第一个循环为指针数组每个成员分配索引位置 (KB) 大小的内存块,并通过 128 为分界分别对 heap 和 mmap 内存分配情况进行计数;第二个循环是 free 索引下标为奇数的项,同时更新计数情况。通过程序的计数与 mallinfo/malloc_stats 接口得到结果进行对比,并通过 print_info 打印到终端。
下面是一个执行结果:
count by itself:
heap_malloc_total=8323072 heap_free_total=0 heap_in_use=8323072
mmap_total= mmap_count=72
count by mallinfo:
heap_malloc_total=8327168 heap_free_total=2032 heap_in_use=8325136
mmap_total= mmap_count=72
from malloc_stats:
system bytes
in use bytes
Total (incl. mmap):
system bytes
in use bytes
max mmap regions =
max mmap bytes
after free
count by itself:
heap_malloc_total=8323072 heap_free_total=4194304 heap_in_use=4128768
mmap_total=6008832 mmap_count=36
count by mallinfo:
heap_malloc_total=8327168 heap_free_total=4197360 heap_in_use=4129808
mmap_total=6119424 mmap_count=36
from malloc_stats:
system bytes
in use bytes
Total (incl. mmap):
system bytes
in use bytes
max mmap regions =
max mmap bytes
由上可知,程序统计和 mallinfo 得到的信息基本吻合,其中 heap_free_total 表示堆内已释放的内存碎片总和。
如果想知道堆内片究竟有多碎 ,可通过 mallinfo 结构中的 fsmblks 、 smblks 、 ordblks 值得到,这些值表示不同大小区间的碎片总个数,这些区间分别是 0~80 字节, 80~512 字节, 512~128k 。如果 fsmblks 、 smblks 的值过大,那碎片问题可能比较严重了。
不过, mallinfo 结构有一个很致命的问题,就是其成员定义全部都是 int ,在 64 位环境中,其结构中的 uordblks/fordblks/arena/usmblks 很容易就会导致溢出,应该是历史遗留问题,使用时要注意!
十. 除了 glibc 的 malloc/free ,还有其他第三方实现吗?
其实,很多人开始诟病 glibc 内存管理的实现,就是在高并发性能低下和内存碎片化问题都比较严重,因此,陆续出现一些第三方工具来替换 glibc 的实现,最著名的当属 google 的 tcmalloc 和 facebook 的 jemalloc 。
网上有很多资源,可搜索之,这里就不详述了。
基于以上认识,最后发现 MySQL 的疑似“内存泄露”问题一方面是 MySQL 5.5 分区表使用更多的内存,另一方面跟内存碎片有关,这也是 TMySQL 一个优化方向。
然而,以上主要介绍了 glibc 虚拟内存管理主要内容,事实上,在并发情况下, glibc 的虚存管理会更加复杂,碎片情况也可能更严重,这将在另一篇再做介绍。
参考文章:
《深入理解计算机系统》第 10 章
小编为您推荐“十问 Linux 虚拟内存管理 (glibc) (二) 精”相关文章
自php5.3.3开始,php源码中包含了php-fpm,不需要单独通过补丁的方式安装php-fpm,在源码安装的时候直接 configure 中增加参数 –enable-fpm即可,使用方法如下
初学Linux,首先需要弄清Linux目录结构,每一个目录的具体作用、存放哪些文件等。下面我们就来分析一下Linux的标准目录结构
1、显示/boot/grub/grub.conf中以至少一个空白字符开头的行;[root@localhost ~]# grep-E ^[[:space:]]+ /boot/grub/grub.conf root (hd0,0)kernel /vmlinuz-2.6.32-431.el6.x86_64 ro root=/dev/mapper/VolGroup-lv_root rd_NO_LUKS.UTF-8 rd_NO_MD rd_LVM_L
在本指南中,我们将介绍如何在 Linux 系统上显示当前工作目录或任何目录及其子目录中的文件数量。我们将使用 find 命令,它用于搜索目录层次结构中的文件,以及 wc 命令,它会打印每个文件或来自标准输入的换行符、单词和字节计数。以下是我们在 find 命令中
什么是 RAM?在智能手机世界,我们每一个人都知道 RAM。因此,我不想深入介绍,这样我就简要概括下。RAM 代表“随机访问内存(Random Access Memory)”,是一种计算机数据存储,它会存储经常使用的程序来提升系统性能。什么是虚拟内存?虚拟内存是一种内存管理方
首先安装好CentOS 7,这个不多说。RPM包的种种当前的GA版本是5.7.17-1,RPM包地址: https://dev.mysql.com/get/Downloads/MySQL-5.7/mysql-5.7.17-1.el7.x86_64.rpm-bundle.tar ,略大,557.1M。下载完后可以验证下MD5,#md5sum mysql-standard-5.7.18-linux
Linux服务器LVM逻辑卷配置过程通过使用Linux的逻辑卷管理器(Logical Volume Manager, LVM),用户可以在系统运行时动态调整文件系统的大小,把数据从一块硬盘重定位到另一块硬盘,也可以提高I/O操作的性能,以及提供冗余保护,它的快照功能允许用户对逻辑卷进
通过 keepalived 实现 lvs 服务器的的双机热备和真实服务器之间的负载均衡的blog 挺多的,在搭建的时候也参考了相关博文,不同人搭建的实验环境不一样,本文是基于 VM 搭建的虚拟集群环境,全部虚拟机网卡模式为 NAT 方式相连,处于同一网段。搭建环境: 使用
项目需求测试MySQL的并发性能,故而需要启动多个实例,即在一台机器上启动多个MySQL服务。只是一个简单需求,也折腾了好几天,本文做一个记录,也希望能帮助更多有此需求的人,节约时间和精力。本文主要从以下几个方面展开:方法简介实验环境MySQL安装MySQL启
在最近的文章中,我们向你展示了 在 Linux 中设置 sudo 的十条 sudoers 实用配置]1以及让 sudo 在你输入错误的密码时“嘲讽”你,在本文中,我们发现了另一个 sudo 贴士,在 Ubuntu Linux 中使 sudo 密码会话(超时)更长或更短。在 Ubuntu 及其衍生版如 Linux
前几天发现由于MySQL的数据库太大,默认安装的/var盘已经再也无法容纳新增加的数据,只能想办法转移数据的目录。网上有很多相关的文章写到转移数据库目录的文章,但转载的过程中还会有一些错误,因为大部分人根本就没测试过,这篇文章是本文测试过整理好后分
Linux查找–find命令首先,大家如果看到有什么不懂的地方,欢迎吐槽!!!find命令的格式findpathname-options[ -print -exec -ok ...]pathname #是find命令所查找的目录路径,例如.表示当前目录,/表示根目录-print #表示find命令将匹配的文件输出到标准输出他的最新文章
他的热门文章
您举报文章:
举报原因:
原文地址:
原因补充:
(最多只允许输入30个字)将应用程序从 OS/2 迁移到 Linux 上: 第 2 部分,内存管理、IPC 和文件处...
我的图书馆
将应用程序从 OS/2 迁移到 Linux 上: 第 2 部分,内存管理、IPC 和文件处...
OS/2 为开发人员提供了一个 32 位的线性地址空间,操作系统以页为单位对内存进行分配、保护和操作。
OS/2 应用程序以内存对象的形式对内存进行分配和操作,一个内存对象可以包含一页或多页。最小内存分配(allocation)是一页。但是应用程序可以以内存块的形式来子分配(
suballocate)内存,内存块是内存对象的一部分,其大小可以从 1 个字节到内存对象的大小。当应用程序请求 OS/2 分配内存时,操作系统就为其保留一个线性地址范围。直到这段内存被提交时,才会将物理内存映射到这个范围的线性地址空间上。所谓提交(commitment)就是将物理内存分配到一个线性地址范围上。任何试图对未提交的内存进行读写的操作都会导致访问异常。
Linux 也有一个 32 位的线性地址空间;但是与 OS/2 不同的是,Linux 中的每个进程都可以看到一个不同的线性地址空间集。任何进程申请分配动态内存都会导致将一个新的线性地址空间范围加入该进程的地址空间。这个地址范围称为
内存区域(memory region)。而物理内存的分配则被延迟到实际引用时才会真正进行。
DosAllocMem
DosAllocMem
DosSubSetMem
DosSubAllocMem
DosSubFreeMem
DosSubUnsetMem
按页对齐,具有内存保护功能
posix_memalign
按页对齐,没有内存保护功能
posix_memalign
不按页对齐,没有内存保护功能
malloc/new
特定于上下文的DosFreeMemfree
delete(在用 new 分配的情况下)
可映射的DosAllocSegmalloc/new可映射的DosFreeSegfree/delete可映射的
类别一列表示该 OS/2 结构是可映射的,还是特定于上下文的:
可映射的:
这个 OS/2 结构可以被映射为特定的 Linux 结构,二者具有相近的类型、参数、返回值和结构形式,而且功能相似。
特定于上下文的:
给定的 OS/2 结构在 Linux 中可能有等价的结构,也可能没有,或者 Linux 有个结构提供类似的功能。无论是哪种情况,决定是否使用特定的 Linux 结构要取决于应用程序的上下文。
在 OS/2 中,推荐的内存管理方式是在程序执行的早期就分配一大段内存,然后在需要使用内存时再对这些内存进行提交或子分配。内存对象被分配之后,应用程序可以使用以下两种方法中的一种来管理内存对象:
根据需要对内存进行提交或回收内存(参阅
将这个内存对象作为一个堆(heap),并从这个堆中再分配内存(参阅
对内存进行提交或回收为应用程序提供了更多控制进程的手段,但是应用程序必须要跟踪哪些页被提交了,哪些没有。
当从一个堆中分配内存时,OS/2 负责跟踪对物理内存页的提交和回收情况,这就是说应用程序无需担心这个问题。
在 Linux 中,应用程序不需要担心提交/回收和子分配的问题。申请分配任意大小的内存都可以使用相同的内存分配的系统调用(
malloc/posix_memalign )。内存分配器将总是从空闲列表中返回一个最合适大小的内存块。操作系统负责内存的提交和分配。
Linux 采用一种乐观的内存分配策略。这意味着即使
malloc() 返回一个非空值,也不能确保现在真的有内存可用。当系统出现内存不足的情况时,OOM(“out of memory”)程序就会杀掉一个或多个进程。
在 OS/2 中,使用系统调用
DosAllocMem() 来保留一段范围内的内存页。内存可以在分配时进行提交,也可以以后调用
DosSetMem() 进行提交:
APIRET DosAllocMem(PPVOID ppbBase, ULONG size, ULONG attr);
APIRET DosSetMem(PVOID pbBase, ULONG size, ULONG flag);
ppbBase 指向所分配的内存位置的指针。
size 说明所请求分配的字节数。
attr 是一组标志,描述内存分配属性,以及希望的访问保护。
flag 是一组标志,指定内存提交或回收,以及希望的访问保护。
对已提交页的访问由一个访问保护属性进行控制;保护属性有读保护(
PAG_READ )、写保护(
PAG_WRITE )、执行保护(
PAG_EXECUTE )以及守护页保护(
PAG_GUARD )。注意由
DosAllocMem 分配的内存总是按页对齐的,不能重新设置大小。
如果一个应用程序希望使用子分配机制,必须首先使用
DosAllocMem() 分配一段内存,然后调用
DosSubSetMem() 为子分配建立要分配的内存堆。然后应用程序就可以使用
DosSubAllocMem() 从堆中分配一段内存,或者使用
DosSubFreeMem() 函数来释放内存。 最后使用
DosSubUnSetMem() 来结束子分配的设置:
APIRET DosSubSetMem(PVOID pbBase, ULONG flag, ULONG size);
APIRET DosSubAllocMem(PVOID pbBase, PPVOID ppMemblock, ULONG size);
APIRET DosSubFreeMem(PVOID pbBase, PVOID pOffset, ULONG size);
APIRET DosSubUnsetMem(PVOID pbBase);
pbBase 指向由
DosAllocMem() 分配的内存。
size 是字节数。
ppMemblock 是指向要由
DosSubAllocMem() 分配的内存块的指针。
pOffset 是指向要由
DosSubFreeMem() 释放的内存块的指针。
在移植程序时,在 Linux 中内存分配可以分为两类:按页对齐的和不按页对齐的。
有或没有内存保护的按页对齐内存分配
在 Linux 中,可以使用系统调用
posix_memalign() 来分配按页对齐的内存。默认情况下,内存区有
PROT_READ | PROT_WRITE 标志。这些内存保护属性可以调用
mprotect() 进行修改。库函数
free() 可以用来释放由
posix_memalign() 分配的内存空间:
int posix_memalign(void **memptr, size_t alignment, size_t size);
int mprotect(const void *addr, size_t size, int prot);
*memptr 包含了所分配的内存的地址。所分配的内存地址是对齐过的,必须是 2 的幂或
sizeof(void *) 的整数倍。
addr 是指向所分配的内存的指针,这段内存的保护标志必须使用
mprotect() 进行修改。
prot 是保护标志,值可以为:
PROT_NONE :该段内存根本不能访问。
PROT_READ :该段内存可读。
PROT_WRITE :该段内存可写。
PROT_EXEC :该段内存可以包含执行代码。
没有内存保护的不按页对齐内存分配
默认情况下,OS/2 分配的内存都是按页对齐的。如果不需要按页对齐的内存,而且默认的访问保护标志
PROT_READ | PROT_WRITE 就已足够了,那么在 Linux 中就可以使用
malloc() 来分配内存,使用
free() 来释放已经分配的内存:
void *malloc(size_t size)
另外,对于使用 C++ 编写的程序来说,可以使用关键字
new 来分配内存,使用关键字
delete 来释放内存空间。请确保在使用
new 时要使用
nothrow 选项,这样当分配内存出错时,就不会触发异常,而是返回一个空指针。
DosFreeMem() 从主进程的虚拟地址空间中释放之前分配的内存对象:
APIRET DosFreeMem(PVOID pbBase);
Linux 使用
free() 释放之前分配的内存:
void free(void *memptr)
DosAllocSeg() 也可以用来分配多达 64K 的内存块;
DosMemAvail() 用来查找还可以分配的可用内存数量,
DosFreeSeg() 用来释放内存段。参数 size 说明要分配的字节数(最大为 64KB);selector 是一个指向整数的指针,它是要获取的选择器;flags 必须为 0。
USHORT BLXAPI DosAllocSeg (USHORT size, PSEL selector, USHORT flags)
USHORT BLXAPI DosFreeSeg(SEL selector)
Linux 中没有分段的内存分配模型。
OS/2 example of memory allocation/de-allocation by committing
// and decommitting memory
/* Allocate 16KB object */
ulrc = DosAllocMem((PVOID *) &pb, 2097152, PAG_READ | PAG_WRITE);
/* Commit 4KB
ulrc = DosSetMem(pb, 4096, PAG_COMMIT);
strcpy(pb, "The memory object has just been used.");
printf("%s\n",pb);
/* De-Commit 4KB
ulrc = DosSetMem(pb, 4096, PAG_DECOMMIT);
//Free the memory area
ulrc = DosFreeMem(pb);
OS/2 example of memory suballocation. It sets up 8192 bytes
// for suballocation and then allocates two small blocks of memory
pbBase, pb1, pb2;
/* Allocate 8K object
ulrc = DosAllocMem((PVOID *) &pbBase, 8192, fALLOC);
/* Set up object or suballocation
ulrc = DosSubSetMem(pbBase, DOSSUB_INIT, 8192);
/* Suballocate 100 bytes */
ulrc = DosSubAllocMem(pbBase,(PVOID *) &pb1,100);
/* Suballocate 500 bytes */
ulrc = DosSubAllocMem(pbBase,(PVOID *) &pb2,500);
/* Free 1st suballocation*/
ulrc = DosSubFreeMem(pbBase, pb1, 100);
/* Suballocate 50 bytes
ulrc = DosSubAllocMem(pbBase, (PVOID *) &pb1, 50);
/* Free all suballocation*/
ulrc = DosSubFreeMem(pbBase, pb1, 50);
ulrc = DosSubFreeMem(pbBase, pb1, 500);
/* End the suballocation
ulrc = DosSubUnsetMem(pbBase)
//Free the memory area
ulrc = DosFreeMem(pbBase);
/* Allocate a 4KB block */
if (DosAllocSeg(4096, &selector, 0) != 0) {
printf("DosAllocSeg()
failed\n");
// print the allocated memory address
printf("4 Kb block allocated at :
%04X\n",selector);
// Free the memory segment
if (DosFreeSeg(selector) != 0) {
printf("DosFreeSeg() failed - selector = %04X\n",
selector);
// Page-aligned memory allocation with memory write protection
int main (void)
/* Pointer to memory
int ret = 0;
/* Return code
//allocate page-aligned memory area. By default both read and write
//are allowed. If that's the requirement then no need to use
ret = posix_memalign(ppcBuf,
//write protect the memory
ret = mprotect(*ppcBuf, 4096, PROT_READ);
//Free the memory area
free(*ppcBuf);
// Non page-aligned memory allocation with default memory protection
// both read and write allowed
#define USE_MALLOC 1
char *pcBaseAddress = NULL;
if (USE_MALLOC == 1) {
// Allocate memory using malloc
pcBaseAddress =
(char *) malloc(4096L);
// Allocate memory using new
pcBaseAddress =
new (nothrow) char[4096];
if (pcBaseAddress == NULL) {
if (USE_MALLOC == 1) {
// free the memory space
free(pcBaseAddress);
// free memory space using delete
delete [] pcBaseA
有名共享内存
DosAllocSharedMem
DosGetNamedSharedMem
可映射的DosFreeMemshmdt
共享内存(Shared memory)是两个或多个应用程序可以读写的内存。共享内存在使用之前要进行一些准备工作,以便所有的应用程序都可以获得一个指针指向这段内存,从而可以访问数据。应用程序必须显式地请求反问共享内存;所有未经授权的应用程序不能访问共享内存。
在 OS/2 中,有两种共享内存:有名共享内存和无名共享内存。
有名共享内存(named shared memory,也称为命名共享内存)中,任何知道共享内存名的应用程序都可以访问这段共享内存。
无名共享内存(unnamed shared memory)中,创建共享内存的进程必须通过某种 IPC 机制向要访问共享内存的进程传递一个指向共享内存的指针。
在 Linux 中,共享内存通常都是有名共享内存(使用一个关键字惟一标识);Linux 不支持无名共享内存。
在 OS/2 中,可以调用
DosAllocSharedMem() 在虚拟地址空间中分配一个共享内存对象。分配共享内存对象会创建一个描述一段要共享的内存区域的对象。共享内存对象的虚拟地址空间在每个进程的虚拟地址空间中都是一样的,这样任何进程可以在自己原来的地址空间中使用相同的地址访问这个共享对象:
APIRET DosAllocSharedMem(PPVOID ppb, PSZ pszName, ULONG cb, ULONG
ppb 是一个变量的指针,该变量是要获取的已分配的页范围的基地址。
pszName 是共享内存名;例如,\SHAREMEM\PUBLIC.DAT。
cb 是要分配的共享内存对象的字节数。
flag 是分配属性以及希望的访问保护标志。
其他进程可以使用
DosGetNamedSharedMem() 来访问这个共享内存对象。要指定共享内存对象的名字,共享内存名字符串中必须包括前缀“
\SHAREMEM\ ”。要在进程的虚拟地址空间中获得一个有名共享内存对象所分配的虚拟地址,请使用:
APIRET DosGetNamedSharedMem(PPVOID ppb, PSZ pszName, ULONG flag);
ppb 是一个指向变量的指针,该变量是要获取的共享内存对象的基地址。
pszName 是与该共享内存对象有关的名字字符串的地址。
flag 是属性标志,它说明了希望该共享内存对象的访问保护模式。
DosFreeMem() 用来释放共享内存对象。
在 Linux 中,在创建或访问共享内存时必须同时使用系统调用
shmget() 和
shmat() 。
shmget() 返回与参数 key 值相关的共享内存段的标识符。这个 key 值可以用来模拟
DosAllocSharedMem 中
pszName 参数的行为。如果
IPC_CREAT ,就创建一个新的共享内存段,其大小等于
PAGE_SIZE 的整数倍(向下一个整页对齐)。如果不使用这个标志,那么
shmget() 就会查找与该 key 值相关的现有共享内存段:
int shmget (key_t key , int size , int shmflg );
shmat() 将由 shmid 指定的共享内存段附加到调用进程的地址空间上。
shmid 是调用
shmget() 时返回的结果,附加的地址是由
shmaddr 指定的;如果将其设置为 0, 操作系统就会自行选择一个地址。
shmflg 是访问保护标志;如果是只读访问,就将其设置为
SHM_RDONLY ,否则设置为 0:
void *shmat ( int shmid, void *shmaddr, int shmflg);
要释放共享内存,首先需要调用
shmdt() 将由 shmaddr 所指定的地址处的共享内存段与调用进程的数据段断开:
int shmdt (void *shmaddr);
要彻底释放共享内存,需要调用
shmctl() ,并指定
IPC_RMID 命令。这个命令只有在两种情况下才能执行:执行该命令的进程的有效用户 ID 等于与 shmid 相关的数据结构中的
shm_perm.cuid 或
shm_perm.uid 的值,或者该进程具有超级用户权限。要释放内存,可以将
buf 设置为 NULL:
int shmctl(int shmid, int cmd,struct shmid_ds *buf);
/* This example allocates named shared memory as Read/Write, and commits it
during allocation. It also writes to it, and shows how other processes can
access it. */
int main (VOID)
pvShrObject = NULL;
/* Pointer to shared memory
pszMemName
= "\\SHAREMEM\\MYTOOL\\APPLICAT.DAT";
pvAltObject = NULL;
/* Alternate pointer to shared
= NO_ERROR;
/* Return code */
/* Size (system rounds to 4096
- page body */
rc = DosAllocSharedMem(&pvShrObject,
/* Pointer to object
pszMemName,
/* Name for shared
ulObjSize,
/* Desired size of
PAG_COMMIT |
PAG_WRITE );
/* Allocate memory as
read/write */
if (rc != NO_ERROR) {
printf("DosAllocSharedMem error:
return code = %u\n",rc);
strcpy(pvShrObject, "Write your shared application data here.");
rc = DosGetNamedSharedMem(&pvAltObject,
/* Pointer to pointer
of object */
pszMemName,
/* Name of shared
PAG_READ);
/* Want read-only
if (rc != NO_ERROR) {
printf("DosGetNamedSharedMem error:
return code = %u\n",rc);
printf("Shared data read was \"%s\"\n",pvAltObject);
rc = DosFreeMem(pvShrObject);
if (rc != NO_ERROR) {
printf("DosFreeMem error:
return code = %u\n",rc);
return NO_ERROR;
// In the following example, Process 1 allocates the shared memory
// and writes its process id to the shared memory. Process 2 obtains
// access to the memory and prints out process id of Process 1.
// In production code, you would also need to include error-
// this code sample assumes that all calls are successful.
#include &sys/types.h&
#include &sys/shm.h&
APP_SHARED_MEMORY
typedef struct {
} shared_t;
// The address of the shared memory
int iShmId;
// The shared mem identifier
// The shared memory is allocated, It is indentified by the key
// APP_SHARED_MEMORY. This same key must be used by process 2
// to obtain access to the shared memory
iShmId = shmget (APP_SHARED_MEMORY, sizeof(shared_t), IPC_CREAT);
shared_mem = (shared_t *) shmat ( iShmid, 0, 0); // Obtain the shared
memory address
shared_mem-& iPID = getpid();
// Write the process
id to the shared mem
// Wait for a signal or do some processing
shmdt(shared_mem);
// Detach the shared memory reference
#include &sys/ipc.h&
#include &sys/shm.h&
#define APP_SHARED_MEMORY 0x20
typedef struct {
} shared_t;
// The address of the shared memory
int iShmId;
// The shared mem identifier
// Obtain the id of shared memory indentified by key APP_SHARED_MEMORY
iShmId = shmget (APP_SHARED_MEMORY, sizeof(shared_t), 0);
shared_mem = (shared_t *) shmat ( iShmid, 0, 0);
// Obtain the shared
// memory address
// print out the
process id of process 1
printf ("Process 1 PID %d\n" , shared_mem-& iPID);
// Detach and remove the shared memory
shmdt(shared_mem);
shmctl(iShmId, IPC_RMID, NULL);
在 OS/2 中,可以使用 DosQuerySysInfo 来获取全部物理内存:
APIRET DosQuerySysInfo (istart, ilast, pBuf, cbBuf)
是返回的第一个系统变量的序号。
是返回的最后一个系统变量的序号。
是数据缓冲区的地址,也就是系统返回变量值的地方。
是数据缓冲区的长度,以字节为单位。
系统调用 DosQuerySysInfo 返回静态变量的值。通用的参数有:
QSV_TOTPHYSMEM:系统中物理内存的总数,以字节为单位。
QSV_TOTRESMEM:系统中常用内存的总数,以字节为单位。
QSV_TOTAVAILMEM:系统中所有进程可以分配的最大内存总数,以字节为单位。
在 Linux 中,可以使用系统调用 sysinfo 来获取全部的物理内存:
int sysinfo (struct sysinfo *info )
表 3. 系统信息映射表
DosQuerySysInfosysinfo
在 Linux 2.3.16 之前的版本上,sysinfo 会以字节为单位给出内存的大小,并以下面这个结构的形式返回一些信息:
struct sysinfo {
/* Seconds
since boot */
unsigned long loads[3];
/* 1, 5, and 15 minute
load averages */
/* Total usable main
memory size */
/* Available memory
/* Amount of shared
/* Memory used by
buffers */
/* Total swap space
/* swap space still
available */
/* Number of current
processes */
char _f[22];
/* Pads structure
to 64 bytes */
从 Linux 2.3.23 (在 i386 体系结构平台上)和 2.3.48 (在所有的体系结构平台上)起,内存大小都是以 mem_unit 整数倍的字节数给出的,所采用的结构如下:
struct sysinfo {
/* Seconds
since boot */
unsigned long loads[3];
/* 1, 5, and 15 minute
load averages */
/* Total usable main
memory size */
/* Available memory
/* Amount of shared
/* Memory used by
buffers */
/* Total swap space
/* swap space still
available */
/* Number of current
processes */
/* Total high memory
/* Available high
memory size */
unsigned int mem_
/* Memory unit size in
char _f[20-2*sizeof(long)-sizeof(int)]; /* Padding
for libc5 */
usigned long ulPhysM
// Get the total number of bytes of physical memory in this system.
DosQuerySysInfo(QSV_TOTPHYSMEM, QSV_TOTPHYSMEM, &ulPhysMem,
sizeof(ulPhysMem));
// ulPhysMem will have the total physical memory in bytes
printf("The total physical memory in bytes is %ld\n", ulPhysMem);
struct sysinfo s_
sysinfo(&s_info);
// the sysinfo structure has the total physical memory of the system.
printf(" The total physical memory available is %ld\n in Bytes",
(s_info.totalram * mem_unit));
在 OS/2 和 Linux 中都可以使用管道(pipe)技术实现进程间通信。进程可以对管道进行读写,就仿佛是标准输入和标准输出一样。在 OS/2 中,管道是双向的,当创建管道时会返回一个句柄,这个句柄可以用来在进程间实现双向通信。在 Linux 中,管道也是双向的,不同之处在于它会返回两个句柄:一个读句柄和一个写句柄。
在对管道进行映射时,需要考虑以下几点因素:
在 OS/2 中,管道大小可以使用一个参数指定。而在 Linux 中,内核将管道大小设置为 4096 字节,用户应用程序不能修改该值。
是有名管道(named pipe,也称为命名管道)还是无名管道(unamed pipe)?无名管道的用法在 OS/2 和 Linux 中相同。OS/2 上的有名管道允许不同机器上的进程相互间进行通信。Linux 则将这种通信只限定在一台机器上。为了在 Linux 上实现 OS/2 中有名管道的功能,可以使用客户端/服务器编程技术(TCP/IP Sockets)。
表 4. 管道映射表
DosCreatePipe
DosCreateNPipe (Server side)
DosConnectNPipe (Server Side)
DosOpen (Client side)
上下文相关的
DosDisconnectNPipe (Server)
DosClose (Client)
上下文相关的
在 OS/2中,可以使用系统调用 DosCreatePipe() 创建一个无名管道:
APIRET DosCreatePipe (HFILE hReadPipe, HFILE hWritePipe, ULONG cb)
hReadPipe和
hWritePipe
分别是指向管道的读句柄和写句柄的指针。
用来设置管道的大小。
在 Linux 中,可以使用系统调用 pipe 来创建一个无名管道:
int pipe (int filedes[])
filedes[0]和
filedes[1]
分别是指向管道的读句柄和写句柄的指针。
在 OS/2 中,有名管道不但可以用来在同一台机器上运行的相关进程或无关进程之间进行通信,甚至可以在不同机器上运行的进程间进行通信。一个进程(服务器进程)创建一个有名管道,并连接到这个有名管道上。然后,客户端进程连接到这个管道的另外一端上,通过读写管道来回传递数据。
在 OS/2 中可以使用系统调用 DosCreateNPipe() 创建一个有名管道:
APIRET DosCreateNPipe (PSZ pszName, PHPIPE pHpipe, ULONG openmode,
ULONG pipemode, ULONG cbOutbuf, ULONG cbInbuf, ULONG msec)
是管道的 ASCII 名。
是管道句柄。
是一组标志,定义了打开管道时的模式。
是一组标志,定义了管道的模式。
cbOutbuf和
分别是为外发(从服务器端到客户端)缓冲区和内达(从客户端到服务器端)缓冲区分配的字节数。
是等待有名管道实例变为可用时等待的最长时间,单位是毫秒。
服务器进程可以使用系统调用 DosConnectNPipe() 连接到管道的一端上,这样就可以将管道设置成监听模式,客户端进程现在就可以使用 DosOpen() 来访问这个管道了:
APIRET DosConnectNPipe (HPIPE hpipe)
是 DosCreateNPipe()返回的管道句柄。
系统调用 DosOpen() 允许客户端进程访问由服务器创建的处于监听模式的有名管道:
APIRET DosOpen(PSZ pszFileName, PHFILEpHf, PULONG pulAction, ULONG
cbFile,ULONGulAttribute, ULONGfsOpenFlags, ULONG fsOpenMode,
ULONGpeaop2)
pszFileName
是有名管道名的 ASCII 值。
包含了有名管道的句柄。
在 Linux 中,有名管道(FIFO)用来在同一台机器上运行的进程之间进行通信。mkfifo() 用来在 Linux 上创建一个有名管道(FIFO)。在创建 FIFO 之后,可以使用 open() 系统调用打开这个 FIFO,以后便可以执行读写操作了:
int mkfifo(const char * pathname , mode_tmode )
是有名管道的名字。
指定 FIFO 的权限。
在 Linux 中,可以使用 open() 来打开 FIFO,其中 filename 参数应该是该文件(FIFO)名的 ASCII值:
int open (const char * filename, int flags )
int open (const char * filename, int flags, mode_t mode)
在 OS/2 中,可以使用 DosRead() 系统调用从有名管道和无名管道中读取数据,其中 hFile 是管道读端的句柄:
APIRET DosRead (HFILE hFile, PVOID pBuffer, ULONG cbRead, PULONG
pcbActual)
在 Linux 中,可以使用 read() 系统调用从管道中读取数据,其中 fd 是管道读端的句柄:
size_t read(int fd, void *buf, size_t count)
在 OS/2 中,可以使用 DosWrite() 系统调用向管道写入数据,其中 hFile 是管道写端的句柄:
APIRET DosWrite(HFILE hFile, PVOIDpBuffer,ULONG cbWrite, PULONG
pcbActual )
在 Linux 中,可以使用 write() 系统调用向管道中写入数据,其中 fd 是管道写端的句柄:
ssize_t write(int fd, const void *buf, size_t count)
对于 OS/2 中的有名管道来说,服务器进程最后需要使用 DosDisConnectNPipe()断开与客户端进程的连接。通常,客户端进程首先要执行 DosClose() 关闭管道,然后服务器端才断开与管道的连接。但是,服务器端的确也可以在客户端之前断开连接,从而强制客户端关闭管道。对于无名管道来说, DosClose() 就可以关闭连接了:
APIRET DosDisConnectNPipe (HPIPE hpipe)
APIRET DosClose (HPIPE hpipe)
是管道读/写端的句柄。
在 Linux 中,可以使用
close 系统调用关闭 FIFO,其中 fd 是 FIFO 读/写端的句柄:
int close (int fd)
In this example, thread 1 creates an unnamed pipe, and writes to the
pipe. Thread 2 reads the data once it becomes available.
ReadHandle
/* Read handle of pipe */
WriteHandle = 0;
/* Write handle of pipe */
/* Size of pipe */
= NO_ERROR;
/* API return code */
// Create pipe
rc = DosCreatePipe(&ReadHandle, &WriteHandle, PipeSize);
// get access to the pipe
// Write to pipe
rc = DosWrite(&WriteHandle,&cBuf, &len, &ulWrote);
// Close write handle
rc = DosClose(&WriteHandle);
// wait for the data to come
// Read from pipe
rc = DosRead(&ReadHandle, &cBuf, &ulLen, &ulRead);
// Close read handle
rc = DosClose(&ReadHandle);
// Client program
// This client program opens a named pipe, and connects to the
// server. It writes messages to the pipe and closes the pipe
// when it has no more data to write.
# define PIPE_NAME \\PIPE\\EXAMPLE
#define BUF_SIZE 255
/* Pipe handle */
HFILE PipeHandle = NULLHANDLE;
APIRET retCode = NO_ERROR; //Return code
message[BUF_SIZE +1]
/* Message buffer */
// Open the pipe
rc = DosOpen(PIPE_NAME,
&PipeHandle,
FILE_OPEN,
OPEN_ACCESS_READWRITE |
OPEN_SHARE_DENYREADWRITE |
OPEN_FLAGS_FAIL_ON_ERROR,
rc = DosWrite(PipeHandle, message,
strlen(message), &bytes);
// end of message
} while (!done);
// close the write handle of PIPE
rc = DosClose(PipeHandle);
// Server program
// This Server program opens a named pipe, and waits for a client
// connection. It writes messages to the pipe, and closes the pipe
// when it finds no data to write
# define PIPE_NAME \\PIPE\\EXAMPLE
#define BUF_SIZE 255
HPIPE PipeHandle = NULLHANDLE;//Pipe handle
ULONG ulBytes = 0;//Bytes read /written
CHAR message[BUF_SIZE + 1]
/* Input/Output
APIRET retCode = NO_ERROR;
/* Return code */
// create a named pipe
retCode = DosCreateNPipe(PipeName,
/* Name of pipe to
&PipeHandle,
/* Handle returned
for pipe */
NP_ACCESS_DUPLEX,
/* Duplex pipe */
NP_TYPE_MESSAGE |
NP_READMODE_MESSAGE |
NP_WMESG |
/* Write messages */
NP_RMESG |
/* Read messages */
/* Unique instance of
sizeof(message),
/* Output buffer size */
sizeof(message),
/* Input buffer size */
/* Use default
time-out */
// wait for connection
retCode = DosConnectNPipe(PipeHandle);
// connected, read the data
retCode = DosRead(PipeHandle, message,
sizeof(message),&ulBytes);
// display the message
printf("\n\nMessage received was: %s\n\n", message);
// end of message
} while (!done);
// end of message, disconnect
rc = DosDisConnectNPipe(PipeHandle);
In this example, thread 1 creates an unnamed pipe, and writes to the
pipe. Thread 2 reads the data once it is available in the unnamed pipe.
int file_pipes[2];
const char some_data[] = "123";
// Create pipe
pipe(file_pipes);
// Get access to pipe
// Write to pipe
data_processed = write(file_pipes[1], some_data, strlen(some_data));
// Close the write handle
close(file_pipes[1]);
char buffer[BUFSIZ + 1];
// get access to pipe
// read from pipe
data_processed = read(file_pipes[0], buffer, BUFSIZ);
// close the read handle
close(file_pipes[0]);
// Client program
/* This client program logs a message to the FIFO, and closes the
write end when it finds no message to write */
# define FIFO_NAME "/home/guest/testpipe"
# define BUF_SIZE 2552
buff[BUF_SIZE+1];
// Open the FIFO with write permission
fd = open (FIFO_NAME, O_WDONLY);
// read from fifo
// display the message
strcpy(buffer, "test message");
dataWrite = write(fd, buffer, BUF_SIZE);
// done with all the messages
} while(!done);
// Close the write end of the FIFO
fclose(fd);
//Server program
/* This Server program creates a FIFO, and waits for a message. If
the message is available, it prints the message to screen.
It closes the read end when it finds no message to read */
# define FIFO_NAME "/home/guest/testpipe"
# define BUF_SIZE 2552
buff[BUF_SIZE+1];
// create a FIFO with user having write and read permission, and
// group and others having read permission.
retCode = mkfifo(FIFO_NAME, S_IWUSR | S_IRUSR |
S_IRGRP | S_IROTH);
// Open the FIFO with read permission
fd = open (FIFO_NAME, O_RDONLY);
// read from fifo
// display the message
dataRead = read(fd, buffer, BUF_SIZE);
// display the message
} while(!done);
fclose(fd);
OS/2 可以在扩展属性中保存有关文件的其他信息;Linux不支持这种功能。
表 5. 文件信息映射表
DosQueryPathInfostat
在 OS/2 中,可以使用系统调用 DosQueryPathInfo 来获取有关文件或目录的信息:
APIRET DosQueryPathInfo (pszPathName, ulInfoLevel, pInfoBuf,
cbInfoBuf)
pszPathName
是一个指向文件名或目录名的指针。
ulInfoLevel
是所需要的信息的级别,其值可以为1、2、3、5;级别 4 是保留值。
是包含所请求的路径信息级别的存储空间的地址:
级别 1:获得文件状态 3,其中包括基本的文件信息,例如创建的日期/时间,最后一次访问的日期/时间,等等。
级别 2:获得文件状态 4。这与文件状态 3 相同,另外还多个一个域显示扩展属性的大小。级别 2 用来在读取文件之前查询扩展属性的大小。
级别 3:获得有关文件的 EA(扩展属性)的一个子集。这些信息用来:
保存文件对象的注释(例如,文件创建者的名字)。
对文件对象进行分类(例如,源代码、例子、图标、位图)。
描述文件对象中包含的数据的格式(例如,数据记录)。
在文件对象中附加上其他数据。
级别 4:保留。
级别 5: 给出完整的文件名。
是存储空间 pInfoBuf 的长度,以字节为单位。
在 Linux 中,可以使用系统调用 stat 来获取有关文件或目录的信息:
int stat (const char *pcPathName, struct stat *pBuf)
pcPathName
是一个指向文件名或目录名的指针。
是一个指向包含文件/目录信息的结构的指针。该结构如下所示:
struct stat {
/* device */
/* inode */
/* protection */
/* number of hard links */
/* user ID of owner */
/* group ID of owner */
/* device type (if inode device) */
/* total size, in bytes */
/* blocksize for filesystem I/O */
/* number of blocks allocated */
/* time of last access */
/* time of last modification */
/* time of last change */
请注意,如果另外一个进程正在对文件对象进行更新,而两个这个进程的共享和访问权限存在冲突,那么 DosQueryPathInfo 调用就会失败。为了防止出现这种情况,文件对象必须以一种拒绝写的共享模式打开。
Linux 中的
stat 调用对正在访问的文件不需要任何访问权限,它只需要对该路径中的所有目录具有查找权限即可。
char * pcPathAndCalFileName = "C:/test.Tbl";
// old file name
char * pcPathAndOldFileName = "C:/test.Old";
// new file name
FILESTATUS3 fsStatB
// structure filled
by DosQueryPathInfo
// Rename the current test file, if it exists, to get ready for
// calibration
if (!DosQueryPathInfo(pcPathAndCalFileName, FIL_STANDARD, &fsStatBuf,
sizeof(fsStatBuf))){
if (!DosQueryPathInfo(pcPathAndOldFileName,
FIL_STANDARD,
&fsStatBuf, sizeof(fsStatBuf))){
// Remove the old calibration data file "test.Old"
remove(&pcPathAndOldFileName[0]);
// Rename the current "test.Tbl" test data file to the name
"test.Old" that is used for old test data.
rename( &pcPathAndCalFileName[0], &pcPathAndOldFileName[0] );
char * pcPathAndCalFileName = "/home/guest/test.tbl";
// old file name
char * pcPathAndOldFileName = "/home/guest/test.old";
// new file name
// buffer with
// file information
if (stat(pcPathAndCalFileName, &buf) == 0){
// File exists so rename it. Before that check if `test.old' exists.
// If yes then remove that file
if (stat(pcPathAndOldFileName, &buf) == 0){
remove(pcPathAndOldFileName);
// Errors returned by this call should be handled appropriately
rename(pcPathAndCalFileName, pcPathAndOldFileName)
// More error handling
// File does not exist
有两种方法可以复制文件句柄:重新分配一个新句柄,或者将原来的一个已知句柄用作一个新句柄。
在 OS/2 中,DosDupHandle() 可以同时支持这两种特性。在 Linux 中,如果要分配一个新句柄,可以使用系统调用 dup();如果要将一个已知句柄当作一个新句柄使用,可以使用系统调用 dup2()。
表 6. 复制文件句柄的映射表
DosDupHandledup
在 OS/2 中,可以使用系统调用 DosDupHandle() 来复制文件句柄:
APIRET DosDupHandle(HFILE hFile, PHFILE pHfile);
保存要复制的文件句柄。
是一个指向保存句柄信息位置的指针。
可能值如下表所示:
0xFFFFFFFF
分配并返回一个新文件句柄。
将该值作为一个新文件句柄分配。有效值可以是分配给标准 I/O 的任何值,或者现在进程打开的文件句柄。
Linux 使用系统调用 dup()。当在 OS/2 中 DosDupHandle() 使用第二个参数(例如 0xFFFFFFFF)时, 0xFFFFFFFF 就表示简单地分配一个新句柄。在 Linux 中,dup 可以实现相同的功能:dup() 系统调用返回一个新的文件描述符;而 dup2() 则复制在文件描述符参数中传递进来的文件描述符:
int dup(int oldfd)
int dup2(int oldfd, int newfd)
要复制的文件描述符。
oldfd 所复制到的文件描述符。
下面这个例子对 OS/2 和 Linux 中文件句柄的复制进行了比较:
StandardOutput = 0x,
// for standard output
StandardError
// for standard error
AllocNewHandle = 0xFFFFFFFF
// for allocating a new handle
HFILE hfOut, hfErr, hfSaveOut, ulSaveE
unsigned long ulR
// Set the standard handles
hfOut = StandardO
hfSaveOut = (HFILE) AllocNewH
hfErr = StandardE
hfSaveErr = (HFILE) AllocNewH
// Save the original handles.
DosDupHandle(hfOut, &hfSaveOut);
DosDupHandle(hfErr, &hfSaveErr);
// Assign some other file handle or pipe handle to the to the standard
// output & error
ulRet = DosDupHandle(hpWrite, &hfOut);
ulRet = DosDupHandle(hpWrite, &hfErr);
Do some processing
// Retrieve the original handles.
ulRet = DosDupHandle(hfSaveOut, &hfOut);
ulRet = DosDupHandle(hfSaveErr, &hfErr);
// Close the temporary handles
ulRet = DosClose(hfSaveErr);
ulRet = DosClose(hfSaveOut);
StandardOutput = 1,
//for standard output
StandardError
//for standard error
// Instead of HFILE, in Linux the file descriptor is
// an integer
int fdOut, fdErr, fdSaveOut, fdSaveE
// Set the standard handles
fdOut = StandardO
fdErr = StandardE
// Save the original handles.
fdSaveOut = dup(fdOut);
fdSaveErr = dup(fdErr);
// Assign some other file handle or pipe handle to the to the
// standard output and error
fdOut = dup2 (hpWrite, fdOut);
fdErr = dup2 (hpWrite, fdErr);
: Do some processing
// Retrieve the original handles.
fdOut = dup2(fdSaveOut, fdOut);
fdErr = dup2(fdSaveErr, fdErr);
// Close the temporary handles
close(fdSaveErr);
close(fdSaveOut);
表 7. 文件访问映射表
DosSetPathInfoutime
在 OS/2 中,可以使用系统调用 DosSetPathInfo():
APIRET DosSetPathInfo (PSZ pszPathName, ULONG ulInfoLevel,
PVOID pInfoBuf, ULONG cbInfoBuf, ULONG flOptions)
pszPathName
是一个指向文件或子目录的完整路径名的 ASCII 值的指针。
ulInfoLevel
是正在定义的文件目录信息的级别。有效值可以为 1 和 2。
pInfoBuf 是一个指向正在设置的信息的指针。
对于级别 1 来说,传递的是 FILESTATUS3。它包含文件的一些标准信息,例如创建日期/时间,访问日期/时间等。
对于级别 2 来说,传递的是 EAOP2 结构。这样可以设置文件的扩展属性集。OS/2 将其他信息保存在扩展属性列表中。
DosSetPathInfo() 不但要设置文件或子目录的访问/修改时间,而且要设置其他一些参数,例如文件创建的日期/时间、文件大小以及文件属性。然而,在本节中,我们只讨论如何修改文件的访问/修改时间。因此,我们使用级别 1,并传递一个包含新的访问/修改时间的 FILESTATUS3 结构。
在 Linux 中,可以使用系统调用 utime() 来修改指定文件的访问时间:
int utime(const char * filename, struct utimbuf * buf );
是一个指向要修改访问时间的文件的 ASCII 文件名的指针。
是一个指向包含该文件的新访问/修改时间结构的指针。utimbuf 结构如下所示:
struct utimbuf {
/* access time */
/* modification time */
char *pcFileName= "xyz.ini";
FILESTATUS3 fs3DirI
/* get the file information */
DosQueryPathInfo(pcFileName, FIL_STANDARD, &fs3DirInfo,
sizeof(fs3DirInfo));
/* get the current time */
tm *ptm = localtime(&lTime);
/* update the file information structure with the new modification
fs3DirInfo.fdateLastWrite.day
= ptm-&tm_
fs3DirInfo.fdateLastWrite.month
= ptm-&tm_mon + 1;
fs3DirInfo.fdateLastWrite.year
= ptm-&tm_year - 80;
fs3DirInfo.ftimeLastWrite.twosecs = ptm-&tm_sec / 2;
fs3DirInfo.ftimeLastWrite.minutes = ptm-&tm_
fs3DirInfo.ftimeLastWrite.hours
= ptm-&tm_
/* set the new file information */
DosSetPathInfo(pcFileName, FIL_STANDARD, &fs3DirInfo,
sizeof(fs3DirInfo), 0);
/* Get the local time
tTime = time(0);
localtime_r(&tTime,&tmr);
/* Update the structure with the new modification time */
buf.actime = buf.modtime = mktime(tmr);
/* set the new file information */
iRc = utime("check.txt", &buf);
本文介绍了将程序从 OS/2 移植到 Linux 上时有关内存管理、IPC 机制和文件处理的一些对应问题。本文的目的并不是要详尽地介绍各个技术细节,而是提供一份将应用程序从 OS/2 移植到 Linux 上时使用的参考手册。本文基于可以达到的适合的移植设计,给出了一些必要的提示和警告,从而帮助简化程序移植的过程。有关本文中提到的 Linux 调用的更详细用法的解释,开发人员可以参考手册页。本系列的下一篇文章将介绍与设备驱动程序接口、定时器及相关问题有关的系统调用的映射。
您可以参阅本文在 developerWorks 全球站点上的
请确保您阅读了本系列的第一篇,
developerWorks,
2004 年 2 月)。
H.M. Deitel 和 M.S. Kogan 所著的
(Addison-Wesley, 1992 年)仍然可以在使用书籍站点上找到;ISBN 为
0-201-54889-5。
有关 Linux 中编程调用的更多细节或具体的调用,不要忘记查看 Linux 中的“info”和手册页。
C++ 程序员可以参考
推荐了一个到
服务器现在都捆绑了 Linux。
IBM 红皮书为那些希望切换到 Windows 或 Linux 上的 OS/2 管理员提供了一份
IBM Global Services
提供了一个 OS/2 到 Linux 定制应用估价、移植和测试服务。
developerWorks,2002
年 3 月)介绍了 Linux 团队在将 LANDP(LAN 分布式平台)从 OS/2 移植到 Linux 上时,LANDP 所碰到的一些问题。
您也可以在同一台机器上同时使用 OS/2 和 Linux。请参考南加利福尼亚州的 OS/2 用户组 的
资源列表,以及 SUSE LINUX 入门中的
对双重引导的一个简要介绍。
专区可以找到为 Linux 开发人员准备的更多参考资料。
在 Developer Bookstore 的 Linux 区可以购买
从 developerWorks 的
部分,下载运行在 Linux 上的 developerWorks 订阅产品的免费试用版,包括 WebSphere
Studio Site Developer、WebSphere SDK for Web services、WebSphere Application
Server、DB2 Universal Database Personal Developers Edition、Tivoli Access
Manager 以及 Lotus Domino Server。为了更容易上手,请自行收集每个产品的 how-to 文档和技术支持。
TA的最新馆藏[转]&[转]&[转]&[转]&[转]&
喜欢该文的人也喜欢}

我要回帖

更多关于 c malloc 释放内存 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信