2014年8月21日星期四

从《西游记》看人之创业与家庭关系

转自:http://bbs.tianya.cn/post-free-1414315-1.shtml

从《西游记》看人之创业与家庭关系
   休言/文
  以前看《西游记》,总是抱着游戏的心态去看。逐渐的接触了企业创业,并知道了更多的有关创业与家庭的故事后,突然明白了,原来《西游记》其实也是很好的创业与家庭关系的启蒙教材。
  其实一个人的创业与家庭息息相关,而一个人的创业过程也无时无刻不有着家庭的帮助与配合。而在创业与家庭的同步进行中,也如同唐三藏一样需要历尽劫难方能得成正果。
  
  创业与家庭第一劫 小成绩后之红尘劫
  
  常常会听到有很多女性抱怨,其老公与其刚走在一起的时候,家徒四壁,但是随着老公开始创业发家后,家庭就开始不和睦,老公要么陷于情人的温柔乡无法自拔,要么沉寂与赌博而冷落家庭,要么干脆吸毒开始家庭败落。
  出现这种情况,其实只是个人创业中第一个劫——“红尘劫”。
  一直认为,大多数创业者都好比土匪,创业的原因是因为家庭贫困无奈而为之。如同以前因为官府腐败官逼民反时期的粗人,因为无法生存揭竿而起,占山为王。但是对于大多数人而言,因为缺乏长远的发展目标与远大的抱负,更多人在占山解决了自己的生存问题后就开始自封固步,忘乎所以。所以,注定了也就是土匪的料子,逐渐的鱼肉乡里,烧杀抢掠,最终只落下人神共愤,身败名裂。
  只有少数的人能够抗拒此中诱惑,具有着长远的发展目标与远大抱负,如梁山好汉般求的大乘,或如李自成般最终最了几天皇帝。
  土匪性格是人的劣根之一,正是这种性格,注定了大多数人创业成功第一步后就无法在前行,而最终抛弃糟糠妻,背弃完美家。
  应该如何避免这样的局面出现呢?
  再回到《西游记》:
  《西游记》主要由五人组成,从表面的分配上,似乎是唐三藏为领导,但是在实际上,从家庭关系上却是孙悟空来主宰的,引导着取经队伍的前行。从这个意义上讲,唐三藏其实就是传统中国小家庭的主妇身份:虽然不进行大的事物决策,却掌管了家庭的经济,在可节制的范围内,任劳任怨苛守家庭,为丈夫外面的打拼解决后顾之忧。
  但是,孙悟空始终是猴子,野性是难训的,如同大多数男人骨子里的叛逆精神。在五指山下被压的时候,想叛逆无从叛逆;当日子逐渐好起来脱离了贫困线衣食无忧的时候,这个野猴子就开始难以节制自己行动,出言顶撞,时不时要从新回到花果山那花花世界去。
  开始的时候唐僧的处理是暴力的,不理智的,整天唠叨不说,还时不时的念紧箍咒增加其与猴子的感情裂痕,当裂痕在白骨精的怂恿下走到破裂边缘的时候,又喊出了离婚一招,将孙猴子彻底赶回了他的花花世界。
  如果因此猴子不回到取经队伍,那么猴子如何能最终修得正果,唐三藏的历史大业如何完成?
  这个时候,多亏了观音点化,以及佛家安置,故意增加了更大灾难,使唐三藏想起了猴子,从而在续前缘。
  这样的结果毕竟是神话故事,现实中是断断无观音点化或者冥冥安排喜剧巧合的,那么作为现实中的家庭女主角就应该从唐三藏身上学习经验、汲取教训。那就是:
  有唐三藏的容忍精神,但是不能唠叨的让猴子生烦产生叛逆心理;明知道猴子已经走在叛经离道的路上,应该具有观音大士的点化聪慧,不能还要紧箍咒不断增加刺激摩擦;不能率性而为高呼分离,而要用宽容与希望赢得其回归的心。也只有这样,其才可以不会堕落与土匪为伍,成为红尘劫的牺牲品。
  而更多创业其中的男性,更应该从猴子的最终回归明白这些劫难道理,清楚自己的发展定位,明确诱惑是阻挠自己继续发展的障碍,理智选择前行之道,才能够在创业的大道上继续前行。
  创业与家庭第二劫 步入正途亲情劫
  一般度过第一劫的人少之又少,往往留下的难以从第二劫度过,原因无他,能参破红尘的大有人在,但是能够屏除亲情的却寥寥无几了。
  当孙猴子接受西天佛祖委托,协助唐三藏西天取经此等光宗耀祖的光辉事迹传遍三山五岳后,猴子那些以前的兄弟就纷纷而出了,目的一个,借助猴子的关系,来吃点唐僧肉,也算是沾个亲戚的光么。
  所以,牛魔王陈利,大鹏王威逼。这个时候,如果猴子稍微有些须动摇,那么唐三藏恐怕就难保其身了。
  现实中,当一个人创业到一定地步的时候,亲情就成了最难以逾越的魔障:
  七大姑八大婆的关系来了,要求在企业有个位置,而这个时候正是需要专业人才管理专业事物的时候,这些目不识丁的人的参与,对于发展公司到底是利是弊?
  以前的各种哥们关系来了,要求承包或者参与公司相关的合作事宜,而这个时候你所需要的合作伙伴应该是越正规越好,杂牌军、游击队能是你共同发展的对象么?
  八秆子打不到的关系来了,什么你现在发达了不要忘记穷亲戚啊什么的,需要你救济啊,而这个时候,你的流动资金正是你发展路上最所需要的,把资金一旦抽调,你将如何发展呢?
  往往很多的创业者就是倒在了这个阶段。
  公司里充斥着不学无术的各种关系,让真的人才无所发挥,各种外部合作关系都是以前老哥们,不是供应质量不成,就是不能如期到位,却不能依靠法律维护自己权益,这个亲戚资助点,哪个亲戚贡献点,当在亲戚面前赢得了足够的面子后,回头才发现公司已经因为流动资金的严重短缺举步唯坚。
  而唐三藏的家庭就很好的处理了这些事情。
  他总是在每个时刻提醒着孙猴子取经大事的重要性,并升华到拯救人类的高度,他总是利用每一个出现的妖魔鬼怪现身说法,指出了只有取经成功才能最终人间和谐的伟大目标性,并明确了妖神之间的界限,从而使孙猴子能早早在心目中种下伟大的发展念头,为穿破亲情劫坚定基础。
  而在亲情劫真的出现的时候,唐三藏又作为急先锋勇敢的冲到最前头,先让猴子所谓的亲戚将自己捉了,激起孙猴子对亲戚的不满心理,从而勇决的作出正确选择。
  所以在家庭创业的过程中,那些在创业者背后的女人们应该记得,当亲情劫开始出现的时候,能够处理这些亲情的只有你们,只有你们完善的处理了亲情障碍的时候,你的老公才能顺利的在继续往前走。
  
  创业与家庭第三劫 接近终点之自大劫
  度过前两劫后,一般的创业者基本在家庭稳定基础上接近于成功了,但是这个时候,自大这个魔障就会不请自到来骚扰了。
  这个时候,往往很多的人已经因为成功而沾沾自喜自以为是的觉得世界都在自己掌控中,飞扬跋扈已经忘记了什么是道义什么操守。唐三藏借助老龟度过无边大江,过江后将要修得正果的喜悦忘记了老龟的临终嘱托,所以当再次返还的时候,老龟发怒,致使经书流失,让辛苦一程的取经大业差点毁于一旦。
  而更多的创业者,这个时候已经成为所谓的领导人,权利的集中,成功的熏陶,已经忘记了曾经的承诺与誓言,眼比天高,只能看见比自己尊贵的人与物。
  然而千里之堤、溃于蚁穴却总是作为千古预言在时刻准备着寻找你破败的空隙。
  没有消费者,你如何成就大业,但是这个时候你偏偏忘记了对消费者的质量承诺;
  没有政策支持,你如何完成梦想,但是这个时候你偏偏就开始蔑视政策,轻浮法规。
  于是你产品质量开始出现问题,甚至纯粹的假大空;于是你开始偷税漏税,甚至恶意欺骗金融贷款;于是你以为你能偏离事物规律如同神仙一样制定秩序,甚至老虎吃天般强行搬动本来自己资金不能运做的大石头,拆东墙补西墙。这样的你,还能走写去么?
  所以更多的人的结局就是遁离海外有家无归,更多的是跄踉入狱,身败名裂。
  《西游记》里唐三藏没有更好的提前处理此类事件,仅仅是作为事情发生后才出面为老龟道歉才没有使取经行动失败。但是现实中,为什么作为创业最大背后支持的女性不能未雨绸缪呢?为什么不在企业发展到这一步的时候多规劝老公,继续保持创业之初本分呢?
  但是更多的女性不是这样,因为成果而奢侈,自己早被权与利迷失了方向,所以不但不会规劝,更多的是纵容甚至挑唆,结果呢,当企业烟消云散的时候,自己也是竹篮打水了。
  
  创业与家庭第四劫 功成后之贿赂劫
  你无职无权的时候,有人巴结你么?有人给你行贿么?没有的,行贿的前提就是你有职有权,所以在你功成名就的时候,行贿就如影随行适时出现。
  《西游记》里唐三藏家庭没有这样的受贿过程,只有行贿的事例,所以这个时候人物的重心就转移到了强行索贿的罗汉身上。
  当如来佛祖要两个罗汉带领唐三藏领取经书的时候,两个罗汉索要了唐三藏不多的行李。应该说两个罗汉已经身居高位,贵为罗汉,不该由此龌龊行为,但是因为权利带来的贪念索贿,最终事发,落了个轮回重度。
  而现实中创业的人,在功成后往往就陷入了贿赂的深渊。
  因为你有权,就有人来求你办事,你可以学习如来佛祖正义抵抗,也可以即来之且安之顺手笑纳。但是作为创业者的妻子,这个时候更多的是扮演罗汉角色。作为如来佛祖最信任的人,你的决定往往决定了创业者的发展前途。
  看贪官污吏案发史,有多少是因为后院起火呢,后院不淑,往往注定了大业的崩溃。但是现实中后院起火可不比罗汉受贿没有央及如来,现实中后院起火,往往烧掉的是整个事业王国。
  所以作为成功男人背后的女人,你应该在这个时候保持你清醒的头脑,保持一份平常的心态,不仅仅要维护好你丈夫的后院,还要勇敢的去劝柬他,远离贿赂诱惑,从而使创业真正得到大圆满。

视频: 创业者很难平衡事业和家庭的关系

管理时间: 应该兼职创业还是全职创业

应该兼职创业还是全职创业?关于这个问题,尽管很多创业者和专家最终打算全职创业,但他们认为,开始时兼职会比较好。

开始创业时选择兼职有几个好处。一来你有全职工作的收入和利益,可以降低风险;二来你有时间让企业慢慢成长。

