Hunter的大杂烩 技术学习笔记

2006-01-02

如果有dnd吧就好了

Filed under: 闲聊 — hunter @ 10:32 am

办杀人游戏俱乐部,需要极为慎重

为什么这么说呢,因为现在杀人游戏俱乐部是有钱就能开. 中国的喜欢跟风现象特别的严重.特别是在一些二级城市,如果市场不成熟就开了一个大规模的杀人游戏俱乐部,那么后果是比较严重的.没有足够的客户群,或者客户群不够成熟,都是令经营者极为头痛的事情.

经过我们的市场搜索和调查,现在国内的省会城市基本上都会在三个月内有一至两家杀人吧.他们的经营效果如何呢?我们拭目以待.

特别值得注意的是,杀人吧的经营定位和运营.当一个行业饱和之时,也就是残酷竞争来临之时.杀人吧的老板们,你们准备好面对激烈的恶性竞争了吗?

随着聚会经济的崛起,我们建议杀人吧的老板们,把目光投入聚会经济.

所以我们提倡,要在杀人吧里面安装上无线宽带,让有笔记本电脑的服户在杀人吧里面能一边上网,一边杀人,就像人们在星巴克一样.让人们,除了杀人外,,还有其他的事情可做.

看到清华bbs上有些人经常组织玩TRPG(http://www.newsmth.net/bbs0an.php?path=%2Fgroups%2Fliteral.faq%2FFantasy%2Fgamebar%2Ftrpg) , 很是过瘾,不知道什么时候深圳也有这类的 RPG吧:)

2005-12-10

微软高级开发者管理峰会演讲摘要:产品质量的基石

Filed under: 闲聊 — hunter @ 1:07 am

主页 测试园地 软件测试 自动测试 质量保证 软件工程 精彩文章 资源下载 论坛交流
微软高级开发者管理峰会演讲摘要:产品质量的基石

微软Bug管理

来自:微软 蔡锫
(2002.12.11)

一.团队组织

1.常见问题

没有人愿意做测试
觉得养不起那么多测试人员
开发人员不遵循规范,随心所欲
项目经理事必躬亲,分身乏术
2.微软团队模型

各角色的职责

角色 职责
项目经理 编写功能规范,协调各角色关系
产品经理 客户联系的桥梁,进行需求分析
用户教育 让产品容易使用
发布经理 保证产品顺利发布

二.项目管理

1.常见问题

无法决定项目所需的资源(人力和预算)
无法决定项目的进度表
无法控制外包项目的进度和质量
2.微软项目管理– 多里程碑式流程

每个里程碑完成部分功能
便于团队集中力量完成一个又一个功能
提供多个机会以适应需求的更改
如何完成一个里程碑

步骤一: 达成共识
基本完成需求调研和分析 (产品经理负责)
确定大方向和长中短期目标
所有角色都参与讨论并真正认同结论
产生的文档:
常见用户情景:覆盖80%以上功能
Vision:言简意赅地说明大方向,并有激励团队的作用
步骤二: 完成项目计划
编写详细的功能规范(项目经理负责)
在编程前想清楚所有功能流程,并引导用户明确需求
所有角色都参与审阅功能规范
制订开发计划和进度表(开发团队)
制订测试计划和进度表(测试团队)
分配资源(人力和预算)
形成项目综合计划和综合进度表
产生的文档:
功能规范,开发计划,测试计划(用例),项目综合计划
开发进度表,测试进度表,综合进度表
步骤三: 完成功能
开发人员分别完成自己的功能
使用版本控制工具
使程序员及时check out和check in,避免积累大量代码
及时进行模块间的整合,及时发现问题(daily build)
对每一项可测试的功能进行测试,无需等待
使用测试用例工具,对功能进行完整和重复的检验
使用BMS进行缺陷跟踪
记录所有程序问题
实现解决Bug的自动流程
按照综合进度表不断检查进度
 
使用的工具:
版本控制工具 VSS
缺陷跟踪工具 Raid/BMS
测试用例管理工具
步骤四: 稳定与发布
测试组全面地测试功能,包括性能和稳定性
开发组全力配合解决Bug
使用BMS进行
监测质量情况
预测发布日期
专家会诊机制:
决定Bug的优先度
决定哪些Bug可以等到下个里程碑或版本中解决
决定由谁解决某个Bug
 
使用的工具:
版本控制工具 VSS
缺陷跟踪工具 BMS
测试用例管理工具
三. 微软的开发管理经验:100%以Bug为核心

1.Bug 及常见类型

功能未实现,和规格说明书不一致
不能工作:死机,没反应
不兼容
边界条件
界面、消息、提示不够准确,不友好
把尚未完成的工作也作为一个Bug
文档与帮助信息中的缺陷也是Bug
2.RAID/BMS的基本功能

完整的Bug数据库
整个产品组的中央记录和控制
强大的查询功能,有效地跟踪项目的状态
所有的记录无法删除,对于每个记录只能一直添加内容
丰富的报表功能,为产品发布提供判断标准
3.Bug 记录中的有效信息 状态
负责人
问题种类
严重级
优先级
修改时间
登记时间
缺陷来源
解决方案
运行环境
缺陷关联
附件
附图
缺陷细节

4.Bug 的严重程度

死机,数据丢失,主要功能组完全丧失,系统悬挂
主要功能丧失,导致严重的问题,或致命的错误声明
次要功能丧失, 不太严重,如提示信息不太准确
微小的问题,对功能几乎没有影响,产品及属性仍可使用. 如有个错别字
5.激活的Bug数量的趋势

代码完成前:很少
代码完成后:增长很快
接近Beta: 下降
接近RC: 奔向零
产品质量和里程碑的信号
每天新建的Bug 与 修正的 Bug 相比较
Active 状态 Bug 的总数
四.微软的一天

1. 让我们看看项目中每个角色的一天是如何度过的

开发
测试
项目经理
注:里程碑的每个阶段每个角色的工作有不同侧重点,我们以“完成功能”阶段为例

微软的一天从几点开始?

答案:半夜

为什么?

因为Daily Build是所有工作的核心,而且是在半夜自动启动。

每日构造Daily Build

你知道自己所用Windows的版本号吗?
Daily Build的意义:
模块得以及时整合
要求程序员及时把最新代码放入代码库
用脚本语言和编译/链接工具实现
BVT Build Verification Test
对Build进行验证
Blocking Bug
让Build无法完成的问题
BVT中发现的问题
2.程序员每天上班前最担心什么?

答案:因为自己昨天的代码check-in,造成Blocking Bug.

为什么?

因为每天的Build是所有人当天工作的基础:
程序员需要Build验证与其他模块的接口
测试需要Build发现新Bug,并验证新Build中已解决的Bug

有Blocking Bug怎么办?

解决问题,并对今天的Build打Patch。

开发人员的正事

经历对Build的提心吊胆和争分夺秒之后,第一件事做什么
答案:打开缺陷跟踪工具,查看指定给自己的Bug,解决高优先度的Bug。因为质量重于新功能。

接下来,开发人员会…

从版本控制工具中Check out代码
修改代码(解决Bug或实现新功能)
取得版本工具中最新变化,在本机Build和单元测试
请开发组同事作Code Review
Check in代码

3.测试人员第一件事做什么?

答案:打开Raid/BMS,查看指定给自己的Bug,验证已解决的Bug。

接下来,测试人员会…

根据测试用例检验今天的Build
在Raid/BMS中记录新发现的Bug
4.专家会诊

参加者:项目经理和开发组长、测试组长
通过Raid/BMS评估每个未解决的Bug
决定Bug优先度
可否等到下个里程碑或版本解决?
谁来解决
预测项目实际进度和发布时间
缺陷走势图

5.回顾微软的一天

构造: daily build
开发: 解决blocking bugs, 实现功能, check-out, code review, check-in
测试: BVT, 使用测试用例进行测试
项目经理/组长: 专家会诊
6.微软的做法解决了那些常见问题?

质量问题

以前解决过的问题发布时又出现了,需要返工
无法预估发布时间 过早发布,带来质量和维护问题
测试发现的问题被忘却或不了了之
无法衡量测试员和开发员的工作
程序中的问题往往在发布后才发现
文档管理问题

文档与程序脱节,文档成为程序结果的描述
项目组把写文档看成负担
团队协调问题

开发人员各自为战,进行整合时发现模块衔接中的严重问题 需要作大的改动
没有保管好公司以往的版本和代码,无法满足用户对旧版本的更改要求
开发人员离职对项目带来很大冲击,没有人知道代码在哪,或无法读懂
五.提高软件管理的步骤

1. 使用Raid/BMS,将流程管理自动化
2. 使用测试用例管理工具
3. 使用文档管理工具
4. 使用版本控制工具,进行Daily Build
5. 建立代码标准
6. 建立Code Review机制
7. 建立专家会诊机制
8. 建立团队沟通机制
9. 根据需要调整团队结构

主页 | 测试园地 | 软件测试 | 自动测试 | 质量保证 | 软件工程 | 精彩文章 | 资源下载 | 论坛交流 | 关于本站

Copyright® 2003 Test Engineer, Inc. All rights reserved.

2005-12-09

什么是爱情

Filed under: 闲聊 — hunter @ 2:26 pm

什么是爱情!
这是篇很好的文章。
如果你暂时没有时间细细品味
也请你有时间的时候将自己的心沉淀下来,
用心的看。
好的文章真的不多了……
单身,有时不一定是贵族。
单身也许会比较自由,
但自由也有一个同义词,叫作寂寞。
因为人不是什麽时候都喜欢一个人独处的;
有时好东西需要跟人分享,
有时候难过需要人安慰。
单身贵族产生的原因,是因为
经济上的独立、人格上的独立以及感情上的独立。
独立是什麽?
独立是需要而不依赖,
一个独立的人需要异性,而不依赖异性。
做情人之前,她应该先是朋友。
她成为你的朋友之後,出现在你的生活里,
才有可能认识你、了解你、知道你的长处
而对你产生好感,进一步发展感情,
变成情人、对象。
世界上的颜色并非只有白色和黑色,
黑与白之间还有很漫长的灰色地带。
只要多相处,便能发现对方的优点、产生好感,
这才是发展感情的自然过程。
『一见锺情』以及『从一而终』的感情是不切实际的,
我们需要的不是这种不切实际而虚幻的感情。
有人形容跟异性交往,
就好像在海边捡石头,大家都会捡喜欢的那一颗。
一旦捡到一颗你最喜欢的石头,便把它带回家去,
好好对待它,因为那是你唯一的石头。
而且要记住,从此後不要再到海边去。
永远相信,
我已经找到最大、最美、最适合我的那一颗。
跟异性交往最重要的不是他有多好,
而是他对你有多好。
一个人如果条件很好,有一百分,
可是这一百分之中,他只给你三四十分,或一二十分;
相反地,另一个人也许只有七八十分,
可是他却是全心全意的对待你,
那你应该选择那一个?
其实,每一个人的条件都是一样的。
不管你有多好,都还有人比你更好。
你虽然做不到一个『最好的人』,
可是你却做得到一个『对对方最好的人』。
每一个男孩子都可以说:
『虽然我不是世界上最好的男人,但我是世界上对你最好的男人』。
反过来女孩子也是一样,这是每一个人都做得到的。
感情最重要的是在於他对你的好,
而不是他自己有多好。
但是如果有一个人本身已经很好了,
对你又是真心真意,真心爱你,
那麽你真的可以把一生托付给他。

现在女性考虑婚姻的唯一条件,
应该就是你爱不爱他,他爱不爱你,
是不是真心真意对你,
跟他在一起会不会有压力,会不会快乐,
而非他有什麽!
人间的真爱是很难得的。
在人的一生中,
很难找到一个
你真正爱,真正可以跟他过一辈子的人。
如果你怯於表达,
或害怕会有什麽事,
错失一辈子可能只有一次的真爱,
那就太可惜了,
所以一定要采取主动,把心里的话说出来。
如果一个男孩子
因为女孩子对他采取主动而看不起她,
那麽这个男孩子不是男生,而是畜生。
更何况,幸福比面子重要,
如果牺牲一时的面子可以换得一生的幸福,
是非常值得的,勇敢把心里的话说出来,
不要隐藏自己的真心。
千万别说缘份未到,其实缘份到处都有,
但却是稍纵即逝,如果『缘』不及时把握,
那就没有『份』了。
大多数的女性对感情是偏重於精神,
男性则偏於物质。
男孩子除了对女孩子殷勤体贴外,
也要学会对女孩子负责任,
要将对天下所有女孩子的殷勤体贴,
全部用来对一个女孩子。
另外,
刚毅木纳并不能讨女孩欢心,
所以要学习对女孩子甜言蜜语,多说好话。
男人,为性而爱;
女人,为爱而性。

一个维持起来轻松、愉快的感情容易长久!
一个维持起来艰难而痛苦的感情不易长久,
这时後就应该有所选择。
我们都是凡夫俗子,
要的是平凡而幸福并且快乐的爱情。
对所有的感情而言,过程远比结果重要。
为什麽?因为所有的感情都是没有结果的。
什麽是结果?结婚吗?
结婚之後就过着幸福快乐的日子吗?
可见我们不以感情的结果来评断它的价值感情,
也不以时间的长短来论定它的价值。
对感情而言,凡是发生过的都存在,
凡是存在过的都有价值。
世界上的感情每一段、每一分、每一秒
都是值得珍惜的。
婚姻是人生里最大的一场赌局。
在这无限长的时间里,
我们还要互相呈现最丑陋的一面给对方看。

婚姻和恋爱不同的是:
恋爱可以花二个小时打扮自己,
精神奕奕的向对方献殷勤、体贴,轻松完成任务。
可是婚姻就无法随时维持高亢的状态。
所以,婚姻是一场大赌注,
需有万全的准备、周详的计划、十足的信任,
然後再去押它一把,即使是这样都还有可能输掉。
因此如果在赌之前,就知道自己
不是心甘情愿、不是很爱他、并不想跟他过一辈子,
那麽这一场赌注注定是要输的。
千万不要
为了爱情之外的任何一个理由结婚。
感情的可贵
不在於可从对方获得什麽,
一方面是一种被依赖、被需要的感觉。
有人依赖我,需要我,
我会得到满足

面对感情,我们所要采取的就是三不政策:
第一是不急:不要急着结婚。
结婚虽然是很美好的事,但是不要着急,
该是你的就是你的。
第二是不怕:不要害怕付出。
彼此必需一辈子努力,才能把感情维持好。
你不可能『得到』一个美好的感情,
你只能找到一个你心爱的人,
来共同经营、造就、完成一段美好的感情。
如果你没决心做好男人,那你就得不到好女人。

世界上没有一个坏男人会有一个好女人,
坏男人只会有可怜的女人;
同样的,也没有一个坏女人会有一个好男人。
何况感情中还有一个
跟我们共同努力、想把它做好的人。
这个合夥事业成功的机会是很大的,
关键就在於你是否愿意努力、付出而已。

第三是不要放弃。
当你受了打击、挫折、伤害,
当你感到灰心、失望时,
有一个人无条件的、永远站在你这一边,
支持你、鼓励你、安慰你,
让你重新站起来面对这个世界。
这个力量,是再亲的父母不能给我们的,
是再要好的朋友也不能给我们的,
只有在人生的旅程中所找到的心爱的伴侣,
才能够给我们。

其实爱情是人生唯一的、真正珍贵的;
也是唯一的、真正值得追求的东西。
只要你有一个心爱的人,
你就有了原动力,
你就能面对全世界。

2005-11-17

在 Linux 中使用共享对象

Filed under: C++,Linux,技术话题,闲聊 — hunter @ 3:12 pm

内容:

示例程序

运用共享内存

参考资料

参考资料

关于作者

对本文的评价

相关内容:

用 Eclipse 平台进行 C/C++ 开发

Java programming for C/C++ developers

developerWorks Toolbox subscription

订阅:

developerWorks 时事通讯

让共享内存为您服务,而不是为您制造麻烦
级别: 初级

Sachin O. Agrawal
资深软件工程师, IBM Software Labs India
2004 年 5 月

充分利用共享内存并不总是容易的。在本文中,IBM 的 Sachin Agrawal 与我们共享了他的 C++ 专门技术,展示了面向对象如何去利用一个独特而实用的进程间通信通道的关键优势。
就时间和空间而言,共享内存可能是所有现代操作系统都具备的最高效的进程间通信通道。 共享内存同时将地址空间映射到多个进程:一个进程只需依附到共享内存并像使用普通内存一样使用它,就可以开始与其他进程进行通信。

不过,在面向对象编程领域中,进程更倾向于使用共享对象而不是原始的信息。通过对象,不需要再对对象中容纳的信息进行序列化、传输和反序列化。共享对象也驻留在共享内存中,尽管这种对象“属于”创建它们的进程,但是系统中的所有进程都可以访问它们。因此,共享对象中的所有信息都应该是严格与特定进程无关的。

这与当前所有流行的编译器所采用的 C++ 模型是直接矛盾的:C++ 对象中总是包含指向各种 Vee-Table 和子对象的指针,这些是 与特定进程相关的。要让这种对象可以共享,您需要确保在所有进程中这些指针的目标都驻留在相同的地址。

在一个小的示例的帮助下,本文展示了在哪些情况下 C++ 模型可以成功使用共享内存模型,哪些情况下不能,以及可能从哪里着手。讨论和示例程序都只限于非静态数据成员和虚函数。其他情形不像它们这样与 C++ 对象模型关系密切:静态的和非静态非虚拟的成员函数在共享环境中没有任何问题。每个进程的静态成员不驻留在共享内存中(因此没有问题),而共享的静态成员的问题与这里讨论到的问题类似。

环境假定

本文仅局限于用于 32 位 x86 Interl 体系结构的 Red Hat Linux 7.1,使用版本 2.95 的 GNU C++ 编译器及相关工具来编译和测试程序。不过,您同样可以将所有的思想应用到任意的机器体系结构、操作系统和编译器组合。

示例程序

示例程序由两个客户机构成:shm_client1 和 shm_client2,使用由共享对象库 shm_server 提供的共享对象服务。对象定义在 common.h 中:

清单 1. common.h 中的定义

#ifndef __COMMON_H__
#define __COMMON_H__

class A {
public:
int m_nA;
virtual void WhoAmI();

static void * m_sArena;
void * operator new (unsigned int);
};

class B : public A {
public:
int m_nB;
virtual void WhoAmI();
};

class C : virtual public A {
public:
int m_nC;
virtual void WhoAmI();
};

void GetObjects(A ** pA, B ** pB, C ** pC);

#endif //__COMMON_H__

清单 1 定义了三个类(A、B 和 C),它们有一个共同的虚函数 WhoAmI() 。基类 A 有一个名为 m_nA 的成员。定义静态成员 m_sArena 和重载操作 new() 是为了可以在共享内存中构造对象。类 B 简单地从 A 继承,类 C 从 A 虚拟地继承。为了确保 A、B 和 C 的大小明显不同,定义了 B::m_nB 和 C::m_nC 。这样就简化了 A::operator new() 的实现。 GetObjects() 接口返回共享对象的指针。

共享库的实现在 shm_server.cpp 中:

清单 2. 库 – shm_server.cpp

#include
#include
#include
#include
#include
#include

#include “common.h”

void * A::m_sArena = NULL;

void * A::operator new (unsigned int size)
{
switch (size)
{
case sizeof(A):
return m_sArena;

case sizeof(B):
return (void *)((int)m_sArena + 1024);

case sizeof(C):
return (void *)((int)m_sArena + 2048);

default:
cerr < < __FILE__ << ":" << __LINE__ << " Critical error" << endl; } } void A::WhoAmI() { cout << "Object type: A" << endl; } void B::WhoAmI() { cout << "Object type: B" << endl; } void C::WhoAmI() { cout << "Object type: C" << endl; } void GetObjects(A ** pA, B ** pB, C ** pC) { *pA = (A *)A::m_sArena; *pB = (B *)((int)A::m_sArena + 1024); *pC = (C *)((int)A::m_sArena + 2048); } class Initializer { public: int m_shmid; Initializer(); static Initializer m_sInitializer; }; Initializer Initializer::m_sInitializer; Initializer::Initializer() { key_t key = 1234; bool bCreated = false; m_shmid = shmget(key, 3*1024, 0666); if (-1 == m_shmid) { if (ENOENT != errno) { cerr << __FILE__ << ":" << __LINE__ << " Critical error" << endl; return; } m_shmid = shmget(key, 3*1024, IPC_CREAT | 0666); if (-1 == m_shmid) { cerr << __FILE__ << ":" << __LINE__ << " Critical error" << endl; return; } cout << "Created the shared memory" << endl; bCreated = true; } A::m_sArena = shmat(m_shmid, NULL, 0); if (-1 == (int)A::m_sArena) { cerr << __FILE__ << ":" << __LINE__ << " Critical error" << endl; return; } if (bCreated) { // Construct objects on the shared memory A * pA; pA = new A; pA->m_nA = 1;
pA = new B;
pA->m_nA = 2;
pA = new C;
pA->m_nA = 3;
}

return;
}

让我们更详细地研究清单 2:

第 9-25 行:operator new ()
同一个重载的操作符让您可以在共享内存中构造类 A、B 和 C 的对象。对象 A 直接从共享内存的起始处开始。对象 B 从偏移量 1024 处开始,C 从偏移量 2048 处开始。

第 26-34 行:虚函数
虚函数简单地向标准输出写一行文本。

第 35-39 行:GetObjects
GetObjects() 返回指向共享对象的指针。

第 40-46 行:初始化器(Initializer)
这个类存储共享内存标识符。它的构造函数创建共享内存及其中的对象。如果共享内存已经存在,它就只是依附到现有的共享内存。静态成员 m_sInitializer 确保在使用共享库的客户机模块的 main() 函数之前调用构造函数。

第 48-82 行:Initializer::Initializer()
如果原来没有共享内存,则创建,并在其中创建共享对象。如果共享内存已经存在,对象的构造就会被跳过。 Initializer::m_shmid 记录标识符, A::m_sArena 记录共享内存地址。

即使所有进程都不再使用它了,共享内存也不会被销毁。这样就让您可以显式地使用 ipcs 命令来销毁它,或者使用 ipcs 命令进行一些速查。

客户机进程的实现在 shm_client.cpp 中:

清单 3. 客户机 – shm_client.cpp

#include “common.h”

#include
#include

int main (int argc, char * argv[])
{
int jumpTo = 0;
if (1 < argc) { jumpTo = strtol(argv[1], NULL, 10); } if ((1 > jumpTo) || (6 < jumpTo)) { jumpTo = 1; } A * pA; B * pB; C * pC; GetObjects(&pA, &pB, &pC); cout << (int)pA << "\t"; cout << (int)pB << "\t"; cout << (int)pC << "\n"; switch (jumpTo) { case 1: cout << pA->m_nA < < endl; case 2: pA->WhoAmI();

case 3:
cout < < pB->m_nA < < endl; case 4: pB->WhoAmI();

case 5:
cout < < pC->m_nA < < endl; case 6: pC->WhoAmI();
}

return 0;
}

#include
void DoNothingCode() {
pthread_create(NULL, NULL, NULL, NULL);
}

第 6-35 行
客户机进程获得指向三个共享对象的指针,建立对它们的数据成员的三个引用,并且 — 依赖于命令行的输入 — 调用三个虚函数。

第 36-39 行
没有被调用的 pthread_create() 函数用来强制链接到另一个共享库。来自所有共享库的任何方法都可以满足这一目的。

共享库和客户机可执行文件的两个实例的编译方法如下:

gcc shared g shm_server.cpp o libshm_server.so lstdc++
gcc -g shm_client.cpp -o shm_client1 -lpthread -lshm_server -L .
gcc -g shm_client.cpp -o shm_client2 -lshm_server -L . lpthread

注意,交换了 shm_client1 和 shm_client2 中 shm_server 和 pthread 的链接顺序,以确保 shm_server 共享库在两个可执行文件中的基址不同。可以使用 ldd 命令进一步验证这一点。示例输出通常如下所示:

清单 4. shm_client1 的库映射

ldd shm_client1

libpthread.so.0 => (0x4002d000)
libshm_server.so => (0x40042000)
libc.so.6 => (0x4005b000)
ld-linux.so.2 => (0x40000000)

清单 5. shm_client2 的库映射

ldd shm_client2

libshm_server.so => (0x40018000)
libpthread.so.0 => (0x40046000)
libc.so.6 => (0x4005b000)
ld-linux.so.2 => (0x40000000)

这里的主要目的是使构建的两个客户机二进制文件具有不同的服务器库基址。在这个示例程序的上下文中,使用不被调用的 pthread_create() 函数和不同的共享库链接顺序来达到这一目标。不过,没有具体规则或统一步骤可以作用于所有链接;需要根据不同的情况采取不同的方法。

例 1:shm_client1 与 shm_client1

在下面的输出中,首先在 shell 中调用 shm_client1。由于现在没有共享对象,于是 shm_client1 创建了它们,引用它们的数据成员,调用它们的虚函数,然后退出 — 将对象留在了内存中。第二次,进程只是引用数据成员和虚函数。

清单 6. shm_client1 与 shm_client1 的输出日志

$ ./shm_client1

Created the shared memory
1073844224 1073845248 1073846272
1
Object type: A
2
Object type: B
3
Object type: C

$ ipcs

—— Shared Memory Segments ——–

key shmid owner perms bytes nattch status
0x000004d2 2260997 sachin 666 3072 0

$ ./shm_client1
1073840128 1073841152 1073842176
1
Object type: A
2
Object type: B
-> 0
-> Segmentation fault (core dumped)

当第二个进程试图通过类型 C * 的指针去引用数据成员 A::m_nA 时(您会记得 C 虚拟继承自 A),共享对象内的基子对象(base sub-object)指针会被读取。共享对象是在现在不存在的进程的上下文中构造的。因此,读取 A::m_nA 和 C::WhoAmI() 时读入的是内存垃圾。

因为 Vee-Table 和虚函数位于 shm_server 共享库内部,恰巧在同一虚拟地址被重新加载,所以,再次引用类型 A * 和 B * 的指针时不会观察到任何问题。

因此,GNU 所采用的 C++ 对象模型没有成功地处理虚拟继承。

例 2:shm_client1 与 shm_client2

在下一个示例输出中,在命令行中首先执行 shm_client1,然后执行 shm_client2:

清单 7. shm_client1 与 shm_client2 的输出日志

$ ./shm_client1

Created the shared memory
1073844224 1073845248 1073846272
1
Object type: A
2
Object type: B
3
Object type: C

$ ipcs

—— Shared Memory Segments ——–
key shmid owner perms bytes nattch status
0x000004d2 2359301 sachin 666 3072 0

$ ./shm_client2
1073942528 1073943552 1073944576
1
-> Segmentation fault (core dumped)

$ ./shm_client2 3
1073942528 1073943552 1073944576
2
-> Segmentation fault (core dumped)

$ ./shm_client2 5
1073942528 1073943552 1073944576
-> 1048594
-> Segmentation fault (core dumped)

然而,Vee-Table 位于 shm_server 共享库内部:在 shm_client1 和 shm_client2 中它被加载到不同的虚地址。这样,读取 A::WhoAmI() 和 B::WhoAmI() 时读入的都是内存垃圾。

运用共享内存

在共享内存中具体使用 C++ 对象时您应该考虑两个主要问题。首先,Vee-Table 指针用于访问虚函数,而对数据成员的访问直接使用编译时偏移量实现。因此,对于所有这种共享对象来说,所有进程中的 Vee-Table 和虚函数都应该具有相同的虚地址。关于这一点,没有一成不变的规则,不过,为相关的共享库采用适当的链接顺序大部分时候都会管用。

另外,永远不要忘记,虚拟继承的对象有指向基对象的基指针。基指针引用进程的数据段,而且永远是特定于进程的。难以确保所有的客户机进程中都有相同的数字值。因此,假如使用 C++ 对象模型,要避免在共享内存中构造虚拟继承的对象。但是,也不要忘记,不同的编译器采用的是不同的对象模型。例如,Microsoft Compiler 使用进程无关的偏移量来为虚拟继承类指定基对象,因而不存在这个问题。重要的是,确保所有客户机进程中的共享对象的地址相同。

参考资料

参考资料

您可以参阅本文在 developerWorks 全球站点上的 英文原文.

Stanley B. Lippman 的 Inside the C++ Object Model (Addison-Wesley, 1996)是理解 C++ 内部机制的极好参考资料。

RFC 1014 – XDR: External Data Representation Standard是描述和编码不同计算机体系结构间的数据的标准。它定义了描述字符串、变长数组以及类似的结构体的语言。

不要忘记去查看关于 ld、ldd、ipcs、ipcrm 等各个函数的系统手册页。

尽管 Eclipse 主要是一个 Java 开发环境,它的体系结构也确保了对其他编程语言的支持。通过 用 Eclipse 平台进行 C/C++ 开发( developerWorks, 2003 年 4 月)一文深入学习。

IBM 的 VisualAge C++是一个高级 C/C++ 编译器,有用于 AIX 和部分 Linux 发行版本的版本。

Java programming for C/C++ developers( developerWorks,2002 年 5 月)教程从已经熟悉 C++ 的开发人员的视角出发介绍了 Java 语言。

在 developerWorks Linux 专区可以找到更多为 Linux 开发者准备的参考资料。

购买 Developer Bookstore Linux 区 打折出售的 Linux 书籍。

通过 developerWorks Subscription 使用最新的 IBM 工具和中间件来开发和测试您的 Linux 应用程序:您可以自 WebSphere、DB2、Lotus、Rational、和 Tivoli 得到 IBM 软件,以及一个可以在 12 个月内使用这些软件的许可,所有的花费都比您想像的要低。

自 developerWorks 的 为您的 Linux 应用开发加油提速 专区下载可以运行于 Linux 之上的经过挑选的 developerWorks Subscription 产品免费测试版本,包括 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 文章和技术支持。

关于作者
Sachin 五年来一直在从事 C++ 各方面的工作,包括为期三年的对各种编译器的 C++ 对象模型的研究。他当前在 IBM Global Services India 工作。您可以通过 sachin_agrawal@in.ibm.com 与他联系。

对本文的评价
您对这篇文章的看法如何?

太差! (1) 需提高 (2) 一般;尚可 (3) 好文章 (4) 真棒!(5)

建议?

developerWorks 中国 > Linux >

 

胸怀的宽广度是一个人的事业限度

Filed under: 闲聊 — hunter @ 2:21 pm

胸怀的宽广度也是一个人的事业限度,如果心中容不下异类,听不进别人的不同意见,不会和敌人共存,很难想像这样的人会取得成功。心胸狭窄的人眼里容不下一粒沙子,不允许别人有一丝一毫的错误,得失心很重,不能批评,这样的人只能活在自己的世界里,不具备成就事业的起码素质。

《史记》有言:“大行不顾细谨,大礼不辞小让”,胸怀宽广的人总是不拘小节,能够随时把眼光控制在大目标上,让小事情为大事情服务。胸怀坦荡者一般具备下面几个特征:

1. 给人视觉印象分明,轮廓突出,自然随意,没有犹豫不定的感觉。额头较高的人一般智商很高,很聪明;额头眉骨凸现的人有一股傲气,不服输,喜欢争斗;额头宽阔给人感觉大气,容易为别人接受,达成一致意见,这种人往往成为一个单位里的领导者,或者是后备的领导干部。

2. 视野的关注点总是集中在自己的理想或者目标上,不会斤斤计较于日常琐事。要成就大事首先需要一个“种子”和环境,种子就是人的理想,环境就是适合种子生长的人的素质,其首要的就是人的胸怀和气度。所谓要取得天下要先有容纳天下的气度,就是这个道理。

3. 具有乐观的人生态度,不会让小事情主导自己的心情。心胸宽广的人往往会对人生抱一种积极向上的看法,很达观,可以娴熟地驾驭自己的心情。

« Newer PostsOlder Posts »

Powered by WordPress