《不辞职如何建立小企业》的作者菲利普·霍兰德(Philip Holland)说:“开始创业时选择兼职是最好的,在这个过程中,你可以了解到创业需要什么,万一失败了,也不至于无路可走。”

然而,这不是说兼职创业就没有危险性或不好的地方。首先,你无法全心全意经营你的事业,无法经营你的服务对象,因为公司无法及时地与顾客联系并解决他们提出的问题,他们会觉得受挫,认为公司无法提供满意的服务或者无法及时满足他们的需求。

对于兼职创业的人来说,也许最大的风险是精力不够。一边做着全职工作,一边兼职创业,使得你几乎没有什么休息、娱乐的时间,这样,你自己和家人的生活都会受到影响。

《任何人都可以创业》(You Can Start Your Own Business)的合作者阿诺德·萨诺(Arnold Sanow)提醒创业者:“白天上班、晚上创业会带来很多冲突和压力。”他还说:“白天上班、晚上创业之间的冲突无法避免,由此产生的家庭问题也无法避免,很多甚至会导致离婚。”

但这也不是说兼职工作就是不可能的。萨诺认为,如果你善于管理时间,自我约束能力比较强,家人和朋友也会支持你,那么兼职创业是可以的。他还说,最重要的是你的自我牺牲精神,“觉得自己已经有一份工作,创业就可以不必太认真就大错特错了,而是应该对两者有一个很好的协调安排。”

AHA!

如果你想一边全职工作,一边兼职创业,可以寻求创业孵化器,只需一小笔花费,它就可以为公司提供办公场地、接听电话服务,还可以提供复印、传真机等。最重要的是,它还可以提供会计、市场调查等帮助。

市场很重要

任何人创业,首先要对市场前景有一个深入的研究,而且这一步可以让你充分认识到,你是该全职创业还是兼职创业。

对于创业,也不能过于狂热而忽视了现实情况。如果公司的产品或服务需求性很强,没有大的竞争,那么毫无疑问可以全职创业。但如果另一方面,市场没那大的需求,而只是可能会有市场或发展,那么最好先兼职创业。

接下来就要调查公司的竞争性、公司所在地区的经济状况、人口状况以及潜在顾客的状况。例如,如果公司准备办一个上层社会美女沙龙,就要调查一下当地类似的社团有多少,有钱的女性客户有多少以及她们愿意消费多少。

当你确定公司的事业有需求后,详细列举公司的目标和策略,还要进行深入研究,做好市场调查,由此确定你的创业目标,这样,公司就可以建立长期发展的可能性,让公司的事业顺利发展。如果你是兼职创业,一定要写一份创业计划,以便日后长期发展你的事业。

有些创业项目就适合兼职做,例如电子商务、餐饮、直销服务业等。进行市场调查,制定创业计划,可以让你从实际出发决定是否兼职创业(关于市场调查和创业计划的具体内容,见第6、7、10章)。

如果你选择传统方式创业,需要全心全意地付出,你也可以想一想:也许有办法可以兼职。例如,不选择开餐馆,只是提供餐饮服务,这样,你也需要制作菜单,吸引顾客,但你可以选择在晚上或周末的时间来做。

WARNING

千万要给自己留条后路。如果你同现在的老板竞争,你就违背了合同中的非竞争条款,会让你陷入道德深渊。如果你确实要做相同的领域,你可以选择你的老板所忽视的一个小的切入点。

融资计划

决定是兼职创业还是全职创业很重要的一个因素是你的资金情况。大多数专家建议,在决定全职创业前,放下一切,先做半年到一年。(当然,时间长短不限;但是详细做好创业的计划,你就可以确定在你的企业盈利之前能够撑多久。)

你还需要考虑的基本因素包括你现有的存款,你是否有可以变现的资产,你的朋友或家人是否可以为你提供资金或贷款,如果你决定全职创业,你的配偶或其他家庭成员是否能支撑整个家庭。

如果你像很多人一样,全职创业的话就没有资金来源,那么兼职就是一个很好的选择。然而即使这样,你心里也必须清楚的一点是:你的企业什么时候可以盈利,让你可以辞掉全职工作,专心创业?

萨诺认为,一个比较好的方法是当你的企业盈利最少达到你目前的全职工作收入的30%时,你就可以全职创业了。他说:“有了这30%的收入,加上花费白天的时间来推动你的企业发展,这时候就可以过渡为全职创业了。另外一种方法是:将你全职收入的工资尽量存起来,这样,一旦你决定全职创业,这些资金就可以弥补你创业所需的收入。”

AHA!

如果你觉得一边全职工作,一边兼职创业太难了,全职创业又风险太大,也可以考虑全职创业兼带一份零工,这样就可以保证你在全力创业之余有一点额外的收入。你可以在晚上或周末来做兼职,这样就可以保证你随时同顾客保持联系。

家庭问题

心理的情感支柱同资金和市场因素在你创业中同样重要,而且对于你决定是兼职创业还是全职创业也同样重要。

首先要向你的配偶或者家庭的其他重要成员说明情况,他们支持你创业吗?他们能理解无论是全职还是兼职创业都需要他们,需要整个家庭作出牺牲吗?在你决定创业之前,一定要让你的亲人充分表达他们的观点和担忧,而不是等到你已经创业了两三个月以后,这时想要退出就已经太晚了。

接着,和家人一起讨论解决问题的实际办法。(例如,你的配偶可以接过你手中的一些家务活吗?)然后为兼职创业制定一些基本准则,例如,周日下午一定不工作,晚饭餐桌上不讨论工作。

要想兼职创业顺利,又不影响家庭关系,时间管理很重要。你要善于平衡时间,要早起,不要将宝贵的时间浪费在不重要的电话以及其他事情上。

个人因素

创业不但会对你的家人造成影响,对你自己的生活同样会造成影响。如果你全身投入创业,放弃稳定的、轻松的收入利益,结果导致失眠,生活窘迫,那么你最好还是选择兼职创业。但是,如果你现在的全职工作每天需要工作很长时间,上下班的路程也很远,家里还有两岁的三胞胎,牺牲所有这些选择兼职创业会让你吃不消,如此一来,创业对你来说可能就毫无意义了。

当然,全职创业很费时间,但兼职创业加一份全职工作,压力更大。如果你决定这么做,那么就要好好考虑一下其对你生活的影响。你可能将不得不利用晚上、周末以及午饭的时间,甚至节假日、病休假等时间来经营公司,因此,你很可能不得不放弃看电影、看电视、看书或者健身等娱乐项目。如果你需要工作到很晚,然后拖着疲惫的身体回家,还得花四个小时准备第二天早上顾客需要的材料,你会是什么感觉?如果你想兼职创业取得成功,这都是你需要付出的,所以一定要认真考虑你是否有这种脑力和体力来承受一边全职工作,一边全职创业。

TIP

如果你没有经济能力承受全职创业,但你必须全天同你的顾客保持联系,你该怎么办?可以考虑找一个在你没时间的时候帮你打理这些事情的人合伙。

慢慢来

所有没有报酬的工作就不值得做吗?一些企业家之所以创业只是出于兴趣爱好,例如做手艺的或做餐饮业的,他们觉得如果全职创业就是在冒险,乐趣就没有了。正如企业家萨诺·阿诺德所说的:“选择全职创业就将这种冒险变成你的职业了。”

有些企业家无法确定他们的商业项目是否会过时,他们不愿意牺牲娱乐的时间来创业,因为这样需要他们发展自己已经有但自己并不知道的技能以及做一些他们宁愿让别人来做的事情。

千万不要为了创业成功只想着创新,而忽视了现实中该承担的责任。对全职创业需要付出什么一定要清楚。也许你可以雇一个人做你不愿意做的事,比如产品销售或日常运营。

谨慎抉择

是全职创业还是兼职创业完全由你自己决定,无论你选择哪种方式,成功的秘诀在于你能够实事求是地评估你的资源,你愿意付出多少,你在当地所能获得的支持,等等。只有全面考虑这些因素,你才能做出正确的选择。

兼职创业的建议

一边全职工作,一边兼职创业不是那么容易的,但也是可以做到的。《任何人都可以创业》(You Can Start Your Own Business)的合作者萨诺·阿诺德提出以下建议,有助于你兼职创业。

让你的家人尽量参与到创业中来。比如接接电话,装装信封,或者把东西整理好。让你的家人参与进来可以大大地提高效率,而且还可以让他们感到他们也是创业中的一员。

做好放弃个人娱乐时间的准备。你可能没有时间看电视、看书或者继续其他爱好,但要相信,这种牺牲是值得的。否则,不仅是你的全职工作,创业也会受到影响。

专注于眼前的事。白天工作的时候就专心工作,千万别因为创业的事影响你白天的工作。

善于利用时间。比如,利用午饭或早上的时间打打电话,利用路上的时间整理文件。

善于利用时差和现代科技。如果你的客户在其他地区或其他国家,可以利用时差的优势,选择早上或下班后打电话,还可以在任何时候通过传真或电子邮件同客户保持联系。

不能犯规。千万别在上班时间打电话或者使用你老板的资源或设备,这是大忌。

要诚实。只有你最了解自己的情况,但是你最好同你的老板提前说明你要创业的想法,只要不影响工作,老板一般是不会干涉的。这样一来,老板就会觉得你很诚实,而不是故意隐瞒什么。

2014年8月17日星期日

ヤフオクに複数枚の写真を載せたい

ヤフオクでは、実は4枚以上の写真を掲載することが可能です。

基本的には、先ほどの項目の内容を盛り込むことはできますが、「3枚でどうしても足りないよ」っていうケースもあるかと思います。

細部を写真でお知らせすることは 落札者にとっても大変入札しやすくなります。

ヤフオクではリンク(外部リンク)は7つまで貼り付けることが可能なのです。
外部リンクすることによって、通常の3枚とプラス7枚の計10枚分を載せることができるのです。

それでは、その方法について説明しましょう。

画像をどこかにアップロードしてからリンクする方法をとります。
アップロード場所を2箇所紹介しておきます。

① ヤフーフォト
② ヤフージオシティーズ

オークションIDをお持ちでしたら、YAHOOジオのHPを無料で利用できます。

 http://geocities.yahoo.co.jp/

ヤフー登録しているとジオシティーズに無料でHPを作成できるサービスにどなたでも参加できますので、ジオシティーズをサーバーとして活用することをオススメします。

そのHPを簡単に製作し、画像をアップロードしてしてその後、タグ入力で掲載可能になります。

ヤフーでも他のサーバーよりもジオシティーズを推奨してますし
出品欄からも安全にリンクをしてくれるので写真が掲載されないなどのトラブルもありません。

※ まず上記のヤフーフォトなどのスペース(プロバイダーのサーバー等)に、載せたいと思う写真をアップロードじます。

アップロードができれば次に、その写真を右クリックして[プロパティ]を確認してください。

その中に写真があるアドレスがありますので
そのアドレスを選択してホームページのHTMLにコピー&ペーストします。

ヤフオク側の商品説明欄に、



を貼り付けるだけで写真は反映されます。
その他にも 何枚か写真を貼り付けたいときには


 改行を入れるといいでしょう


また写真を画面中央にしたい場合は
中央にそろえるを使います


ここにを貼り付けます











 をつけてもいいです。


※タグに詳しい方は上のタグをみて??と思うかもしれませんが、

タグの代わりにタグを使用しています。

タグはヤフーでは
で閉じなくても良いようです。


画像の下に、説明を付け加えたいのであれば


説明をここに書きます。。。


また

リンクして見せる画像につきましては
サイズなどは関係ありませんので
自由なサイズで掲載可能です。

2014年8月15日星期五

即時作業系統 (RTOS) 的基本概念

转自:http://cms.mcuapps.com/devscenes/ds0001/

筆者在 2012 年 2 月時收到一份 IAR 的電子刊物,裡面附帶有一篇不錯的 PDF 文章 – Basic Concepts for Real Time Operating Systems 
筆者閱讀之餘,覺得該文足夠精簡摘要地歸納出 RTOS 的要旨,因此嘗試作個翻譯,以饗懶得看英文的讀者。又筆者翻譯的方式有時會較為接近意譯,並不拘泥於原文的字句,也可能會配合文意,另外附加一些補充資料。
此外,MCUApps 將會持續整理一些各具特色的 RTOS 系統的資料,置於 RTOS 技術資料 提供給大家參考。
以下就是我們的譯文

本文將會闡釋 RTOS 的一些基本概念,但不會擴及個別的 RTOS 與其特點。為了簡化起見,只會討論 RTOS 最具代表性的一些重要特性。
在談過 RTOS 的架構,以及為何我們需要採用它之後,我 (IAR System 的 Mats Pettersson) 將會解釋典型 RTOS 中的每一個基本元件,並且展示他們是如何被整合到系統之中。
在本文中出現的少許程式碼範例,所採用的是 Express Logic 的 ThreadX 系統。

Thread 導向的設計

設計嵌入式應用幾乎總是相當具有挑戰性的。為了降低複雜度,我們通常會採用 threads 導向的設計,把一個專案切分成比較易於管理的小塊(也就是 threads),而後每一個 thread 負責該應用程式的一部分。這樣的系統有助於識別 threads 之間的重要順序。也就是說,某些 threads 具有即時性的需求,必須盡量快速並且正確地回應它們。如果你的系統採用了專業的 RTOS,一定會有劃分 threads 優先權 (prioritization) 的設計。除了優先權之外,也會提供一套乾淨而且被仔細測試過的 API,有助於簡化 threads 之間的通訊。
所以如果我們採用 RTOS 的話,就會獲得一些工具能夠:
  • 確保能夠在即時約束條件 (real-time constraints) 內執行時間關鍵 (time-critical) 部分的程式碼。或許同樣都很重要,但高優先權的 threads 所需要的即時行為,並不會受到低優先權 threads 的影響。
  • 確保易於開發和維護複雜的應用。開發和維護小的 threads,比起硬搞整套應用更為容易。此外,對於低優先權 threads 的更動也不會影響到高優先權 threads 的即時處理。
  • 能夠將整體應用程式的不同部分,分派給多個開發人員。每一個開發人員能夠擔負應用程式的一個或多個 threads,而且當他們在進行開發工作時,還會有一套乾淨的 API 能夠讓不同的 modules / threads 相互溝通。
當然也可以不必動用到 RTOS,就把應用大卸八塊成為不同的 threads。但是採用了 RTOS 的話,你不但能夠創建 threads,同時也具備一些讓它們能夠彼此溝通的工具,再加上能夠確保能夠在即時約束條件內執行完畢 threads 具時間關鍵的部分工作。由於採用了 RTOS,不同 threads 之間的介面將會變得非常乾淨,在進行開發的時候你就可以省時省力。

RTOS 是如何工作的?

RTOS 的核心被稱為 kernel,並提供有一個可以透過 kernel 去創建 threads 的 API。一個 thread 就像是一個擁有自己的堆疊、並帶有 Thread 控制區塊(TCB – Thread Control Block)的函式。除了 thread 本身私有的堆疊之外,每個 TCB 也保有一部分該 thread 的狀態訊息。
kernel 還包含有一個 scheuler,scheuler 會按照一套排程機制來執行 threads。各種 scheulers 之間主要的差異,就是如何分配執行他們所管理之各種 threads 的時間。基於優先權的 preemptive scheuler 是嵌入式 RTOS 之間最流行和普遍的 threads 調度演算法。通常情況下,相同優先權的 threads 會以 round-robin 循環的方式加以執行。
多數內核還會利用系統時脈 (system tick) 中斷,其典型的頻率為 10ms。如果在 RTOS 中缺乏系統時鐘,仍然能夠有某種基本形式的調度,但時間相關的服務則否。這種與時間有關的服務內容包括:軟體定時器、thread 睡眠 API 呼叫、thread 時間片段、以及逾時的 API 呼叫。
為了實現系統時脈中斷,可以透過嵌入式晶片的硬體計時器。大多數的 RTOS 有能力動態地擴增或重新設置計時器的中斷頻率,以便讓該系統進入睡眠,直到被下一個計時器期限或外部事件喚醒。例如,如果你有一個對耗能敏感的應用程式,您可能不希望每 10ms 就運行一次不必要的系統時脈處理程序。所以假設應用程式處於閒置狀態,想要把下一個定時器期限改為 1000ms。在這種情況下,計時器可以被重新規劃成 1000ms,應用程式則會進入低功耗模式。一旦在這種模式下,處理器將呈現休眠狀態,直到產生了外部事件、或是計時器的 1000ms 到期。在任一種情況之下,當處理器恢復執行時,RTOS 就會根據已經經過了多少時間來調整內部時間,並恢復 RTOS 和應用程式處理。如此一來,處理器只會在執行應用程式有事可做時進行運算。空閒期間處理器可以睡眠,並且節省電力。
在本文稍後,將有進一步關於調度演算法和系統時脈的討論。

思考 thread…

也許開始一個 RTOS 應用程式最好的方式,就是去思考如何將一個應用程式劃分為不同的 threads。例如,一個簡化的引擎輸入控制應用程式可以劃分為以下 threads:
  • 引擎溫度
  • 機油壓力
  • 每分鐘轉數 (RPM, rotation per minute)
  • 用戶輸入
這些模組可以被設置為 threads,也可以被劃分成子 threads。例如:
  • 引擎溫度
    • 讀取引擎溫度
    • 更新 LCD 的目前溫度
  • 機油壓力
    • 讀取目前的石油壓力
    • 展開應急發動機關機
  • 每分鐘轉速
    • 讀入 RPM
    • 更新 LCD 的目前 RPM
  • 用戶輸入
    • 油門踏板的角度
    • 獲取當前檔位
這種劃分成子 threads 的工作可以不斷繼續下去,直到可以被一個單獨的 thread 加以處理為止。

RTOS 的組件

讓我們來看看一個 RTOS 必須提供哪些功能,而這些功能又如何在不同的應用中派上用場。

Threads

 Threads 類似於函式,但每個 thread 都會有它自己的堆疊和 thread 控制塊(TCB)。然而與大多數函式不同的是,一個 thread 幾乎總是一個無限循環。也就是說,一旦它被創建,它(經常是)永遠不會退出。
1
2
3
4
5
6
void ThreadSendData( void )
{
  while (1) {
    // Send the data...
  }
}
一個 thread 總是處於幾種 states 其中之一。一個 thread 可以準備好被執行,也就是說,在 READY 狀態。或者該 thread 可能會被暫停(pending),也就是該 thread 在進入 READY 狀態之前,正在等待某事發生。這就是所謂的 WAITING state。
以下是我們對於 ThreadX 中 states 的一段簡短描述。
State說明
Executing這是當前正在運行的 thread。
Ready這個 thread 已經就緒
Suspended這個 thread 正在等待某件東西。這可能是一個事件或一個消息,也可能是等 RTOS 時鐘到達某個特定的值(延遲)。
Completed一個處於完成狀態的 thread 已經完成其運算處理、 並且自它的入口函數返回。(處於完成狀態的 thread 不會再次被執行。)
Terminated一個 thread 之所以會處於終止狀態,是因為另一個 thread 或是該 thread 本身呼叫了 tx_thread_terminate 服務。(處於終止狀態的 thread 不會再次被執行。)
  • :不同的 RTOS 可能會對這些 states 賦予不同的名稱*
Scheduler
你可以從兩種主要類型的 schedulers 中加以挑選:
  1. 事件驅動 (Event-driven) – 具優先權控制的調度演算法
    通常,不同的 threads 會有不同的響應要求。例如,在一個控制馬達、鍵盤和顯示器的應用程式中,馬達通常比鍵盤和顯示器需要更快的反應時間。這必須得靠一個事件驅動的 scheduler。
    在事件驅動的系統中,每個 thread 都會被分配到一個優先權,而優先權最高的 thread 就會被執行。執行的順序都仰賴於這個優先權。規則非常簡單:schedule 從所有就續的 threads 中挑出具備最高優先權的 thread 予以執行。
  2. 分時共享 (Time-sharing)
    最常見的分時演算法叫做 round-robin。也就是 scheduler 列出系統中所有 threads 的清單,然後一一查驗下一個 thread 是否就緒可以被執行。如果 thread 為 READY 時,該 thread 就會執行。每個 thread 又分派到一份 時間切片 (time-slice)。時間切片是每一回合中,單一 thread 被容許之最長的執行時間。
典型的基於優先權的 preemptive scheduler 會同時支援 preemptive 與 non-preemptive 的調度。在 preemptive 的情況下,較高優先權的 thread 會立即打斷(搶佔)執行中的低優先權 thread。而具備相同優先權的 threads 則會以 non-preemptive 的方式進行調度,而正在被執行的 thread 會繼續完成它的執行,再輪到另一個具備相同或較低優先權的 thread。
指派優先權 (Assigning priorities)
將正確的優先權指派給不同的 threads 是相當重要的。許多論文都在探討在基於 RTOS 的應用程式中,如何把這件工作做到完美。我們並不會深入這個題目,但是在此要談一些有用的規則:
  1. 盡量採用最少的優先權層級
    僅在搶佔是絕對必要的狀況下指派不同的優先權。這樣可以降低系統中 context switches 的數量,越少的 context switches,就表示有越多的時間是花費在執行應用程式代碼。
  2. 確認滿足了你應用程式中所有的時間關鍵約束條件
    端視你的應用程式類型而定,這可能會很難搞。有個解法是採用 RMA (Rate Monotonic Algorithm)。ThreadX RTOS 也提供了一個獨門絕活叫做 preemption-threshold。這能夠被用於降低 context switches,也能確保應用程式 threads 的執行。詳見 http://www.nxtbook.com/nxtbooks/cmp/esd0311/#/26
Internet 上有許多關於指派優先權的相關資訊。想要深入的朋友可以看下列兩則文章:
Thread 通訊 (Thread communications)
在 RTOS 應用程式中,我們也必須能夠在 threads 之間相互通訊。通訊可以採用 event、semaphore(旗號)的形式,或者是以訊息的方式傳送給另外一個 thread。
最基本的通訊是透過 event。一個中斷服務函式 (ISR) 也能夠傳送一個 event 給某個 thread。有些 RTOS 還能將單一 event 傳送給多個 threads。
Semaphores 通常被用於保護共享的資源,譬如說不只一個 thread 想要對同一塊記憶體(變數)進行讀寫時。作法是讓一個變數不會隨著另外一個作用中的 thread 而被改變。原則就是在你讀寫這塊記憶體之前,你必須先獲取一個以一個變數保護著的 semaphore。一旦你獲得這個 semaphore 之後,其他人都不能對這塊記憶體進行讀寫,直到你釋放 semaphore 為止。這樣一來你就可以確保同時只有一個 thread 會對該記憶體位置或變數進行讀寫。
訊息 (messages) 則能夠讓你將資料傳送給一個或多個 threads。這些訊息幾乎可以是任意的大小,通常以 mailbox 或 queue 的方式來實作。而 mailboxes 與 message queues 的行為會隨不同的 RTOS 而有所差異。
通盤整合 (Putting it all tegether)…
讓我們透過下面這張圖重新歸納我們至今所談過的東西。
那麼現在,讓我們看看我們能夠利用手上的組件做些什麼。試著回想一下,我們需要創建一個引擎控制的應用,而我們有一個微控制器和 LCD 顯示屏。我們還想在 LCD 顯示屏上顯示當前時間。我們將會重用早先將此應用程式分為不同的模組和 threads 的例子。(我們會忽略 “用戶輸入” 便於解說。)
針對這個練習,我們將創建下面的 threads:
  1. // Read the temperature and oil pressure of the engine
    void T_OilRead(void) and void T_TempRead(void)
  2. // Print the temperature and oil pressure on the LCD
    void T_OilLCD(void) and void T_TempLCD(void)
  3. // Controls the engine
    void T_EngineCTRL(void)
系統方塊圖如下所式:
其中我們以不同的顏色和外觀,分別表示了 threads、mailboxes、以及 semaphores 等元素。
關於這個系統,我們將需要一個 semaphore 來控制對 LCD 的寫入。因為我們有兩個不同的 threads,所以我們需要這個機制,如果其中一個 thread 被另一個打斷,輸出很有可能會被搞爛掉。
我們需要以下的信號傳導機制。
  • mailboxes M_TempLCD 與 M_OilLCD
    這些 mailboxes 包含要列印在 LCD 上的訊息。
  • mailboxes M_TempCTRL 和 M_OilCTRL
    這些 mailboxes 包含要送給引擎控制 thread 的訊息。
  • semaphore S_LCD
    這個 semaphore 將會確保在同一時刻當下,只有唯一的一個 thread 能夠列印到 LCD 上。
系統時脈 System ticks
我們現在有了一個系統,它可以更新 LCD上的馬達資訊、並顯示當前時間。然而,有一個非常重要的事情還沒搞定,就是 RTOS 的時脈功能。正如早些時候所言,kernel 需要隨時能夠掌控系統,然後才能夠執行例如根據 RTOS 的 scheduler 切換 threads 的工作。這通常是透過 MCU 內部計時器所驅動的時脈處理 API 呼叫達成。如果沒有系統時脈,整個系統就無法動彈。
連接到 MCU 的硬體
拼圖的最後一塊就是將這一切連接到硬體。在我們的例子中,我們將假設我們可以利用一套韌體函式庫。我們假設我們有以下的韌體 API 函式:
  • // Set text X,Y coordinate in characters
    void GLCD_TextSetPos(int X, int Y);
  • // Set draw window XY coordinate in pixels
    void GLCD_SetWindow(int X_Left, int Y_Up, int X_Right, int Y_Down);
  • // Function that reads the engine temperature
    char Engine_ReadTemp(void);
  • // Function that reads the engine oil pressure
    char Engine_ReadOilPressure(void);
  • // Function that controls the engine
    char Engine_Control(int CtrlValue);
現在,我們只要把這些片段兜在一起,看看我們的系統是否如同預期一般工作。最簡單的方法就是使用 RTOS 廠商的 BSP,如果有辦法取得的話。對於大多數 RTOS 而言,都會有為各種設備所設計的許多不同的 BSP。
如果是要在沒有 BSP 的情況下來建立一個應用,則需要:
  • 將編譯器/組譯器所需 include paths 加入到建構工具。
    我們需要確保編譯器和組譯器可以找到我們系統所需要的頭文件。
  • 添加共通的 RTOS 函式庫或源碼文件。
    我們需要添加 kernel。kernel 可能是源碼,也可能會是函式庫。
  • 添加 target-specific 的 RTOS 文件。
    很多 RTOS 對於不同的電路板/設備都會有一個電路板支援包(BSP, Board Support Package)。在這樣的一個情況下,你只需要確保你已經包含了這些 target-specific 的文件。在這樣的一個 BSP 環境下,你應該就會擁有已經配置好時鐘的程式碼,能夠正確配置 RTOS 的系統時脈。
  • 您的應用程式必須初始化 RTOS,並且在啟動 RTOS 之前就創建至少一個 thread。這通常是在 main() 函式中,但也可以在你應用程式的其他地方。
假設我們已經根據 RTOS 的規格添加了所有的文件,並且設置好建構選項,我們就可以開始創建我們應用程式中的 threads。但是在此之前,我們需要定義它們的 Thread Control Blocks(TCB)。
下面就是 Express Logic 之 ThreadX 的 TCB 定義範例。
1
2
3
4
5
6
// Express Logic ThreadX example
TX_THREAD TCBEngineCTRL;
TX_THREAD TCBOilLCD;
TX_THREAD TCBTempLCD;
TX_THREAD TCBOilRead;
TX_THREAD TCBTempRead;
現在,剩下的就是在我們創建的 threads 中建立我們的主要功能,以便完成我們所需要的應用程式。
在 ThreadX 中,我們會在 tx_application_define 這個 API 函式中創建我們最初的 threads。因此,在 main中我們唯一必須做的事情就是去呼叫 API 函式tx_kernel_enter。隨後 ThreadX 就會呼叫 tx_application_define(),我們在其中創建了我們的 threads、semaphores、message queues 之類一開始就會需要用到的東西。
大多數的 RTOS 都允許你在運行應用程式時才動態地創建資源,例如 threads 之類的。但是,我們至少需要有一個 thread 作為 schedular,用以接管我們的應用程式。
當做到這一步時,我們就可以啟動我們的 RTOS,並且讓 schedular 接手,以確保在正確的時間執行正確的 thread。

總結

這篇文章解釋了 kernel、threads、訊息(如 events 和 mailboxes)、scheduler、以及 RTOS 應用程式中主要元件的概念。一個真正的應用程式還會需要更多的細節,例如,如何創建 mailboxes 和 events。
如果你剛接觸 RTOS,上手最好的辦法就是從各家 RTOS 供應商的許多範例中,挑一個架起來並且用用看。在上述文章中,我已經用上了 Express Logic 的程式碼片段作為示範。Express Logic 提供了許多的應用範例,並有許多不同電路板和設備的 BSPs (Board Support Packages)。

RTOS系统简介

目录·RTOS系统的定义
·什么是RTOS
·RTOS系统的特点
·RTOS系统的分类
·RTOS系统的调度
·RTOS产生并得到迅速发展的原因

·RTOS系统的定义



  实时系统(Real-time operating system,RTOS)的正确性不仅依耐系统计算的逻辑结果,还依赖于产生这个结果的时间。实时系统能够在指定或者确定的时间内完成系统功能和外部或内部、同步或异步时间做出响应的系统。因此实时系统应该在事先先定义的时间范围内识别和处理离散事件的能力;系统能够处理和储存控制系统所需要的大量数据。

·什么是RTOS


  1.RTOS是一个内核
  典型的单片机程序在程序指针复位后,首先进行堆栈、中断、中断向量、定时器、串行口等接口设置、初始化数据存储区和显示内容,然后就来到了一个监测、等待或空循环,在这个循环中,CPU可以监视外设、响应中断或用户输入。
  这段主程序可以看作是一个内核,内核负责系统的初始化和开放、调度其它任务,相当于C语言中的主函数。
  RTOS就是这样的一个标准内核,包括了各种片上外设初始化和数据结构的格式化,不必、也不推荐用户再对硬件设备和资源进行直接操作,所有的硬件设置和资源访问都要通过RTOS核心。硬件这样屏蔽起来以后,用户不必清楚硬件系统的每一个细节就可以进行开发,这样就减少了开发前的学习量。
  一般来说,对硬件的直接访问越少,系统的可靠性越高。RTOS是一个经过测试的内核,与一般用户自行编写的主程序内核相比,更规范,效率和可靠性更高。对于一个精通单片机硬件系统和编程的“老手”而言,通过RTOS对系统进行管理可能不如直接访问更直观、自由度大,但是通过RTOS管理能够排除人为疏忽因素,提高软件可靠性。
  另外,高效率地进行多任务支持是RTOS设计从始至终的一条主线,采用RTOS管理系统可以统一协调各个任务,优化CPU时间和系统资源的分配,使之不空闲、不拥塞。针对某种具体应用,精细推敲的应用程序不采用RTOS可能比采用RTOS能达到更高的效率;但是对于大多数一般用户和新手而言,采用RTOS是可以提高资源利用率的,尤其是在片上资源不断增长、产品可靠性和进入市场时间更重要的今天。
  2.RTOS是一个平台
  RTOS建立在单片机硬件系统之上,用户的一切开发工作都进行于其上,因此它可以称作是一个平台。采用RTOS的用户不必花大量时间学习硬件,和直接开发相比起点更高。
  RTOS还是一个标准化的平台,它定义了每个应用任务和内核的接口,也促进了应用程序的标准化。应用程序标准化后便于软件的存档、交流、修改和扩展,为嵌入式软件开发的工程化创造了条件、减少开发管理工作量。嵌入式软件标准化推广到社会后,可以促进软件开发的分工,减少重复劳动,近来出现的建立于RTOS上的文件和通信协议库函数产品等就是实例。
  RTOS对于开发单位和开发者个人来说也是一种提高。引入RTOS的开发单位,相当于引入了一套行业中广泛采用的嵌入式系统应用程序开发标准,使开发管理更简易、有效。基于RTOS和C语言的开发,具有良好的可继承性,在应用程序、处理器升级以及更换处理器类型时,现存的软件大部分可以不经修改地移植过来。
  对于开发人员来说,则相当于在程序设计中采用一种标准化的思维方式,提高知识创造的效率;同时因为具有类似的思路,可以更快地理解同行其它人员的创造成果

·RTOS系统的特点



  一、时间约束性
  实时系统的任务具有一定的时间约束(截止时间)。根据截止时间,实时系统的实时性分为“硬实时”和“软实时”。硬实时是指应用的时间需求能够得到完全满足,否则就造成重大安全事故,甚至造成重大的生命财产损失和生态破坏,如在航空航天、军事、核工业等一些关键领域中的应用。软实时是指某些应用虽然提出时间需求,但实时任务偶尔违反这种需求对系统运行及环境不会造成严重影响,如监控系统等和信息采集系统等。
  二、可预测性
  可预测性是指系统能够对实时任务的执行时间进行判断,确定是否能够满足任务的时限要求。由于实时系统对时间约束要求的严格性,使可预测性称为实时系统的一项重要性能要求。除了要求硬件延迟的可预测性以外,还要求软件系统的可预测性,包括应用程序的响应时间是可预测的,即在有限的时间内完成必须的工作;以及操作系统的可预测性,即实时原语、调度函数等运行开销应是有界的,以保证应用程序执行时间的有界性。
  三、可靠性
  大多数实时系统要求有较高的可靠性。在一些重要的实时应用中,任何不可靠因素和计算机的一个微小故障,或某些特定强实时任务(又叫关键任务)超过时限,都可能引起难以预测的严重后果。为此,系统需要采用静态分析和保留资源的方法及冗余配置,使系统在最坏情况下都能正常工作或避免损失。可靠性已成为衡量实时系统性能不可缺少的重要指标。
  四、与外部环境的交互作用性
  实时系统通常运行在一定的环境下,外部环境是实时系统不可缺少的一个组成部分。计算机子系统一般是控制系统,它必须在规定的时间内对外部请求做出反应。外部物理环境往往是被控子系统,两者互相作用构成完整的实时系统。大多数控制子系统必须连续运转以保证子系统的正常工作或准备对任何异常行为采取行动。
  早期的实时系统功能简单,包括单板机、单片机,以及简单的嵌入式实时系统等,其调度过程相对简单。随着实时系统应用范围的不断扩大,系统复杂性不断提高,实时系统具有以下新特点。
  1、多任务类型
  在实时系统中,不但包括周期任务、偶发任务、非周期任务,还包括非实时任务。实时任务要求要满足时限,而非实时任务要求要使其响应时间尽可能的短。多种类型任务的混合,使系统的可调度性分析更加困难。
  2、约束的复杂性
  任务的约束包括时间约束、资源约束、执行顺序约束和性能约束。时间约束是任何实时系统都固有的约束。资源约束是指多个实时任务共享有限的资源时,必须按照一定的资源访问控制协议进行同步,以避免死锁和高优先级任务被低优先级任务堵塞的时间(即优先级倒置时间)不可预测。执行顺序约束是指各任务的启动和执行必须满足一定的时间和顺序约束。例如,在分布式端到端(end-to-end)实时系统很重,同一任务的各子任务之间存在前驱/后驱约束关系,需要执行同步协议来管理子任务的启动和控制子任务的执行,使它们满足时间约束和系统可调度要求。性能约束是指必须满足如可靠性、可用性、可预测性、服务质量(Quality of Service,QoS)等性能指标。
  3、具有短暂超载的特点
  在实时系统中,即使一个功能设计合理、资源充足的系统也可能由于一下原因超载:
  1)系统元件出现老化,外围设备错误或系统发生故障。随着系统运行时间的增长,系统元件出现老化,系统部件可能发生故障,导致系统可用资源降低,不能满足实时任务的时间约束要求。
  2)环境的动态变化。由于不能对未来的环境、系统状态进行正确有效地预测,因此不能从整体角度上对任务进行调度,可能导致系统超载。
  3)应用规模的扩大。原先满足实时任务时限要求的系统,随着应用规模的增大,可能出现不能满足任务时限要求的情况,而重新设计、重建系统在时间和经济上又不允许。

·RTOS系统的分类



  实时系统主要分为以下两类。
  强实时系统(Hard Real-Time):在航空航天、军事、核工业等一些关键领域中,应用时间需求应能够得到完全满足,否则就造成如飞机失事等重大地安全事故,造成重大地生命财产损失和生态破坏。因此,在这类系统的设计和实现过程中,应采用各种分析、模拟及形式化验证方法对系统进行严格的检验,以保证在各种情况下应用的时间需求和功能需求都能够得到满足。
  弱实时系统(Soft Real-Time):某些应用虽然提出了时间需求,但实时任务偶尔违反这种需求对系统的运行以及环境不会造成严重影响,如视频点播(Video-On-Demand,VOD)系统、信息采集与检索系统就是典型的弱实时系统。在VOD系统中,系统只需保证绝大多数情况下视频数据能够及时传输给用户即可,偶尔的数据传输延迟对用户不会造成很大影响,也不会造成像飞机失事一样严重的后果。

·RTOS系统的调度


  为了精确管理“时间”资源,已达到实时性和与预测性要求,并能够满足是实时系统的新要求,需用实时调度理论对任务进行调度和可调度性分析。任务调度技术包括调度策略和可调度性分析方法,两者是紧密结合的。任务调度技术研究的范围包括任务使用系统资源(包括处理机、内存、I/O、网络等资源)的策略和机制,以及提供判断系统性能是否可预测的方法和手段。例如,什么时候调度任务运行、在哪运行(当系统为多处理机系统或分布式系统时)、运行多长时间等等;以及判断分析用一定参数描述的实时任务能否被系统正确调度。
  给定一组实时任务和系统资源,确定每个任务何时何地执行的整个过程就是调度。在非实时系统中,调度的主要目的是缩短系统平均响应时间,提高系统资源利用率,或优化某一项指标;而实时系统中调度的目的则是要尽可能地保证每个任务满足他们的时间约束,及时对外部请求做出响应。实时调度技术通常有多种划分方法,常用以下两种。
  抢占式调度和非抢占式调度
  1)抢占式调度通常是优先级驱动的调度。每个任务都有优先级,任何时候具有最高优先级且已启动的任务先执行。一个正在执行的任务放弃处理器的条件为:自愿放弃处理器(等待资源或执行完毕);有高优先级任务启动,该高优先级任务将抢占其执行。除了共享资源的临界段之外,高优先级任务一旦准备就绪,可在任何时候抢占低优先级任务的执行。抢占式调度的优点是实时性好、反应快,调度算法相对简单,可优先保证高优先级任务的时间约束,其缺点是上下文切换多。而非抢占式调度是指不允许任务在执行期间被中断,任务一旦占用处理器就必须执行完毕或自愿放弃。其优点是上下文切换少;缺点是在一般情况下,处理器有效资源利用率低,可调度性不好。
  静态表驱动策略和优先级驱动策略
  2)静态表驱动策略(Static Table-Driven Scheduling)是一中离线调度策略,指在系统运行前根据各任务的时间约束及关联关系,采用某种 搜索策略生成一张运行时刻表。这张运行时刻表与列车运行时刻表类似,指明了各任务的起始运行时刻及运行时间。运行时刻表一旦生成就不再发生变化了。在系统运行时,调度器只需根据这张时刻表启动相应的任务即可。由于所有调度策略在离线情况下指定,因此调度器的功能被弱化,只具有分派器(Dispatcher)的功能。
  优先级驱动策略指按照任务优先级的高低确定任务的高低确定任务的执行顺序。优先级驱动策略又分为静态优先级调度策略。静态优先级调度是指任务的优先级分配好之后,在任务的运行过程中,优先级不会发生改变。静态优先级调度又称为固态优先级调度。动态优先级调度是指任务的优先级可以随着时间或系统状态的变化而发生变化。

·RTOS产生并得到迅速发展的原因



  单片机处理器能力的提高和应用程序功能的复杂化、精确化,迫使应用程序划分为多个重要性不同的任务,在各任务间优化地分配CPU时间和系统资源,同时还要保证实时性。靠用户自己编写一个实现上述功能的内核一般是不现实的,而这种需求又是普遍的。在这种形势之下,由专业人员编写的、满足大多数用户需要的高性能RTOS内核就是一种必然结果了。
  对程序实时性和可靠性要求的提高也是RTOS发展的一个原因。此外,单片机系统软件开发日趋工程化,产品进入市场时间不断缩短,也迫使管理人员寻找一种有利于程序继承性、标准化、多人并行开发的管理方式。从长远的意义上来讲,RTOS的推广能够带来嵌入式软件工业更有效、更专业化的分工,减少社会重复劳动、提高劳动生产率。

2014年8月14日星期四

キュー制御用API関数の使い方

データをタスク間で転送する場合に使うバッファがキュー(Queue)で、待ち行列の
構成となっています。
【キューの働き】
キューバッファつまり待ち合わせ行列は下図のような動作をさせるときに使います。
例えば、タスクが3個あり、Task3が液晶表示器に表示出力するタスクとします。
これに対し、Task1とTask2の2つのタスクから表示データを送って表示要求をするもの
とします。Task1とTask2は全く非同期で表示出力するものとします。
 Task3は表示要求が来たらすぐ表示をしますが、2つのタスクからの表示要求が
重なったら同時には動作できませんから、どちらかを優先して後の要求を待たせる
必要があります。この「待たせる」という動作をさせるためにキューバッファを使います。

 つまり、すべての表示要求は、いったんキューバッファに蓄え、早いものから順に
取り出しては表示するようにします。(FIFO(First-In First-Out)動作)
こうすれば、同時に表示要求が来ても待たせることができますし、表示する動作に
時間がかかる場合でも表示終了までバッファに貯めておくことで、ゆっくり処理すること
ができます。


 FreeRTOSのキューバッファ動作は、データそのものをキューバッファにコピーします
ので、取り出す場合にもデータ全部を取り出せる一時バッファが必要となります。
この場合の出し入れするデータサイズは、キューを確保するときのパラメータとして
固定長として与えます。

【キュー制御用関数】
キューを制御する基本のAPI関数には下記が用意されています。

関数名機能と書式
xQueueCreateキューバッファの生成
《書式》
  xQueueHandle xQueueCreate(unsigned portBASE_TYPE uxQueueLength,
               unsigned portBASE_TYPE uxItemSize);
   uxQueueLength :格納する最大アイテム数
   uxItemSize   :アイテムのバイト数(固定長)
   戻り値     :確保できたらハンドル値 不可なら0
《使用例》
   lcdQueue = cQueueCreate(5, 17); //17バイト幅で5個分のキュー
xQueueSendキューに送信(キューの最後尾に書き込み) xQueueSendToBackと同じ
割り込み処理内では使用禁止
《書式》
  portBASE_TYPE xQueueSend(xQueueHandle xQueue,
         const void *pvItemToQueue,
         portTickType xTicksToWait);
   xQueue    :キューのハンドル値
   pvItemToQueue :キューへ送るデータへのポインタ
   xTicksWait  :キュー空きを待つ最大時間
           portMAX_DELAYとすると永久待ちとなる
   戻り値    :成功でpdTRUE 失敗でerrQUEUE_FULL
《使用例》
   Msg[] = "Display Data xxxx";
   xQueueSend(lcdQueue, Msg, portMAX_DELAY);
xQueueSendToBackキューに送信(キューの最後尾に書き込み) xQueueSendと同じで
書式もxQueueSendと同じ。割り込み処理内では使用禁止
xQueueSendToFrontキューに送信(キューの最前部に書き込み)
書式はxQueueSendToBackと同じ。割り込み処理内では使用禁止
xQueueReceiveキューからデータを取り出す。取り出したデータを格納するアイテムサイズと同じ長さのバッファを用意する必要がある
割り込み処理内では使用禁止
《書式》
  portBASE_TYPE xQueueReceive(xQueueHandle xQueue, void 
*pvBuffer,
                portTicksType portTicksToWait);
    xQueue     :キューのハンドル値
    pvBuffer    :取り出し先のバッファのポインタ
    portTicksToWait:キューが空のときデータ送信を待つ最大時間、
             portMAX_DELAYとすると永久待ち
    戻り値     :正常ならpdTRUE 失敗ならpdFALSE
《使用例》
   unsigned char Buf[20];
   xQueueReceive(lcdQueue, Buf, portMAX_DELAY);
xQueuePeekキューからデータを取り出す。キューのデータはそのまま残す
取り出したデータを格納するバッファが必要
割り込み処理内では使用禁止
《書式》
   portBASE_TYPE xQueuePeek(xQueueHandle xQueue, void *pvBuffer,
                portTicType xTicksToWait);
    xQueue   :キューのハンドル値
    pvBuffer  :取り出し先のバッファのポインタ
    xTicksToWait:キューが空のときデータ送信を待つ最大時間、
             portMAX_DELAYとすると永久待ち
    戻り値   :正常ならpdTRUE 失敗ならpdFALSE
xQueueSendFromISR割り込み処理ルーチン内で使うxQueueSend
データをキューにコピーするので、データサイズは小さくすべき
《書式》
   portBASE_TYPE xQueueSendFromISR(xQueueHandle xQueue,
          const void *pvItemToQueue,
          portBASE_TYPE *pxHigherPriorityTaskjWoken);
    xQueue    :キューのハンドル値
    pvItemToQueue :キューへ送るデータへのポインタ
    pxHigherPriorityTaskWoken:pdTRUEへのポインタとすると
      キューを送った先のタスクのブロックを解除し優先レベル
      を現在実行タスクより高くする
      pdFALSEへのポインタとした場合には割り込み処理の最後で
      portSWITCH_CONTEXT()を実行してコンテキストの切り替えを
      する必要がある
    戻り値    :正常ならpdTRUE 、失敗ならpdFALSE
xQueueSendToBackFromISR割り込み処理ルーチン内で使うxQueueSendToBackで
書式はxQueueSendFromISRと同じ
xQueueSendToFrontFromISR割り込み処理ルーチン内で使うxQueueSendToFrontで
書式はxQueueSendFromISRと同じ
xQueueReceiveFromISR割り込み処理ルーチン内で使うxQueueReceive
《書式》
   portBASE_TYPE xQueueReceiveFromISR(xQueueHandle xQueue,
         void *pvBuffer, portBASE_TYPE *pxTaskWoken);
     xQueue   :キューのハンドル値
     pvBuffer  :取り出し先のバッファのポインタ
     pxTaskWoken :pdTRUEへのポインタとすると、キューが空で
            待っていたタスクのブロックを解除する。
            pdFALSEへのポインタとすると何もしない
xQueueAddToRegistryデバッグ用にキューに名前を付け、レジストリにキューを追加する
《書式》
   void vQueueAddToRegistry(xQueueHandle xQueue,
                signed portCHAR *pcQueueName);
     xQueue   :キューのハンドル値
     pcQueueName :キューに付ける名称へのポインタ
《使用例》
   xQueue = xQueueCreate(10, sizeof(portCHAR));
   vQueueAddToRegistry(xQueue, "MeaningfulName");
vQueueUnregisterQueueデバッグ用に追加したキューのレジストリを削除する
《書式》
   void vQueueUnregisterQueue(xQueueHandle xQueue);
      xQueue :キューのハンドル値
《使用例》
   vQueueUnregisterQueue(xQueue);
   vQueueDelete(xQueue);

【キュー制御用補助関数】

マクロ名機能と書式
uxQueueMessagesWaitingキューに格納されているアイテム数を返す
《書式》
   unsigned portBASE_TYPE uxQueueMessagesWaiting(
                 xQueueHandle xQueue);
    xQueue:キューのハンドル値
    戻り値:キュー内のアイテム数
xQueueDeleteキューを削除する。キューの使用しているメモリを開放する
《書式》
   void vQueueDelete(xQueueHandle xQueue);
     xQueue:キューのハンドル値
【キューバッファの使用例】
例題として最初の3つのタスクで液晶表示器に表示を出す例題を試してみます。
まず、メイン関数部で、キューバッファと、タスクの生成をします。
例題は下記リストのように、宣言部でキューのハンドル用変数の宣言定義とタスク関数
のプロトタイプをしてから、main関数の中でキューバッファの生成(xQueueCreate)と
タスクの生成(xTaskCreate)を実行し、スケジュールを開始しています。
キューの生成を行うとキューの名前がハンドル変数(lcdQueue)に返されますので、
以降はこのキューハンドルを使ってキューを指定します。
キューの大きさは液晶表示器の1行分を1回に送るデータとすることとしますので、17バイト
を単位とし、それを5個まで蓄えられるサイズとしています。



 次にタスク関数部は下記リストのようにして、キューを使います。
まずタスク1とタスク2ではキューにデータを送り(xQueueSendToBack)
タスク3ではキューからデータを取り出して(xQueueReceive)います。
送る側では、16文字の液晶表示器への表示文字を用意し、それを一定時間
間隔で送っています。表示データの中味にカウンタデータを含め、毎回データ
が異なるようにしています。
キューが一杯で送れない場合にはバッファが空くのを永久に待つものとしています。

キューを取り出す方では、一時バッファとしてBufを用意し、これに毎回取り出しては
表示出力します。キューが空の場合には、キューにデータが送られるまで永久に待つ
ものとしています。
さらに取り出したデータの内容をチェックして、タスク1からのデータの場合には1行目
に表示し、タスク2からの場合には2行目に表示するようにしています。



このプログラムの実行結果では、液晶表示器に下図のように表示されます。
数値が高速で更新されていき、2行目の数値は1行目の半分になっています。



★★★ この例題のプロジェクトファイル一式 (sample3)

2014年8月13日星期三

特斯拉的成功秘诀:愿景、勤奋和创业环境(图)

  埃隆•马斯克正在一条新路上驾驶特斯拉狂飙,拿着望远镜几乎都看不到后来者,而宝马、福特等则在另一条路上艰难行进,偶尔看一眼这边想拐又犹豫,但马斯克坚定前行,并吸引了万众目光,每个人都想拥有特斯拉,包括我。

  马斯克的“驾照”

  我第一次听说特斯拉这家公司,还是通过一份股市报告,当时它的股价有了大幅攀升,但我没有买特斯拉的股票,我觉得它可能是被炒作出来的,于是我开始关注这家公司,通过研究,我对特斯拉公司及马斯克有了新的认识。

  埃隆•马斯克说过喜欢高强度的工作,他喜欢克服各种艰巨的挑战从而改变人类的命运,让全人类享有更美好的生活,这是一项伟大的事业,他热爱它并为之投入所有激情。

  听其言观其行,我觉得埃隆•马斯克和特斯拉能够获得如此巨大成功主要有以下几个因素:

  首先,埃隆•马斯克追逐的目标不是金钱,因为他已经很富有,并且还向社会捐献了大量财富,他心忧天下,认为如果人类继续不断的从地球攫取资源,人类将会遭受灭顶之灾,所以他一直在做SpaceX空间探索技术公司,后来,做特斯拉汽车,因为这样的汽车对环境有益,能造福人类。这样的动因和愿景,对一个企业的成功非常重要,中国有句古话,“取法乎上,仅得其中;取法乎中,仅得其下;取法乎下,无所得。”所以说要想建立一家伟大的企业,就必须要有一个伟大的愿景,埃隆•马斯克和他的公司,拥有十分清晰的愿景。

  其次,埃隆•马斯克是一个自信且坚韧的人,当埃隆•马斯克旗下公司SpaceX的“龙”太空舱成功与国际空间站对接并返回地球时,我读到一篇文章,文章讲述,在他成功之前,他也失败过很多次,但是他坚信他决不放弃,直到成功!他说,如果你没有失败,那说明你做的事情还不够创新,所有这一切都清晰地诠释了他坚韧不拔的性格,我认为这就是促使他成为一个非常成功的企业家的根本原因,因为他从不放弃,因为他坚信自己,坚信初衷。

  再次,埃隆•马斯克拥有高明的商业智慧,这从其开放专利的决定中就可以看出来,通过将专利分享给竞争对手,最终将使电动汽车行业受益,他没有将其他电动汽车的生产厂商视作竞争对手,而是将燃油汽车厂商视为竞争对手,我绝对赞成他的观点,我也会做同样的事情,如果市场还不成熟,或者还不够大,即使拥有最好的技术,企业都很难成功,但是,如果市场足够大,那么将会出现多个赢家。特斯拉通过分享技术,将使整个电动汽车行业市场扩大,很大程度上,特斯拉也将在新崛起的电动汽车产业保持领先地位。

  此外,勤奋是必不可少的,他说他每周工作100个小时,这是一个典型的工作狂,也是成功企业家的一个典型特质,成功没有秘密,惟有坚持不懈地努力工作。

  我认为,以上几个方面是决定一家初创企业成功的主要因素,埃隆•马斯克全部拥有并把它们做到了极致,这就是他能够出色驾驶特斯拉的“驾照”。

  在哪驾驶很重要

  一个企业家的成功,其实是受到所在国各种条件所限制的,作为一个企业家,选择进入一个正确的市场或国家创业,是非常重要的。

  美国的创业环境也是埃隆•马斯克得以成功的关键,他说过,美国是创新阻力最小的国家,当然他想表达的是,美国的创新环境本来可以更好,但是,现在美国依然是全世界最适合创新的地方。很多时候,一些伟大的抱负,诸如私人研发登陆火星的新型火箭等等不可思议的想法,通常都能够顺利实施,这一定程度上缘自美国的经济规模和各种软硬基础设施,在美国,市场非常大,有完善的法律和充足的资本,因此,在美国进行这些看似异想天开的项目更容易并有望获得成功。

  还有一点也不能忽略,制造火箭等项目是个庞大的工程,此前基本是各个有能力的大国几乎举全国之力来实施,而且经常失败,私人企业来操作这样的项目难度可想而知,这需要各种合作,而这正是美国商业环境中所拥有的,我在《硅谷式创业的成功基因》中就写过,硅谷很多创业公司的成功都得益于互利互惠的合作精神,只要你的产品独特、有价值,大公司都会愿意跟小公司合作,而这对于初创公司是非常关键的,特别是那些家底薄又目标远大的公司。

  此外,美国还有得天独厚的人才优势和良好的用人环境,美国拥有全世界最多的顶尖大学,培养各种优秀人才,与此同时它还像磁石一样吸引各国的高级人才,所以你想要实现一个伟大的理想,很容易找到一些志同道合并技艺超群的人,而且他们也愿意来初创公司工作,而不是执着于进入政府部门或大型企业,美国的优秀的人才非常乐意加入创业公司,一些有能力的、有想法的毕业生对创业公司怀有激情,他们认为这些公司更能激发自己的创造力,同时成功的回报也异常丰厚,而美国社会更是赞赏这种冒险精神。

  天时地利人和将马斯克推到了成功的风口,他有理由继续狂飙,整个汽车行业将因此重塑。每个时代以及每个行业都需要这些特立独行的人去变革,从而加速人类的进步、增进人类的福祉,那些因面对变化无动于衷而被淘汰的公司不值得怜惜,沉舟侧畔千帆过,已经有更优秀者勇立潮头,提供更优秀的产品和服务。
 

2014年8月12日星期二

c语言中返回指向临时变量的指针的探讨

今天在CSDN的一个Blog上看到了一篇讲C/C++中常见内存错误的文章,里面讲到了 返回指向临时变量的指针会导致不可预料的后果,原文如下:
10.返回指向临时变量的指针
大家都知道,栈里面的变量都是临时的。当前函数执行完成时,相关的临时变量和参数都被清除了。不能把指向这些临时变量的指针返回给调用者,这样的指针指向的数据是随机的,会给程序造成不可预料的后果。
下面是个错误的例子:

/*********** 1 *****************/
char* get_str(void) 

    char str[] = {"abcd"}; 
    return str; 

int main(int argc, char* argv[]) 

    char* p = get_str(); 
    printf("%s/n", p); 
    return 0; 
}
下面这个例子没有问题,大家知道为什么吗?
/*********** 2 *****************/

char* get_str(void) 

    char* str = {"abcd"}; 
    return str; 

int main(int argc, char* argv[]) 

    char* p = get_str(); 
    printf("%s/n", p); 
    return 0; 

}

我在VC++6.0上一试,果然前者有问题,后者没有问题。于是就在想为什么会不一样,看了反汇编代码有请教了一位朋友,才明白了其中的原因,相关语句的反汇编代码如下:
7:        char *str = {"abcd"};
0040D798   mov         dword ptr [ebp-4],offset string "abcd" (00422fa4)
11:       return str;
0040D79F   mov         eax,dword ptr [ebp-4]
7:        char str[] = {"abcd"};
0040D798   mov         eax,[string "abcd" (00422fa4)]
0040D79D   mov         dword ptr [ebp-8],eax
_max_test:
0040D7A0   mov         cl,byte ptr [string "abcd"+4 (00422fa8)]
0040D7A6   mov         byte ptr [ebp-4],cl
11:       return str;
0040D7A9   lea         eax,[ebp-8]
"abcd"是编译时放在代码段中的一个无名常量,当程序载入内存的时候是放在另一段内存中的,而非堆栈所在的内存中。从汇编代码中也可以看出,使用char *str这种方式时,程序是把"abcd"所在的内存地址先放到了堆栈中,然后再放入eax作为返回值的,也就是说eax中的地址指向的是"abcd"所在的内存。而用char str[]这种方式是,是先把原来的"abcd"复制到堆栈中的,然后再把存储"abcd"的堆栈的地址放入eax的,也就是说eax中的地址指向的是堆栈中"abcd",而不是原来的那个"abcd",由于函数使用的堆栈中的内存在函数退出后就会被释放,所以作为返回值的指向堆栈的指针就有可能会造成内存错误。后来我又实验了动态分配内存,由于动态分配的内存是在堆上的,所以不会造成上述的内存错误。




C语言 临时变量不能作为函数的返回值?

留看,不大明白

转自:http://blog.sina.com.cn/s/blog_6c04852d0101oznf.html

这个问题一直纠结啊,不明白其中的原理,总是记不住,那些临时变量是不能作为返回值的?
上网查了一下,结合经验陈述如下。
原理:
首先需要明白一件事情,临时变量,在函数调用过程中是被压到程序进程的栈中的,当函数退出时,临时变量出栈,即临时变量已经被销毁,临时变量占用的内存空间没有被清空,但是已经可以被分配给其他变量了,所以有可能在函数退出时,该内存已经被修改了,对于临时变量来说已经是没有意义的值了

C语言里规定:16bit程序中,返回值保存在ax寄存器中,32bit程序中,返回值保持在eax寄存器中,如果是64bit返回值,edx寄存器保存高32bit,eax寄存器保存低32bit。

由此可见,函数调用结束后,返回值被临时存储到寄存器中,并没有放到堆或栈中,也就是说与内存没有关系了。当退出函数的时候,临时变量可能被销毁,但是返回值却被放到寄存器中与临时变量的生命周期没有关系。
如果我们需要返回值,一般使用赋值语句就可以了
A a = func();
综上,函数是可以将临时变量的值作为返回值的。

是将一个指向局部变量的指针作为函数的返回值是有问题的。
由于指针指向局部变量,因此在函数返回时,临时变量被销毁,指针指向一块无意义的地址空间,所以一般不会有返回值。
如果得到正常的值,只能是幸运的,因为退出函数的时候,系统只是修改了栈顶的指针,并没有清内存;所以,是有可能正常访问到局部变量的内存的。但因为栈是系统自动管理的,所以该内存可能会可以被分配给其他函数,这样,该内存的内容就会被覆盖,不再是原来的值了。

常规程序中,函数返回的指针(函数指针,数组指针,结构体指针,联合体指针等)通常应该是:
(1)指向静态(static)变量;
(2)指向专门申请分配的(如用malloc)空间;
(3)指向常量区(如指向字符串"hello");
(4)指向全局变量;
(5)指向程序代码区(如指向函数的指针)。
除这5项以外,其它怪技巧不提倡。
函数内的变量,没有关键字static修饰的变量的生命周期只在本函数内,函数结束后变量自动销毁。当返回为指针的时候需要特别注意,因为函数结束后指针所指向的地址依然存在,但是该地址可以被其他程序修改,里面的内容就不确定了,有可能后面的操作会继续用到这块地址,有可能不会用到,所以会出现时对时错的情况,如果需要返回一个指针而又不出错的话只能调用内存申请函数

对于结构体和联合体来说,在作为函数的参数和返回值时,表现与C语言的内置类型(int,float, char等)是一样的,当为临时变量的时候,作为返回值时有效的。这个也是与指针不同的地方,所以一定要区分好,总是为当返回结构体或者联合体的时候,该怎么处理,原来直接返回就可以了。(不要与结构体指针或者联合体指针弄混了,他们是指针,不是自定义数据类型^_^)

2014年8月11日星期一

跳出来看FreeRTOS

题目自命题

转自:http://www.programmer-club.com.tw/showSameTitleN/embedded/73.html


雖然嵌入式系統與RTOS有密切的關係,但是在選用前,最好還是釐清一些問題。
1. 你真的需要RTOS嗎?(如果你只需要簡單的應用,RTOS反而會拖慢你的系統)
2. 你的硬體負擔的了RTOS嗎?(RTOS雖然都標榜很小的CODE SIZE、但他所耗用的RAM(Task Stack, Task Structures...),或CPU資源(Context switch損耗,Scheduling 損耗...)等部分,卻著墨很少)
3. RTOS增加的成本有計算過嗎?(即使是免費的,仍有潛在的成本)
4. 你絕對需要他的原因?
如果要我用一句話來回答第四個問題,我會答:程式比較容易寫(也表示開發上會比較快)。我的回答其實是希望淡化:"RTOS是一定要的啦!"的立場!
沒有硬體TIMER的中斷支援,RTOS根本無法做到Preemption,另外,RTOS提供的不同優先權(Priority)的功能,現階段的CPU多有支援,另外,一些SOC開發的特殊功能(例如省電模式、動態調頻等),通常能提供更有效率的使用,只是,在RTOS的加持下,這些功能可能會被隱形、或放棄掉!
如果你的系統很複雜,你真的不用考慮太多,用(RTOS)就對了!如果經驗不是很豐富,又不用考慮成本,用就對了!如果你的人手不足,許多模組要用買的,用就對了!如果沒有RTOS你不會寫程式了,用就對了!
RTOS會耗掉多少資源?(就像我問你,Win-XP能不能在486的電腦上跑一樣?486太弱了嗎...請再想想)一個作業系統的介入能提昇效能?非也非也!RTOS跟我有仇?非也非也!我只是站在反方提出一些看法罷了!

2014年8月8日星期五

堆(heap)和栈(stack)有什么区别?

简单的可以理解为:
heap:是由malloc之类函数分配的空间所在地。地址是由低向高增长的。
stack:是自动分配变量,以及函数调用的时候所使用的一些空间。地址是由高向低减少的。


预备知识—程序的内存分配

一个由c/C++编译的程序占用的内存分为以下几个部分
1、栈区(stack)— 由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。
2、堆区(heap) — 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收 。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表,呵呵。
3、全局区(静态区)(static)—,全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。 - 程序结束后有系统释放
4、文字常量区 —常量字符串就是放在这里的。 程序结束后由系统释放
5、程序代码区—存放函数体的二进制代码。

二、例子程序
这是一个前辈写的,非常详细
//main.cpp
int a = 0; 全局初始化区
char *p1; 全局未初始化区
main()
{
int b; 栈
char s[] = "abc"; 栈
char *p2; 栈
char *p3 = "123456"; 123456在常量区,p3在栈上。
static int c =0; 全局(静态)初始化区
p1 = (char *)malloc(10);
p2 = (char *)malloc(20);
分配得来得10和20字节的区域就在堆区。
strcpy(p1, "123456"); 123456放在常量区,编译器可能会将它与p3所指向的"123456"优化成一个地方。
}


二、堆和栈的理论知识
2.1申请方式
stack:
由系统自动分配。 例如,声明在函数中一个局部变量 int b; 系统自动在栈中为b开辟空间
heap:
需要程序员自己申请,并指明大小,在c中malloc函数
如p1 = (char *)malloc(10);
在C++中用new运算符
如p2 = (char *)malloc(10);
但是注意p1、p2本身是在栈中的。
2.2
申请后系统的响应
栈:只要栈的剩余空间大于所申请空间,系统将为程序提供内存,否则将报异常提示栈溢出。
堆:首先应该知道操作系统有一个记录空闲内存地址的链表,当系统收到程序的申请时,
会遍历该链表,寻找第一个空间大于所申请空间的堆结点,然后将该结点从空闲结点链表中删除,并将该结点的空间分配给程序,另外,对于大多数系统,会在这块内存空间中的首地址处记录本次分配的大小,这样,代码中的delete语句才能正确的释放本内存空间。另外,由于找到的堆结点的大小不一定正好等于申请的大小,系统会自动的将多余的那部分重新放入空闲链表中。
2.3申请大小的限制
栈:在Windows下,栈是向低地址扩展的数据结构,是一块连续的内存的区域。这句话的意思是栈顶的地址和栈的最大容量是系统预先规定好的,在 WINDOWS下,栈的大小是2M(也有的说是1M,总之是一个编译时就确定的常数),如果申请的空间超过栈的剩余空间时,将提示overflow。因此,能从栈获得的空间较小。
堆:堆是向高地址扩展的数据结构,是不连续的内存区域。这是由于系统是用链表来存储的空闲内存地址的,自然是不连续的,而链表的遍历方向是由低地址向高地址。堆的大小受限于计算机系统中有效的虚拟内存。由此可见,堆获得的空间比较灵活,也比较大。
2.4申请效率的比较:
栈由系统自动分配,速度较快。但程序员是无法控制的。
堆是由new分配的内存,一般速度比较慢,而且容易产生内存碎片,不过用起来最方便.
另外,在WINDOWS下,最好的方式是用VirtualAlloc分配内存,他不是在堆,也不是在栈是直接在进程的地址空间中保留一快内存,虽然用起来最不方便。但是速度, 也最灵活
2.5堆和栈中的存储内容
栈: 在函数调用时,第一个进栈的是主函数中后的下一条指令(函数调用语句的下一条可执行语句)的地址,然后是函数的各个参数,在大多数的C编译器中,参数是由右往左入栈的,然后是函数中的局部变量。注意静态变量是不入栈的。
当本次函数调用结束后,局部变量先出栈,然后是参数,最后栈顶指针指向最开始存的地址,也就是主函数中的下一条指令,程序由该点继续运行。
堆:一般是在堆的头部用一个字节存放堆的大小。堆中的具体内容有程序员安排。
2.6存取效率的比较

char s1[] = "aaaaaaaaaaaaaaa";
char *s2 = "bbbbbbbbbbbbbbbbb";
aaaaaaaaaaa是在运行时刻赋值的;
而bbbbbbbbbbb是在编译时就确定的;
但是,在以后的存取中,在栈上的数组比指针所指向的字符串(例如堆)快。
比如:
#include
void main()
{
char a = 1;
char c[] = "1234567890";
char *p ="1234567890";
a = c[1];
a = p[1];
return;
}
对应的汇编代码
10: a = c[1];
00401067 8A 4D F1 mov cl,byte ptr [ebp-0Fh]
0040106A 88 4D FC mov byte ptr [ebp-4],cl
11: a = p[1];
0040106D 8B 55 EC mov edx,dword ptr [ebp-14h]
00401070 8A 42 01 mov al,byte ptr [edx+1]
00401073 88 45 FC mov byte ptr [ebp-4],al
第一种在读取时直接就把字符串中的元素读到寄存器cl中,而第二种则要先把指edx中,在根据edx读取字符,显然慢了。
?

2.7小结:
堆和栈的区别可以用如下的比喻来看出:
使用栈就象我们去饭馆里吃饭,只管点菜(发出申请)、付钱、和吃(使用),吃饱了就走,不必理会切菜、洗菜等准备工作和洗碗、刷锅等扫尾工作,他的好处是快捷,但是自由度小。
使用堆就象是自己动手做喜欢吃的菜肴,比较麻烦,但是比较符合自己的口味,而且自由度大。

堆和栈的区别主要分:
操作系统方面的堆和栈,如上面说的那些,不多说了。
还有就是数据结构方面的堆和栈,这些都是不同的概念。这里的堆实际上指的就是(满足堆性质的)优先队列的一种数据结构,第1个元素有最高的优先权;栈实际上就是满足先进后出的性质的数学或数据结构。
虽然堆栈,堆栈的说法是连起来叫,但是他们还是有很大区别的,连着叫只是由于历史的原因针值读.

2014年8月7日星期四

关于typedef的用法总结



转自:http://www.cnblogs.com/csyisong/archive/2009/01/09/1372363.html




不管实在C还是C++代码中,typedef这个词都不少见,当然出现频率较高的还是在C代码中。typedef与#define有些相似,但更多的是不同,特别是在一些复杂的用法上,就完全不同了,看了网上一些C/C++的学习者的博客,其中有一篇关于typedef的总结还是很不错,由于总结的很好,我就不加修改的引用过来了,以下是引用的内容(红色部分是我自己写的内容)。


用途一:

定义一种类型的别名,而不只是简单的宏替换。可以用作同时声明指针型的多个对象。比如:

char* pa, pb; // 这多数不符合我们的意图,它只声明了一个指向字符变量的指针,

// 和一个字符变量;

以下则可行:

typedef char* PCHAR;

PCHAR pa, pb;
这种用法很有用,特别是char* pa, pb的定义,初学者往往认为是定义了两个字符型指针,其实不是,而用typedef char* PCHAR就不会出现这样的问题,减少了错误的发生。


用途二:
用在旧的C代码中,帮助struct。以前的代码中,声明struct新对象时,必须要带上struct,即形式为: struct 结构名对象名,如:

struct tagPOINT1

{
int x;

int y;
};

struct tagPOINT1 p1;

而在C++中,则可以直接写:结构名对象名,即:tagPOINT1 p1;

typedef struct tagPOINT
{
int x;

int y;
}POINT;

POINT p1; // 这样就比原来的方式少写了一个struct,比较省事,尤其在大量使用的时

候,或许,在C++中,typedef的这种用途二不是很大,但是理解了它,对掌握以前的旧代

码还是有帮助的,毕竟我们在项目中有可能会遇到较早些年代遗留下来的代码。

用途三:

用typedef来定义与平台无关的类型。

比如定义一个叫 REAL 的浮点类型,在目标平台一上,让它表示最高精度的类型为:

typedef long double REAL;

在不支持 long double 的平台二上,改为:

typedef double REAL;

在连 double 都不支持的平台三上,改为:

typedef float REAL;

也就是说,当跨平台时,只要改下 typedef 本身就行,不用对其他源码做任何修改。

标准库就广泛使用了这个技巧,比如size_t。另外,因为typedef是定义了一种类型的新别名,不是简单的字符串替换,所以它比宏来得稳健。
     这个优点在我们写代码的过程中可以减少不少代码量哦!


用途四:

为复杂的声明定义一个新的简单的别名。方法是:在原来的声明里逐步用别名替换一部

分复杂声明,如此循环,把带变量名的部分留到最后替换,得到的就是原声明的最简化
版。举例: 
 原声明:void (*b[10]) (void (*)());
变量名为b,先替换右边部分括号里的,pFunParam为别名一:
typedef void (*pFunParam)();
再替换左边的变量b,pFunx为别名二:
typedef void (*pFunx)(pFunParam);
原声明的最简化版:
pFunx b[10];
 
原声明:doube(*)() (*e)[9];
变量名为e,先替换左边部分,pFuny为别名一:
typedef double(*pFuny)();
再替换右边的变量e,pFunParamy为别名二
typedef pFuny (*pFunParamy)[9];
原声明的最简化版:
pFunParamy e;
理解复杂声明可用的“右左法则”:从变量名看起,先往右,再往左,碰到一个圆括号
就调转阅读的方向;括号内分析完就跳出括号,还是按先右后左的顺序,如此循环,直
到整个声明分析完。举例:
int (*func)(int *p);
首先找到变量名func,外面有一对圆括号,而且左边是一个*号,这说明func是一个指针
;然后跳出这个圆括号,先看右边,又遇到圆括号,这说明(*func)是一个函数,所以
func是一个指向这类函数的指针,即函数指针,这类函数具有int*类型的形参,返回值
类型是int。
int (*func[5])(int *);
func右边是一个[]运算符,说明func是具有5个元素的数组;func的左边有一个*,说明
func的元素是指针(注意这里的*不是修饰func,而是修饰func[5]的,原因是[]运算符
优先级比*高,func先跟[]结合)。跳出这个括号,看右边,又遇到圆括号,说明func数
组的元素是函数类型的指针,它指向的函数具有int*类型的形参,返回值类型为int。
这种用法是比较复杂的,出现的频率也不少,往往在看到这样的用法却不能理解,相信以上的解释能有所帮助。
*****以上为参考部分,以下为本人领悟部分*****
使用示例:
1.比较一:
#include
using namespace std;
typedef int (*A) (char, char);
int ss(char a, char b)
{
    cout<<"功能1"<
    cout<
    cout<
    return 0;
}
 
int bb(char a, char b)
{
    cout<<"功能2"<
    cout<
    cout<
    return 0;
}
void main()
{
    A a;
    a = ss;
    a('a','b');
    a = bb;
    a('a', 'b');
}
2.比较二:
typedef int (A) (char, char);
void main()
{
    A *a;
    a = ss;
    a('a','b');
    a = bb;
    a('a','b');
}
 
两个程序的结果都一样:
功能1
a
b
功能2
b
a

*****以下是参考部分*****
参考自:http://blog.hc360.com/portal/personShowArticle.do?articleId=57527
typedef 与 #define的区别:
案例一:
通常讲,typedef要比#define要好,特别是在有指针的场合。请看例子:
typedef char *pStr1;
#define pStr2 char *;
pStr1 s1, s2;
pStr2 s3, s4;
在上述的变量定义中,s1、s2、s3都被定义为char *,而s4则定义成了char,不是我们
所预期的指针变量,根本原因就在于#define只是简单的字符串替换而typedef则是为一
个类型起新名字。
案例二:
下面的代码中编译器会报一个错误,你知道是哪个语句错了吗?
typedef char * pStr;
char string[4] = "abc";
const char *p1 = string;
const pStr p2 = string;
p1++;
p2++;
  是p2++出错了。这个问题再一次提醒我们:typedef和#define不同,它不是简单的
文本替换。上述代码中const pStr p2并不等于const char * p2。const pStr p2和
const long x本质上没有区别,都是对变量进行只读限制,只不过此处变量p2的数据类
型是我们自己定义的而不是系统固有类型而已。因此,const pStr p2的含义是:限定数
据类型为char *的变量p2为只读,因此p2++错误。虽然作者在这里已经解释得很清楚了,可我在这个地方仍然还是糊涂的,真的希望哪位高手能帮忙指点一下,特别是这一句“只不过此处变量p2的数据类型是我们自己定义的而不是系统固有类型而已”,难道自己定义的类型前面用const修饰后,就不能执行更改运算,而系统定义的类型却可以?