2008年10月31日星期五
(转)中关村崩盘前夜,靠欺诈还能繁荣多久?
中关村"繁荣"的景象背后,正在酝酿一场前所未有的危机,以诚信为本的商业信条已被越来越多的商家抛弃,取而代之的是以透支诚信为代价的欺诈经营,并已被演义得日渐熟练且自成体系。
少数诚信不好的商家对一个商圈的影响是有限的,但当非诚信经营成为大多数商家的共同选择时,便已经从根本上动摇了这个群体所赖以生存的商业基础。
2008年,随着IT冬天的来临,经销商正在承受着越来越重的生存压力,而这种压力正在被商家试图以抛弃诚信的方式转嫁给消费者。而这饮鸩止渴的行为也进一步加速着自我毁灭的进程。
中关村正在遭遇自建立以来最黑暗的时刻,以电脑城为基础的商业形态在这个冬天经历着最为严厉的考验,在3C、连锁等商业形态的进逼之下,中关村等待着一次浴火重生。
输掉的官司
"我万万没有想到,板上钉钉的官司竟然输了。"王银波手里拿着判决书,满脸无奈。
2008年9月10日,消费者王银波收到了北京海淀区法院的民事判决书,针对王银波状告北京神州想联科技有限公司价格欺诈一案做出一审判决,以证据不足为由,驳回其诉讼请求。
对于王银波来说,这个结果多少有点讽刺意味,一方面自己现在是一家网络公司的运营总监,也算是IT圈里的人,另一方面自己十年前也曾经在中关村一家IT公司工作过近两年,算是对中关村比较熟悉了,可尽管有这样的资历,买电脑还是栽了跟头。
三个月前的6月15日,受小舅子的委托,王银波去中关村买一台笔记本,在位于中关村的北京想联科技有限公司内发生了如下的一幕。
"想联公司的工作人员向我介绍了一款三星笔记本,价格是5000元,我提出先看配置再付款,但是他们说先付款才能快一点拿到机器,我就支付了5000元现金。"王银波称付完钱之后,情况就开始发生变化了。
想联公司的业务员先是称这台三星笔记本运行速度慢,而给他推荐了另一款DELL的机器,而条件是需要再增加四百块钱,王银波想想可以有更高的配置,于是答应了,对方也很快为这台DELL的机器装好了系统,王银波也随手在服务确认单上签了字。而回到家中上网一查型号,王银波吸了口凉气,DELL这款机器网上的报价最高的三千四百元,最低的只有三千元。
这明显是被人坑了,愤怒的王银波立刻去找商家退货,提出不但要退货,而且要双倍赔偿。为了更加有把握,王银波甚至专门请了两位律师一起前往。然而想联公司想都没想就直接拒绝了王银波的要求。
"后来他们说给我一块U盘了事,我没同意,他们又提出可以给个40G的移动硬盘,但我怎么可能答应"。王银波回忆,由于双方没有达成协议,电脑城的管理部门介入,表示:"他们卖多少,那是他们自主决定的,我们不管,你如果说他们欺诈,那你就要拿出证据。"这让王银波确信电脑城明显和这些人是一伙的。遂决定用法律手段解决。
6月下旬,王银波通过律师将想联公司告上法庭,理由是价格欺诈。"傻子都看得出他们是在骗人,我坚信法律会给我一个说法。"为王银波说。
在支付给律师4000块费用时,他坚信自己能赢得这场官司,并且这个费用最终还得商家来出,那时他最担心的是,一旦自己胜诉,对于这样的商家,执行会不会有困难?
然而,事实证明,王银波的担心是多余的,因为他是败诉的一方,法院以证据不足为由驳回他的诉讼请求。
接到判决书,律师说如果要再上诉的话,不要再掏律师费了,可以再试试,王银波对此却心灰意冷。"这次败诉了,自己也没什么想法了,挺无奈的,也不想折腾了。"王银波淡淡地说。
虽然输了官司,小舅子的笔记本还得重新买,有了前车之鉴,王银波第二次买笔记本选择了某全国连锁3C卖场,"中关村电脑城打死再也不去了,哪怕多花点钱也买个心理踏实。"王银波说道。
"我在网上看到了很多类似的案例,最后都不了了之,我本来想通过这件事(胜诉),让大家对中关村有信心。现在想想,觉得自己挺幼稚的,要是在工商局工作可能能解决好,我们这些平头百姓根本就无能为力。中关村再也不值得信任了。"王银波沮丧地说。
探访中关村
9月20日,下午两点,笔者专程探访中关村某电脑城11层,整个楼层中看不到几个消费者,而各个店面的销售人员却有几十人站在各家门口,一旦有客人从自己门口走过,便上去热情邀请,吆喝声此起彼伏。
笔者随意走进一家店面,店员立刻端上杯热水,询问需要什么机器,笔者假装要买一台数码相机,与店员周旋。
而在店面的一角,一个学生打扮的女孩坐在沙发上,已经哭成泪人,三四个店员围坐在女孩周围,女孩不时情绪激动,大声哭起来,嘴里嚷嚷着:"我太傻了,就相信你们了,反正这两个镜头我不要了。"
店员们木然地抽着烟,当听说女孩提出退货时,一个店员冷笑一声,说道,你想都别想。另一个店员则好像好声劝慰。女孩哭声时断时续,但哭声依然很大,一个店员大概担心影响生意,对着门口放音乐的同事吼:"音乐声再调大些。"立刻,店内的哭声人声统统被巨大的音乐声淹没。
忽然,又一对年轻情侣夺门而入,指着一个店员说:"算你狠,这个价格你都敢卖。"随即找到一个座位坐下来,掏出一个相机,放在桌上,等待处理。一个店员迎过去问怎么了,年轻人情绪激动的说,别人卖一千二的相机,你们卖给我二千五百块钱。还没等说完,店员扔下他扭头自顾忙去了,年轻人坐在椅子上干坐着,忽然看见了旁边还在不断发出哭声的女孩......
笔者从电脑城11层下楼梯到10层,楼道的两侧排满了IT卖场,过道上同样站满了翘首等待的销售员。
一家店面内,110的pol.ice正在处理一起买卖纠纷,一位小伙子情绪激动地在给pol.ice讲述自己的遭遇。
"我和女朋友来电脑城想买个DV,问他们有没有这个机型,他们说有,然后就给我拿来了一个盒子,问我说你看是不是这个机子,我看了看盒子外面,看不清楚,就随手打开了盒子包装,但是他们就说,你打开了包装,只能买了,不买就不能走。"小伙子说着已经眼睛含着泪,"这哪是在做生意,不买东西还不让我走了,就是强买强卖。"
与小伙子态度不同,卖东西的女店员倒是一副讲道理的口气,她也向pol.ice阐述了自己的说法。
"他说要这款机器,我们家没有,于是向别家公司调的货,他要是不买的话,我还可以给人家退回去,但是他却打开了包装,这样我们就没法给人家退了,我跟他说,你把包装都拆了,所以这个机子我们也给你个公道价格,你买了大家就算了,他还不愿意。"女店员说道。
顺着小伙子所指,可以看到,与正常的外包装封条贴在盒子开口的最关键和最醒目的地方有所不同,这个盒子的一个封条十分隐蔽地藏在开口舌头的下方,而关键的开口处却没有封条,在花哨的外包装下,十有八九的人都不会发现这个封条的存在,而会随手打开包装。
"一是你没有告诉我这个盒子没有打开,也没有提醒我这个盒子不能打开,而是让我感觉这个盒子是开开的,同时,照你的说法,难道我们连看机器的机会都没有,只能通过一个盒子就必须确定买还是不买?"小伙子争辩道。
"反正包装是你拆开的,你今天就必须买。"女店员说道。
110pol.ice听着双方的争执,看了看盒子,研究半天,对双方表示,没有办法解决这种纠纷,你们还是找物业管理方吧。物业赶到,一面客客气气送走了pol.ice,一面把双方当事人带到了调解室,笔者也以"看看"为由,一路跟了下去。
到了物业处,电脑城的调解人员询问完事情经过后,把小伙子拉到一边,劝,"你反正也是要买DV,现在你又打开了人家的包装,不如我去查询一个价格,看他们出的价格是否有问题,如果没有问题,你不如就买下算了,机器总是要买的么。""我们的同事态度不好,你不要介意,我们这可是新机子,你再仔细看看。"一同下到管理处的女店员的同事―高个子的光头青年,此时面带微笑地说道。
小伙子此时显然是被说动了,对光头青年诺诺地说,你这样说话,人还是能接受,做生意嘛,不能说必须买你的机器,不买就不能走人。
电脑城市管理者理人员出去一会儿回来说,我查过价格了,他们卖给你的价格一点都不高,绝对没有问题。
一场纠纷就此达成协议,小伙子还是以最初的价格买走了这款机器,商家又出了一个机器,电脑城又解决了一次纠纷。
成熟的模式与完整的系统
"在中关村发生这种被骗的事情已经是很平常的事情,没什么值得大惊小怪,包括我们也在转型。"已经在中关村混迹了五年的宋强对此不以为然,从店员开始做起,一直到现在的店长,宋强对中关村发生的事情已经见怪不怪了。
所谓转型,既如文章开头王银波的遭遇,消费者在去电脑城之前,已经在网上查询并确定了自己想买的机器,而且对于价格也十分了解。对待这样的客户,商家一般首先会报出一个较低的价格,以此为诱饵,吸引客户到达楼上的店面,随即便伺机给客户推荐另一款产品,利用客户对这款产品不熟悉的特点,然后以远远高出市场价的价格卖给客户,这样,就完成了一次转型。
"现在这都是公开的秘密了,只是与前几年相比,现在很多公司的做法的确挺疯狂的。"宋强说道。虽然坦陈自己也"转型",但是也只是有时候操作,而有些公司几乎单单都在转。
在中关村一家公司老板周丛看来,如果一个消费者来到中关村,90%人都将会被商家收罗。"一般情况下,会有20%到30%的消费者会被转型,这部分人大多对市场和行情不了解,且对自己要买的机器没有充分的把握。但如果消费者就是坚持要买自己看中的机型,那么20%到30%的人会在配件方面被商家卖了高价,如果一些客户不要配件,那么好,商家则要求消费者先交钱,而一旦交了钱,往往消费者等了半天会被告之,没有货了,一个星期后才能到货,此时钱已经没有再要回来的可能,甚至会有文身和光头的青年出现,以武力相威胁,消费者这时大多会屈服。想想看,在这重重的陷阱之下,真正能不落圈套的消费者会有几个。"
"对于商家而言,现在没有哪家公司是真正不转型的,这已经成为集体的潜规则。"在周丛看来,如果说之前这种欺诈消费者只是一些个别现象,那么现在这种现象已经蔓延到了整个中关村,以转型为代表的操作手法已经从一些个别公司变成了大部分公司都在参与的做法。
"欺诈并不可怕,可怕的是欺诈成为一种普遍认可和操作的商业模式了,可怕的是这种非诚信经营,已经被绝大多数的商家接受并加以复制,成为了中关村的一种常态。"周丛表示,在中关村欺诈,蒙骗已经成为一套非常完整的体系和系统,对于单个公司而言,有专门拉客的,有专门谈判的,有专门摆平顾客的,甚至在公司的设置上,也会除了大厅之外,还会有单独的小隔间用来谈判和解决问题。
而对于整个中关村而言,这也形成了一个体系,凡是进入到这个体系的消费者,最终都可能被骗,而诉之无门。
在周丛看来,谁都知道这样的时日长不了,但谁都在继续转型,这种欺诈已经成为一种不计后果的行为,"大家都知道有完蛋的一天,但谁都忍不住要继续捞上一把。"
"中关村已经进入一个恶性循环的时期,口碑与日俱下导致了客流减少,客流减少使得成本和收益压力增大,进而导致转型欺诈现象更加肆无忌惮,反过来又再次成为众矢之的,使更多的消费者远离中关村。"周丛说道。
刀手周章
周章不愿意别人把自己称做"刀手",在他看来,这是个不值得吹嘘的角色,甚至有些不光彩。
事实上,在中关村的生态中,"刀手"有着其光鲜的一面,能被中关村圈里私下称为"刀手"的人,往往都是公司的销售精英,承担着主要的销售任务。这些人往往具有高超的销售技巧和丰富的销售经验,对客户的心理和销售的节奏能够有很强的把控力。也是因此,在公司中,"刀手"的地位往往能够做到部门经理级别,深为公司器重。
不仅仅如此,在中关村欺诈风盛行的今天,"刀手"也已成为公司销售中欺诈客户的主要实施者和操盘手,"刀"字本身就有暗含有"宰客"之意,他(她)能根据客户的信息迅速判断决定采用什么样的方法,宰多少,而且下手快而狠。
在中关村的IT公司,一般都会按照楼上楼下两个店安排,楼下店面是展示区,主要以展示产品和吸引客户为主,并不进行实际销售。楼下的销售人员主要的任务则是将客户带到楼上。而十层八层楼上一般都会比楼下大出几倍的面积,里面陈列着各种品牌的产品,这里主要进行销售,而"刀手"就是楼上的主要销售人员。
在中关村的"刀手"几乎家家都有一两个,而周章就是其中之一。从外地来北京已经三四年之间,从最初的一般的店面销售,经过努力成为了一名销售精英,周章也曾付出了大量的努力。如今,周章所在的公司经营产品主要以数码相机和笔记本为主,他也是公司销售人员中资历最老的一个。
"中关村的销售时间主要在早上的10点到11点之间,下午主要在1点到2点之间,现在已经3点多了,你看哪有几个人。估计今天又是个'钢圈'。"周章对今天的业绩有些郁闷,因为他的一个同事在刚刚的一次相机销售中,完成了一次转型,公司净挣了"一毛六"。
这里要解释下这些中关村的行话,所谓"钢圈"就代表销售为零,而"一块"代表一万元、"一毛"则代表一千元,"一分"代表一百。也就是说,刚刚自己的同事,通过转型,让一个只有两百元利润的数码相机到达了一千六百元,按照提成,他的同事这笔单至少要提成10%,即一百六十块。
"楼下的导购一个月还有800的底薪,拉上来一个客户成交还有35元钱的提成,而我们这些人都没有基本工资,而是靠提成生活,因此必须特别卖力才行。"周章说道。
按照公司的要求,周章每个月的任务是完成流水70万,利润必须达到1.5万,且提成比例不同,如果完成一万的利润,按照10%提,如果完成1.5万以上,则按15%提。
周章这个月的营业额任务基本可以完成了,因为就在几天前,他刚跟一个老客户完成了一笔50万元的订单,但感到失望的是,利润只有8000块。这离他的营业额任务不远了,但是为了能有更多的提成,他还必须在随后的一些天里,再加把劲,这样才能保证3000块以上的收入。
"我们不想骗消费者,可是没办法。"周章在谈到欺诈的原因时说道,"我们公司70多平方米的面积,30多个员工,每天的实际开销就要在四五千,这就是说,如果每天20个单子,每个单子挣250,那么我们才刚刚保证不赔本,但实际上,现在这样的生意,一天能做几单就不错了。因此一旦有客户,就要尽可能提高利润,没有办法,大家都要生存下去。"
同时,周章也认为:"其实也不能完全都怪商家,有时候消费者自己太贪,做生意哪有不挣钱的,他们一味地要最低价,如果按照他给的价格,只能是赔着做了,那商家还活不活?销售一生气宰他也是应该的。"
"现在竞争太激烈了,你看哪个客户从店里拎着东西出来,整个楼道销售看他都是异样的眼光,因为只有他自己不知道自己已经被骗了。"周章说道,"骗别人,自己也不舒服,谁都不是畜生,也知道受骗的感受,只是没有办法而已,这个工作就是个垃圾职业。"
如今周章对这份工作已经厌倦了,挣不了太多钱,还要背负良心的谴责,最关键的是他对中关村的未来已经完全丧失了信心。"这里没有黑幕,只有潜规则。中关村已经不行了,很多公司都是在赔着做,口碑越来越差,欺诈也越来越厉害,折腾不了多久了。"他说道。
现在周章最大的愿望就是能换个工作,他想先坚持到过年,希望能重新找到一份心安理得的工作,从此离开中关村。
老板的苦衷
找到钱刚,是因为网上的一个帖子,这个帖子称,买相机遭遇黑店,黑店的经理就是钱刚。发贴者详细控诉了自己被钱刚的公司骗掉两千多元的惨痛经历,并将钱刚的公司名称地址,乃至电话都披露在网上,希望以此曝光黑店,惩罚奸商。
与之前预计的一样,要采访钱刚并不顺利,电话那头听说要采访,第一个回答就是钱刚去外地出差了,等后来见了面才知道回答我电话的正是钱刚本人。
见到钱刚,比想象中坦率从容。"一个学生,本来就没钱,被我挣了两千,能不跟我拼命吗?但又能怎么样?"钱刚坐在店面的沙发里,平静地说道。
如今,钱刚在这座电脑城一共有三个店面,一楼大厅有两个店面,主要用来展示产品,吸引客户,18层的卖场则主要是销售和办公,人员有十几个。
在钱刚看来,中关村的经销商采取欺诈消费者的手段实在也是不得已而为之。他为笔者算了一笔账,他的公司三个卖场一个月总共的租金是五万,再加上销售、财务、商务、库管、电费、电话费等,如今公司每个月的成本是六万三千元。六万三除以三十天,每天的硬性成本是二千一百元,而这还没有算楼下销售和楼上人员的各20%总共40%的提成,加上提成,公司每天的销售利润必须达到三千五百元,才可以实现保本。
"这样的情况下,一天的毛利不上块(万),吃谁喝谁去?而要实现一天的毛利,就意味着最少我们每天必须卖八台机器,而每台机器的利润需达到一千二百五十才行,你觉得我们怎么才能完成这个任务?"钱刚反问道。
钱刚认为,高房租和网络是促使经销商非诚信经营的直接原因。电脑城的房租近几年开始迅速飙升,使得成本大幅攀升。
"几年前八千块的铺位涨到了四万,而几年前还是三万的铺位如今涨到了二十万,房租涨了几倍之多,利润却越来越少了。"钱刚表示,利润缩水主要是因为网络经销商发动价格冲击。
在位于钱刚18层的店面中的一角,如今已经用毛玻璃隔起了一个大约五平方米左右的单独空间,而这个空间被钱刚以每个月三千元的价格出租给了一个网络渠道。
"他们基本不要实体店面,在电脑城租一个办公的空间三五千块,再加上网络店面上的费用三千块钱,一共也就是六到八千块的成本,一两个人就可以开始单干。"钱刚表示,实体店面相对于这样的小的网络渠道却没有优势可言。
原因很简单,网络销售不需要样机、不需要库存、不需要承担高额的店面成本,"假设他们以4750元未税的价格销售,他还挣25块钱,而我们同样的机器卖到4850,还是赔钱的。原因很简单,就是因为我们的成本费用高出他们几倍,在这种情况下,消费者往往拿着网店给出的价格来实体店面购买,你说实体店面怎么活?"
钱刚表示,这样的网店销售商如今已经是一个非常大的群体,仅自己所在的电脑城里已经有上百家这样的网店,而中关村在线等网站的商家更是有五六千家之多。这样的网店往往挣一点就可以成交,这样的价格战也使得实体店面的生存岌岌可危。
"中关村(卖场)快死了,名气越来越差了。中关村要想口碑好起来,就需要一次彻底的洗牌,而洗牌最后的结果也只有15%的渠道能剩下来。现在都是在硬扛,谁能剩到最后,谁就可能留下来。"钱刚表示。
在他看来,更危险的是,中关村的商业逻辑也悄然发生变化,对于很多渠道商而言,只有两条路可走,要么也加入欺诈的行列,以高利润维持高成本,要么就离开中关村。
恶性循环的根源
"如果说非诚信经营现象,在中关村成立之初就存在,但那时是针对企业级的,也是偶发的现象,而现在则是针对消费者的,是频繁发生的,这是与之前的不同。"一位中关村老渠道商这样说。
如上所说,中关村在过去的二十多年时间里,职能和角色都发生了巨大的变化,过去以批发和行业客户为主的集散地特征正在消退,而以零售为主的中关村商业形态已经成型。在业内人士看来,零售形态中消费者更加弱势,这也是容易滋生欺诈的一个客观条件。
而如果要探询滋生非诚信经营的原因,不难发现,主要原因有两点:
1.成本骤升,2.利润下降。
"我们两年前还能维持22%的利润,现在已经降到了6%。以前我们这样的公司,过去一年挣一百万,现在平均下来也就十五万。"谈到现在中关村的形势,钱刚颇为感叹。
与此同时,网络导致的价格透明化也进一步侵蚀了渠道商的利润。而很多低成本网店以极低价格售卖对市场形成了最为残酷的价格打压。
诸多因素影响之下,渠道商日益艰难。有渠道表示,虽然销量上升了,但是利润却在下滑,因此整体而言,公司的赢利水平出现明显的倒退。
利润下降的同时,成本却在大幅攀升。"这两年无论是人力成本,还是店面租金都出现了大幅攀升,而尤其以租金的上涨为主。"渠道商冯涛抱怨道。
"中关村的房租在近几年出现了持续上涨,而最明显则是2006年之后,房租几乎是以每年15%的速度在非理性增长,我们公司四十平方米的店铺2006年租金还是70万,而到了2008年已经涨到了一百万。"冯涛说。
冯涛认为,房租高涨大大提高了渠道的运营成本,甚至已经超出了渠道的承受能力。在解释房租快速上涨的原因时,冯涛认为首先是电脑城对赢利预期过高,而厂商也在无意之间成为房租高涨的推动力量。
"在2006年之前,租铺位一般都是渠道商自己的事情,厂商并不直接参与,而之后,厂商开始介入到电脑城铺位的争夺,尤其争夺电脑城里面的黄金铺位。例如海尔提出的为渠道商承担半年或者一年的房租等等,其他厂商纷纷效仿,厂商的加入使得电脑城对赢利有了更高的预期,反向刺激了房租上涨。"冯涛表示。
实际上,成本升高与利润下降并非新鲜话题,而在冯涛看来,关键在于这两年两者的比例发生了根本性的变化,也就是说,过高的成本和过低的利润形成了倒挂,出现了收支失衡,渠道商已经开始从最初挣钱到现在少挣钱甚至赔钱。而这也是欺诈现象发生的重要原因。
然而也有不同的观点,鼎好电子商场业务总监李忠晋对于不诚信的原因也有着自己的看法:"即使我们调低房租,这些人还会依然存在,这种现象也会存在,关键在于人的贪欲和逐利。"他同时表示,鼎好一直在努力遏制不诚信现象的发生。但同时电脑城监管时也有现实难度,卖场并不是裁判者,现在的状况是一碗饭一百个人吃,过度竞争并不是卖场造成的。
也有卖场人士表示,虽然这两年房租是有上涨的现象,但将这种现象的责任推给电脑城并不合理,房租上涨主要是由供求关系决定。
事实上,细细探究不难发现,除此之外,欺诈发生的表象下,还有着更深层次的原因。
首先是新零售势力的崛起,网络销售、宏图三胞以及国美、苏宁这样的3C卖场都对传统卖场零售业态形成了巨大的冲击,而其中又以国美、苏宁为突出代表。
近两年,IT厂商与3C卖场之间的隔阂与偏见正在逐步消解,为了能够更加融洽地合作,双方都在做着妥协和让步,来积极地适应对方。DELL、联想为代表的企业都将与3C卖场的合作看做是战略重点,并且在各方面展开合作。而3C卖场IT产品的实际销量也在大幅攀升,成为对电脑城业态冲击最大的一股力量。
其次是,中关村缺乏一个健康的保证公平竞争的商业机制,实际上也就是一套相对合理公正的游戏规则。这个机制的最本质特征应该是对诚信的维护和对不正当竞争的惩戒,但建立这个机制则需要go-vern-ment部门、电脑城、厂商、渠道共同搭建,但显然,这个环节明显缺失了。
"这(欺诈现象)其实是由工商、法院、pol.ice、电脑城、经销商共同促成的结果,这些环节在有意无意之间,为欺诈现象搭建了一个得以滋生的温床。"渠道商这句话虽然有过激的嫌疑,但却也说出了关键。
由于缺乏一个健康竞争的商业机制,致使很多小的商家可以乘虚而入,通过不正当的手段竞争,而大的商家的规模化优势却无法发挥出来,反倒成为公司的劣势。如一些小公司灵活,可以不开发票,可以四处炒货,这就使得整个电脑城经销商群体无法长大,大大延长了优胜劣汰的时间,从而延缓了整个行业的进化和成长速度。
等待中的浴火重生
"今年的价格战格外的严重。"英特尔中国大区销售总监李翔评论,这个时期也可以说是黎明前的黑暗。
事实上,业界有着更加悲观的看法,认为现在中关村渠道商已经到了最艰难的时刻,中关村也步入了最黑暗的时期。
冯涛对于这样的观点也表示认同,"现在的中关村已经没有可以挽回的余地了。它只能重新洗牌。"他估计,中关村现在有六成左右的商家已经是赔着做,希望扛过这个冬天。
然而,对于更多的渠道商而言,黑夜已经来临,而黎明还不知什么时候出现。对于将来的洗牌的结果大多仍处于迷茫中。但洗牌大体的出路业界仍然能梳理出两个方向―一种可能是电脑城的经销商大量出局,剩下少数的成规模的公司,他们为了有更大的发展,往往能在诚信方面有所坚持,最终使得整个市场的诚信环境逐步恢复。
另外一种可能则是中关村的客流将会大量分流,网络、社区等连锁店面逐步被大多数人接受和认可。而这个过程也将伴随着中关村这种聚集型商圈的逐步衰落,正如当年欺诈横行的很多汽车交易市场逐步被4S店取代一样。而这个过程中,国美、宏三以及美承等连锁企业往往会有更大的发展机遇和生存空间。
显然,这两条路中的任何一条,都没有给中关村的经销商提供一条可行的出路。对于他们而言,继续在中关村按照原来的传统方式经营已经是日暮西山,而自寻出路已是迫在眉睫。
2008年10月29日星期三
邮件中的感谢
邮件的开头
感谢读者是邮件开场白的好办法。感谢您的读者能让对方感到高兴,特别是之后你有事相求的情况下会很有帮助。Thank you for contacting us.
如果有人写信来询问公司的服务,就可以使用这句句子开头。向他们对公司的兴趣表示感谢。Thank you for your prompt reply.
当一个客户或是同事很快就回复了你的邮件,一定记得要感谢他们。如果回复并不及时,只要将"prompt"除去即可,你还可以说,"Thank you for getting back to me."
Thank you for providing the requested information.
如果你询问某人一些信息,他们花了点时间才发送给你,那就用这句句子表示你仍然对他们的付出表示感激。Thank you for all your assistance.
如果有人给了你特别的帮助,那一定要感谢他们!如果你想对他们表示特别的感激,就用这个句子,"I truly appreciate … your help in resolving the problem."Thank you raising your concerns.
就算某个客户或是经理写邮件给你对你的工作提出了一定的质疑,你还是要感谢他们。这样你能表现出你对他们的认真态度表示尊重及感激。同时,你也可以使用,"Thank you for your feedback."
在邮件的结尾
在邮件开头表示感谢一般是表示对对方过去付出的感谢,而在邮件结尾处表示感谢是对将来的帮助表示感谢。事先表示感谢,能让对方在行动时更主动更乐意。Thank you for your kind cooperation.
如果你需要读者帮助你做某事,那就先得表示感谢。Thank you for your attention to this matter.
与以上的类似,本句包含了你对对方将来可能的帮助表示感谢。Thank you for your understanding.
如果你写到任何会对读者产生负面影响的内容那就使用这句句子吧。Thank you for your consideration.
如果您是在寻求机会或是福利,例如你在求职的话,就用这封邮件结尾。Thank you again for everything you'e done.
这句句子可以用在结尾,和以上有所不同。如果你在邮件开头已经谢过了读者,你就可以使用这句话,但是因为他们的帮助,你可以着重再次感谢你们的付出。
************************************************************
Joke of Today:
Don't Let the Dogs Bother You!
While driving down a winding country road, a man came upon a youth running hard with three huge dogs snarling at his heels. The man screeched his car to a halt and threw open the passenger door. "Get in, get in!" he shouted.
"Thanks," gasped the youth. "You're terrific! Most people won't offer me a ride when they see I have three dogs!"
2008年10月26日星期日
linux编程学习之thread
MFC中的是微秒,linux下的是秒。
linux下用微秒的线程休眠函数是: void usleep(unsigned long usec);
int usleep(unsigned long usec); /* SUSv2 */
或者用select函数+timeval结构也可以(最多精确到微秒),
或者用pselect函数+timespec(可以精确到纳秒,足够精确了!)
需要注意的是:Linux的最小时间单位为10ms,当usleep设置的值小于10时,usleep其实sleep的时间还是10ms 。
附:linux生成一个线程,主线程与新生线程通过semphore通讯,延迟0.5s。
/*
Filename : thread_bg.c
Description : Demo how to create thread with semaphore in Linux.
Release : 27/10/2008
Compile : g++ -lpthread thread_bg
*/
#include <stdio.h> // printf(),
#include <stdlib.h> // exit(), EXIT_SUCCESS
#include <pthread.h> // pthread_create(), pthread_join()
#include <semaphore.h> // sem_init()
sem_t binSem;
int NUM;
void* helloWorld(void* arg);
int main() {
// Result for System call
int res = 0;
NUM = 0;
// Initialize semaphore
res = sem_init(&binSem, 0, 0);
if (res) {
printf("Semaphore initialization failed!!\n");
exit(EXIT_FAILURE);
}
// Create thread
pthread_t thdHelloWorld;
res = pthread_create(&thdHelloWorld, NULL, helloWorld, NULL);
if (res) {
printf("Thread creation failed!!\n");
exit(EXIT_FAILURE);
}
while(1) {
// Post semaphore
sem_post(&binSem);
//sleep(1);//sleep 1 second
usleep(500000); //usec
}
// Wait for thread synchronization
void *threadResult;
res = pthread_join(thdHelloWorld, &threadResult);
if (res) {
printf("Thread join failed!!\n");
exit(EXIT_FAILURE);
}
exit(EXIT_SUCCESS);
}
void* helloWorld(void* arg) {
while(1) {
// Wait semaphore
sem_wait(&binSem);
printf("Hello world! %d\n",++NUM);
}
}
2008年10月25日星期六
国外典故这些你应该知道的
达摩克利斯剑:达摩克利斯是希腊神话中暴君迪奥尼修斯的宠臣,他常说帝王多福,以取悦帝王。有一次,迪奥尼修斯让他坐在帝王的宝座上,头顶上挂着一把仅用一根马鬃系着的利剑,以此告诉他,虽然身在宝座,利剑却随时可能掉下来,帝王并不多福,而是时刻存在着忧患。人们常用这一典故来比喻随时可能发生的潜在危机。
缪斯:缪斯是希腊神话中9位文艺和科学女神的通称。她们均为主神和记忆女神之女。她们以音乐和诗歌之神阿波罗为首领,分别掌管着历史、悲剧、喜剧、抒情诗、舞蹈、史诗、爱情诗、颂歌和天文。古希腊的诗人、歌手都向缪斯呼告,祈求灵感。后来,人们就常用"缪斯"来比喻文学、写作和灵感等。
斯芬克斯之谜:斯芬克斯是希腊神话中以隐谜害人的怪物,埃及最大的胡夫金字塔前的狮身人面怪兽就是他。他给俄狄浦斯出的问题是:什么东西早晨用四只脚走路,中午用两只脚走路,傍晚用三只脚走路?俄狄浦斯回答:是人。在生命的早晨,他是个孩子,用两条腿和两只手爬行;到了生命的中午,他变成壮年,只用两条腿走路;到了生命的傍晚,他年老体衰,必须借助拐杖走路,所以被称为三只脚。俄狄浦斯答对了。斯芬克斯羞愧坠崖而死。" 斯芬克斯之谜"常被用来比喻复杂、神秘、难以理解的问题。
皮格马利翁:皮格马利翁是希腊神话中的塞浦路斯国王。他憎恨女性,决定永不结婚。他用神奇的技艺雕刻了一座美丽的象牙女像,并爱上了她。他像对待自己的妻子那样抚爱她,装扮她,并向神乞求让她成为自己的妻子。爱神阿芙洛狄忒被他打动,赐予雕像生命,并让他们结为夫妻。" 皮格马利翁效应"后来被用在教育心理学上,也称"期待效应"或"罗森塔尔效应",比喻教师对学生的期待不同,对他们施加的方法不同,学生受到的影响也不一样。
犹大的亲吻:犹大是《圣经》中耶稣基督的亲信子弟12门徒之一。耶稣传布新道虽然受到了百姓的拥护,却引起犹太教长老司祭们的仇恨。他们用30个银币收买了犹大,要他帮助辨认出耶稣。他们到客马尼园抓耶稣时,犹大假装请安,拥抱和亲吻耶稣。耶稣随即被捕,后被钉死在十字架上。人们用"犹大的亲吻"比喻可耻的叛卖行为。
诺亚方舟:出自《圣经》。上帝对人类所犯下的罪孽非常忧伤,决定用洪水消灭人类。诺亚是个正直的人,上帝吩咐他造船避灾。经过40个昼夜的洪水,除诺亚一家和部分动物外,其他生物都被洪水吞没。后被用来比喻灾难中的避难所或救星。
伊甸园:出自《圣经》。上帝在东方的一片富饶的平原上开辟了一个园子,里面有果树和各种飞禽走兽。上帝让亚当看守园子。为排解他的寂寞,上帝从亚当的身上取出一根肋骨,造成一个女人――夏娃来陪伴他。他们过着无忧无虑的日子。人们用伊甸园比喻人间的乐园。
禁果:出自《圣经》。亚当和夏娃住在伊甸园中,上帝允许他们食用园中的果实,唯独"知善恶树"上的果实不能吃。狡猾的蛇引诱他们吃了禁果,从此他们懂得了善恶,辨别出真假,而且产生了羞耻之心。上帝因此将他们逐出伊甸园。禁果比喻被禁止得到而又渴望得到的东西。
多米诺骨牌:是一种西洋游戏,将许多长方形的骨牌竖立排列成行,轻轻推倒第1张牌时,其余骨牌将依次纷纷倒下。用于比喻时,"多米诺骨牌效应"常指一系列的连锁反应,即"牵一发而动全身"。
象牙塔:出自19世纪法国诗人、文艺批评家圣佩韦・查理・奥古斯丁的书函《致维尔曼》。奥古斯丁批评同时代的法国作家维尼作品中的悲观消极情绪,主张作家从庸俗的资产阶级现实中超脱出来,进入一种主观幻想的艺术天地――象牙之塔。"象牙塔"被用来比喻与世隔绝的梦幻境地。
滑铁卢:1815年,在比利时的滑铁卢,拿破仑率领法军与英国、普鲁士联军展开激战,法军惨败。随后,拿破仑以退位结束了其政治生涯。"滑铁卢"被用来比喻惨痛的失败。
鳄鱼的眼泪:西方传说,鳄鱼捕到猎物时,一边贪婪地吞噬,一边假惺惺地流泪。喻指虚假的眼泪,伪装的同情。又被引申为一面伤害别人,一面装出悲天悯人的阴险狡诈之徒。
山姆大叔:是美国的绰号,产生于1812年美英战争时期。纽约州一位诚实能干的肉类包装商被人们亲切地称为"山姆大叔 "。他担任纽约州和新泽西州的军需检验员,负责在供应军队的牛肉桶和酒桶上打戳。人们发现该厂的牛肉桶上都盖有E.A―U.S.标记。本来,E.A是一个军火承包商的名字,U.S是美国的缩写。碰巧山姆大叔(UncleSam)的缩写与美国的缩写(U.S.)相同,人们就管美国叫"山姆大叔"。美国人把"山姆大叔"诚实可靠、吃苦耐劳以及爱国主义精神视为自己民族的骄傲和共有的品质。1961年,美国国会正式承认"山姆大叔"为美国的民族象征。
2008年10月24日星期五
(转)不可以空腹吃的食品
柿子、西红柿
含有较多的果胶、单宁酸,上述物质与胃酸发生化学反应生成难以溶解的凝胶块,易形成胃结石。空腹吃柿子,大量的柿胶酚和红鞣质收敛剂与胃酸凝结成硬块,形成"柿石"。容易引起恶心、呕吐、胃溃疡,甚至胃穿孔等。柿子在饭后吃就不易形成"柿石"。
香蕉
香蕉中有较多的镁元素,镁是影响心脏功能的敏感元素,对心血管产生抑制作用。空腹吃香蕉会使人体中的镁骤然升高而破坏人体血液中的镁钙平衡,对心血管产生抑制作用,不利于身体健康。
山楂、橘子
含有大量的有机酸、果酸、山楂酸、枸橼酸等,空腹食用,会使胃酸猛增,对胃黏膜造成不良刺激,使胃发胀满、泛酸,若在空腹时食用会增强饥饿感并加重原有的胃痛。
牛奶、豆浆
这两种食物中含有大量的蛋白质,空腹饮用,蛋白质将"被迫"转化为热能消耗掉,起不到营养滋补作用。最好的饮用方式是与含面粉的食品同食,或餐后两小时再喝,或睡前喝均可,既有滋补保健、促进消化作用,又有排气通便作用。
糖
糖是一种极易消化吸收的食品,空腹吃糖太多会使血液中的血糖突然增高,人体短时间内不能分泌足够的胰岛素来维持血糖的正常值,使血液中的血糖骤然升高容易导致眼疾。而且糖属酸性食品,空腹吃糖还会破坏机体内的酸碱平衡和各种微生物的平衡,对健康不利。
白薯
白薯中含有单宁和胶质,如果空腹吃,会刺激胃壁分泌更多胃酸,引起烧心等不适感。
冷饮
空腹时大量吃各种冷冻食品,会刺激胃肠发生挛缩,久之将导致各种酶促化学反应失调,诱发肠胃疾病。也会导致内脏器官功能受到损伤。女性月经期间还会使月经发生紊乱。
酒
空腹饮酒容易刺激胃黏膜,引起胃炎和胃溃疡等多种病变。人体会很容易出现低血糖,进而头晕、出冷汗、心悸,严重者导致低血糖昏迷甚至死亡。
大蒜
大蒜含有辛辣的蒜素,空腹吃蒜,会对胃黏膜、肠壁造成刺激,引起胃痉挛,影响胃肠消化功能。
茶
空腹饮茶会稀释胃液,降低消化功能,还会引起"茶醉",表现为心慌、头晕、四肢无力、胃肠不适、腹中饥饿等。
黑枣
黑枣含有大量果胶和鞣酸,这些成分与胃酸结合,同样会在胃内结成硬块。
维生素
维生素虽然不是食物,不过,如果在空腹时吃,会在人体还来不及吸收利用之前即从粪便中排出。如维生素A等脂溶性维生素,溶于脂肪中才能被胃肠黏膜吸收,应宜饭后吃用,才能够较完全地被人体吸收。,
菠萝
富含强酵素的菠萝,空腹吃会伤胃,其营养成分,最好在吃完饭后食用,才能更好地被吸收。
2008年10月23日星期四
刚才有客户投诉微软XP黑屏
微软:是的,请问您有什么事吗?
用户:前几天看到你们发布公告,不是说好了要黑屏的吗?我都等一天了,怎么还不黑啊。。。。
微软:#%$^&@
用户:你们到底黑不黑了?
微软:…….
用户:说啊,你们黑不黑?
微软:黑… 盗版的才黑,你是盗版吗?
用户:是啊,100%盗版,绝对盗版.
微软:那不应该啊,对了,你打开自动更新了吗?
用户:打开了啊,早早的就打开了,就等着看黑屏呢!
微软;##$%^&%$#@$%…
用户:怎么了?不需要打开啊?
微软:需要,需要!
用户:那咋还不黑呢?
微软:你的自动更新有提示你下载补丁吗?
用户:这倒没有,我等老半天了,最后等不了了,自己去你们网站上下了一个,结果还是不黑,你说这玩意整的!
微软:….
微软:对不起,先生,我们暂时无法解决你的问题,呆会找技术人员跟你联系,谢谢!
挂机…………
电脑用户保持眼睛湿润
信息科技时代来临,计算机使用的普及,不少长期在计算机前工作的朋友常常会觉得眼睛干涩、视力模糊,这其实很可能就是干眼症的早期表现。提醒各位,对这种状况不可掉以轻心。
根据美国全国职业保健与安全研究所的调查显示,每天在计算机前工作3小时以上的人之中,有90%的人眼睛有问题,表现症状是:眼睛干涩、头痛、烦躁、疲劳、注意力难以集中等,这种计算机视力综合症就是典型的干眼症。日本眼科医学会的调查结果也显示,每3名长期面对计算机屏幕的工作人员中,就有1名患有干眼症。因此,在计算机前工作的人需要特别注意保护眼睛。
诺贝尔眼科医师 张朝凯表示泪液主要有三大功能:一是湿润眼球;二是保持眼球的洁净;三是与眼屈光有关。如果泪液分泌太少或者蒸发过多,角膜表面和结膜表面得不到足够的滋润,看东西时,就会出现视物不清、眼睛酸涩等状况。而眨眼是一种保护性的神经反射作用,使泪水均匀地涂在角膜和结膜表面,以保持其润湿。正常人每分钟眨眼约为1020次,倘若长时间凝视计算机屏幕,眨眼次数常减少至每分钟45次,眼睛便会感到干涩。
防治干眼症,最重要还是让眼睛充分获得休息。若是无法避免与计算机为伍,我建议计算机屏幕上显示的亮度应为周围光线的3倍左右,屏幕的上端稍微低于视线10~15度,眼睛与计算机屏幕距离要保持在30cm以上;在计算机前每工作1个小时,就应该闭目休息10分钟,并用手按摩放松眼部周围的肌肉,或者眺望远处的景物等;此外,也可使用一些人工泪液来滋润眼睛,或刻意增加眨眼的次数,在饮食方面,可多吃富含维生素A的食物,例如胡萝卜和动物肝脏等。
别漠视干眼症,往往一开始只是感到眼睛干燥和酸涩,眼睛尚处于功能性损伤的阶段,但是如果这时还不注意保护眼睛,持续让你的眼睛长期处于干燥的状态,则可能引起角膜上皮细胞的脱落,造成器质性的损伤,当症状进一步恶化,则可能严重影响视力。最后还是要再次叮咛你,注意生活饮食规律正常,避免熬夜,才是眼睛保健根本之道。
(转)如何加速for loop? (C/C++)
原题如下
有一个for循环,从0加到100,可是我觉得它不够快,要怎样才能让她更快呢?(不可以用数学公式)
for(i = 0; i <= 100; i++)
s = s + i;
我能想到的解法
1.调compiler参数,vc++和gcc都有-o可以调,可以optimize for speed,是最懒的方式。
2.善用多核心:
/*
(C) OOMusou 2008 http://oomusou.cnblogs.com
Filename : parallel_for_100.c
Compiler : Visual C++ 8.0
Description : Demo how to parallel sum 0 to 100
Release : 03/14/2008 1.0
*/
#include <stdio.h>
#include "omp.h"
int main() {
int i;
int s = 0;
printf("Number of core : %d\n", omp_get_num_procs());
printf("Number of threads : %d\n", omp_get_num_threads());
#pragma omp parallel for
for(i = 0; i <= 100; ++i) {
printf("thread %d : %d\n", omp_get_thread_num(), s = s + i);
}
printf("%d\n", s);
}
行结果
mber of core : 2
mber of threads : 1
read 0 : 0
read 1 : 51
read 0 : 52
read 0 : 106
read 1 : 104
read 0 : 109
read 1 : 162
read 0 : 166
read 1 : 220
read 0 : 225
read 1 : 280
read 0 : 286
read 1 : 342
read 0 : 349
read 1 : 406
read 0 : 414
read 1 : 472
read 0 : 481
read 1 : 540
read 0 : 550
read 1 : 610
read 0 : 621
read 1 : 682
read 0 : 694
read 1 : 756
read 0 : 769
read 1 : 832
read 0 : 846
read 1 : 910
read 0 : 925
read 1 : 990
read 0 : 1006
read 1 : 1072
read 0 : 1089
read 1 : 1156
read 0 : 1174
read 1 : 1242
read 0 : 1261
read 1 : 1330
read 0 : 1350
read 1 : 1420
read 0 : 1441
read 1 : 1512
read 0 : 1534
read 1 : 1606
read 0 : 1629
read 1 : 1702
read 0 : 1726
read 1 : 1800
read 0 : 1825
read 1 : 1900
read 0 : 1926
read 1 : 2002
read 0 : 2029
read 1 : 2106
read 0 : 2134
read 1 : 2212
read 0 : 2241
read 1 : 2320
read 0 : 2350
read 1 : 2430
read 0 : 2461
read 1 : 2542
read 0 : 2574
read 1 : 2656
read 0 : 2689
read 1 : 2772
read 0 : 2806
read 1 : 2890
read 0 : 2925
read 1 : 3010
read 0 : 3046
read 1 : 3132
read 0 : 3169
read 1 : 3256
read 0 : 3294
read 1 : 3382
read 0 : 3421
read 1 : 3510
read 0 : 3550
read 1 : 3640
read 0 : 3681
read 1 : 3772
read 0 : 3814
read 1 : 3906
read 0 : 3949
read 1 : 4042
read 0 : 4086
read 1 : 4180
read 0 : 4225
read 1 : 4320
read 0 : 4366
read 1 : 4462
read 0 : 4509
read 1 : 4606
read 0 : 4654
read 1 : 4752
read 0 : 4801
read 1 : 4900
read 0 : 4950
read 1 : 5050
50
以上结果可以发现,thread 0由0开始跑,thread 1由51开始跑,但之后就随机分配到两个核心,所以充分利用两个核心来运算。
inline assembly:我asm忘了差不多了, 一时也写不出来XD
改用unsigned int,而不用int,这种写法经我测试可以快一倍速度,主要是int须动用到有号数运算,速度较慢,详细原理请参考(原创) 无号数及有号数的乘加运算电路设计 (初级) (IC Design) (Verilog) (Linux)
/*
(C) OOMusou 2007 http://oomusou.cnblogs.com
Filename : for_loop_original.c
Compiler : Visual C++ 8.0
Description : Demo how to optimize for loop
Release : 01/05/2008 1.0
*/
#include <stdio.h>
int foo(int n) {
unsigned int i, s = 0;
for(i = 0; i <= n; i++)
s = s + i;
return s;
}
int main() {
int s;
s = foo(100000000000);
printf("%d\n", s);
}
使用Loop unwinding,请参考Wiki Loop unwinding
/*
(C) OOMusou 2007 http://oomusou.cnblogs.com
Filename : for_loop_unwiding.c
Compiler : Visual C++ 8.0
Description : Demo how to optimize for loop
Release : 01/11/2008 1.0
*/
#include <stdio.h>
int foo(int n) {
unsigned int i, s = 0;
for(i = 1; i <= n; i+=10) {
s += i;
s += i+1;
s += i+2;
s += i+3;
s += i+4;
s += i+5;
s += i+6;
s += i+7;
s += i+8;
s += i+9;
}
return s;
}
int main() {
int s;
s = foo(100);
printf("%d\n", s);
}
使用C++的Template Meta Programming方式,由于这是一种在compile time的运算,而不是在run time计算,所以在run time速度很快。
/*
(C) OOMusou 2007 http://oomusou.cnblogs.com
Filename : for_loop_tmp_cpp.cpp
Compiler : Visual C++ 8.0
Description : Demo how to optimize for loop by TMP
Release : 01/05/2008 1.0
*/
#include <iostream>
using namespace std;
template <unsigned n>
struct foo {
enum { value = n + foo<n-1>::value };
};
template<>
struct foo<0> {
enum { value = 0 };
};
int main() {
cout << foo<100>::value << endl;
}
C++的template类似C的macro,是否可用c来达成呢?我试着用以下的C,但无法compile成功,主要是C并不支持recursive macro,所以无福消受。
1#include <stdio.h>
2#define foo(x) ((x)?((x)+foo(x-1)):(0))
3
4int main() {
5 int s = foo(100);
6 printf("%d\n", s);
7}
7.使用硬件加速编译器;
浅谈嵌入式系统之SOC
灵活的外设和硬件算法的实现,这些都是Nios II的优点。不过若以研究来说,Nios II远比ARM有趣,因为软件硬件的功力都需要有,一旦上瘾就很难自拔。
2008年10月22日星期三
(转) 给对电机领域有兴趣的学弟学妹建议
假如能重新来过读大一,你会怎么读呢?以下是我对学弟学妹的建议。
Introduction
电机领域很大,有固态电子、IC设计、计算机、通讯、控制、光电、医工...等,由於我有兴趣的是IC设计和计算机,若学弟学妹对此领域有兴趣,我有以下建议:
在大一时,顷全力将C语言和Verilog两个语言搞好,为什么呢?
1.C语言:
这是软体语言的代表,这科影响了后续的资料结构、演算法、计算机组织、作业系统、驱动程式、系统程式、韧体实验、编译器的学习,也就是说只要是软体且要写程式,都逃不掉C语言,C语言没学好,剩下这几科根本学不好。
2.Verilog语言:
这是硬体语言的代表,这科影响了后续的逻辑设计、数位设计、计算机组织、VLSI、SOC、FPGA等科目的学习,当你学这些科目时,若你懂硬体语言,可以直接在code中理解背后的理论,所谓『有code有真相』,而且还能藉由理论自己实作来验证,如在计算机组织学到的pipeline理论,自己可以用Verilog将其实现,这样将更有感觉。
Conclusion
C语言和Verilog语言分别是软体语言和硬体语言的代表,若两个都熟,会帮你同时打通软体和硬体的任督二脉,尤其嵌入式系统的兴起,软体和硬体已经整合,没什么只懂硬体或只懂软体的人才,两个都得懂。大一电机系都会教C语言,只要跟著学校好好学就好(当然我承认C语言很难学),但是通常学校将Verilog或者VHDL放到大二或大三才教,我觉得太晚了,建议学弟学妹大一就自修Verilog,这样大二大三的逻辑设计、数位设计、计算机组织会学得更好,更有感觉,这我有深切的体认。
至於C++呢?我的建议是:『行有余力,再学C++』,毕竟C语言和Verilog的重要性远大於C++,虽然C++的物件导向泛型非常有趣,MFC、Qt、SystemC..等也是用C++,但对於电机系大一而言,C语言和Verilog的重要性远大於C++,所以先将这两科搞好比较重要。
C/C++几点知识
很早以前就有这个疑问,在C/C++中,若明知自己的计算没有很大,是否可以char或short来取代int以节省内存。C++ Primer 4th在P.38做了以下的建议:
When performing integer arithmetic, it is rarely right to use shorts. In most programs, using shorts leads to mysterious bugs when a value is assigned to a short that is bigger than the largest number it can hold. What happens depends on the machine, but typically the value "wrap around" so that a number too large to fit turns into a large negative numbers. For the same reason, even though char is an integral type, the char type should be used to hold characters and not for computation. The fact that char is signed on some implementations and unsigned on others makes it problematic to use it as a computational type.
2. 浮点运算时,该使用float还是double?
在C++ Primer 4th P.38,对float和double做了以下的建议
Determining which floating-point type to use is easier: It is almost always right to use double. The loss of precission implicit in float is significant, whereas the cost of double precision calculations versus single precision is negligible. In fact, on some machines, double precision is faster than single.
3. i++和++i哪个速度较快?
++i和i++哪个速度较快呢?在C++ Primer 4th整本书中,都是用++i。为什么,答案是因为++i较快,所以C++ Primer才都使用++i。
++i相当于
i = i + 1;
return i;
而i++相当于
int j = i;
i = i+1;
return j;
由于i++还必须copy值给j,所以速度较慢。
4. include了不必要的Header File,是否会增加编译后程序的大小呢?
编译器会根据您所撰写的程序内容自己到所含括进来的头文件去撷取所需要的信息,而没有使用到的信息则不属于这个程序的范围,故不会增加代码的大小。当然,我们也没有必要含括一些没有必要的头文件到程序来,因为这只会徒增程序阅读的困扰。
参考: C语言教学手册 P2-6,洪维恩著
数字图像处理的经典人物Lena
(转) UML中文FAQ
V1.0 张恂 2003年10月29日
编者按:
一直很难找到中文的、适合国人看的UML FAQ,而英文的UML FAQ往往散落于各处不易收集和消化,导致很多看似简单却很基本、很关键的问题在网络论坛、BBS上被反复地提出,既浪费了提问者、解答者不少的时间和精力,也给大家学习总结、进一步提高带来了困难。于是,结合自己的所学所想试着编写了一个UML中文FAQ供各位参考。众人拾柴火焰高,有什么不足之处,欢迎大家多多提出宝贵意见,不断补充,一起来完善!
联系邮箱:zhangxun2001@hotmail.com
什么是UML?
UML有哪些特点?
UML有什么用?
UML不能做什么?
为什么要学习和掌握UML?
什么情况下不需要或不适合用UML?
什么情况下应该用UML?
促进UML普及和应用对于加强我国软件业的实力有什么重要意义?
UML的统一性表现在哪些方面?
UML是如何诞生的?
UML是一家之言,或少数派的观点吗?
UML之父是谁?
UML标准有哪些最新进展?
初学者如何开始学习UML?
世界上有哪些著名的公司、组织参与了UML标准的制订?
什么是UML?
Unified Modeling Language(统一建模语言)是国际对象管理组织OMG制定的一个通用的、可视化建模语言标准,可以用来描述(specify)、可视化(visualize)、构造(construct)和记载(document)软件密集型系统的各种工件(artifacts,又译制品)。
UML有哪些特点?
UML的"通用性"主要是指不仅仅可以用它来描述软件,而且还可以用它来描述一般企业或组织的业务流程以及由软、硬件共同组成、以软件为主的复杂系统(即所谓的软件密集型系统),甚至还包括非软件系统。
UML的"可视性"是指可以通过UML一系列的图形符号,组成多种视图(view)来直观、清晰地表达系统分析设计中方方面面的、许多复杂的概念。UML主要是为了人的阅读和使用而设计的,所以它采用了半形式化的,易于人们理解、交流的形式。
UML是一种分析设计专用的建模语言,它本身不是编程(programming)语言,不能直接用来生成可执行的软件。UML是一种抽象层次比C、C++、Java、VB、Delphi等文本高级语言更高的图形语言,通过它我们可以抽象地表示用高级编程语言编写的文本程序的逻辑结构和行为。相比传统的高级编程语言,UML能够更加高效、准确地反映软件设计的方案和思路,是真正用来"设计程序"(design programs)的语言。从这个意义上看,不妨称UML为"甚高级"程序设计语言。
UML基本上不能算作全新的发明,它并非学者教授、科研机构的研究成果,而是直接来自于产业界、工程界的实践总结,是在归纳基础上进行理论升华的产物,其核心内容反映了30多年来全球软件工业的领导者在软件设计构造领域的最佳实践和成功经验,因而具有很高的实用价值。
实践证明,OO分析设计(OOAD)方法比传统方法能更加准确、全面地描述现实世界。UML是用来表述OO概念的一种语言工具,而很奇妙,它本身作为一件产品同样也是用OO方法来设计的,这使得UML具有传统建模语言所不具备的很强的语义表达能力和非常灵活的可扩展性。
UML有什么用?
UML的用途非常广泛,可以概括为"描述、可视化、构造、记载"4种基本功能,在业务建模、需求分析、系统设计、实现和测试、数据建模、项目管理等阶段任务中均可根据需要采用。
UML建模是建立软件开发文档的一个有效手段,通过UML可视化地描述系统需求,记载软件构成,能够显著地提高文档的质量和可读性,减少编写文档的工作量。
UML实质上是一种系统分析设计专用语言,通过可视化的图形符号结合文字说明或标记可以帮助业务/系统分析员、软件架构师/设计师、程序员等各种建模者有效地描述复杂软件(或业务)的静态结构和动态行为,包括工作流(数据流和控制流)、功能需求、结构元素及关系、架构组成、设计模式、对象协作、事件响应和状态变化等等。
UML不能做什么?
UML不是高度形式化的语言,一般不能用于定理证明。
UML是基于OO方法的通用建模语言,不适合用户图形界面设计、超大规模集成电路(VLSI)设计、基于规则的人工智能等专业领域。
UML是一种离散型建模语言,适合对由软件、固件或数字逻辑构成的离散系统建模,不适合对工程和物理学领域中的连续系统建模。
本身作为语言,UML仅仅是一种表达形式,不是建模方法,在实际的软件项目开发中仅仅掌握一套标准的图形符号是远远不够的。用好UML首先需要掌握OOAD的基本原则和方法,并在一定的软件开发过程(如UP/RUP、XP等)的指导下进行有取舍的运用。
为什么要学习和掌握UML?
首先,UML对于当前大多数希望进一步改进质量的软件开发团队来说是必不可少或必须的。为什么这样说呢?因为,C++、Java等源码并不能直观、方便地反映复杂程序的设计:如内部逻辑结构、各种隐含的依赖关系、运行时的状态改变和特殊行为等等。写好的代码仅仅是一种实现方式,很难反映出现象背后的真实本质――设计,因此对于大多数稍稍复杂点的项目来说,仅有代码是不够的。
可视化建模历来是一种成功的工程实践做法,并非软件行业所特有。软件的设计方案在用C++、Java实现之前通常隐藏在人们的头脑当中,而设计正确与否是决定软件质量好坏的要害,一个前提是必须用适当的方式把它表达出来。通过程序代码来表达、讨论、评估和选择复杂软件的设计往往是很笨拙的,这一表达
方式的空欠唯有通过类似UML的建模语言来填补。
其次,OO方法是当代主流的软件开发技术。世界先进的软件团队和个人早已摆脱了对如何使用具体的平台API、个别编程语言特性的纠缠,而把更多的精力放在了需求、架构、设计机制和模式等对软件质量有重大影响的核心要素――分析与设计上。UML作为OO建模语言的事实上标准和主要的表达媒介,在这些场合能发挥关键的作用。所以,熟练地掌握并运用好UML是当今系统分析员、架构师/设计师以及程序员等软件工程师所必备的一项基本技能。
什么情况下不需要或不适合用UML?
对于一个特定的软件公司或开发团队,在下列情况下建议不采用UML:
1)传统的做法已完全适用,对OOAD的要求也不高,项目非常成功,无任何改进的必要;
2)开发的系统比较简单,直接用源码配上少量的文字就能解决问题,软件开发文档也无需添加图形来辅助说明;
3)开发人员更习惯于直接阅读源码,用源码交流,这样做不影响工作效率和质量;
4)开发的系统本身不属于OO方法、UML适用范围。
什么情况下应该用UML?
对于一个特定的软件公司或开发团队,在下列情况下建议采用UML:
1)OO方法是项目决定采用的方法论,是整个项目或产品成功的关键;
2)开发人员感觉用源码说明不了真正的问题,希望利用可视化建模语言简化文档,提高交流的效率,准确抓住问题的本质;
3)系统的规模和设计都比较复杂,需要用图形抽象地表达复杂的概念,增强设计的灵活性、可读性和可理解性,以便暴露深层次的设计问题,降低开发风险;
4)公司希望记录已成功项目、产品的公共设计方案,在开发新项目时可以参考、重用过去的设计,节省投入,提高成功率。
促进UML普及和应用对于加强我国软件业的实力有什么重要意义?
OO方法自上世纪80年代以来已经成为软件开发的主流技术,标准OO建模语言UML的问世说明OO技术的发展达到了一个新的高峰。推广普及UML的应用可以使我国软件开发人员、软件企业和客户乃至整个行业都从中获益:
1)个人
UML相关知识体系蕴含了非常丰富的当代软件工程先进知识。软件开发人员通过学习和掌握UML概念、表示法及相关的软件过程、软件工程技术,能够加深对OOAD原则、方法的理解,提高抽象思维能力,从而站在更高的层次上分析问题、解决问题,这是一条快速提高个人软件设计能力的有效途径。
2)企业
对软件企业内部,用好UML,不但能直接提升企业的软件设计开发能力,而且由于UML能形象直观地记录软件设计的核心思想,可以使软件开发管理透明
化,促进企业知识资产的保护和增值,促进软件重用和整体效益的提升。对外,由于UML是通行的软件行业国际标准,企业在业务交往中有效运用UML,无论对于开拓国内外产品市场还是保障工程承接、项目外包等业务的顺利开展都大有裨益。
3)行业
积极采用国际通行的软件描述和设计语言UML,一方面能增加信息透明度,显著降低软件企业之间、客户与开发商之间的沟通成本,减少项目失败的风险,另一方面能促进行业市场的规范化和标准化,增进国际技术交流,整体提高我国软件业的技术水平和参与国际市场竞争的能力。据悉,与UML有关的国家级标准目前正在考虑制定过程当中。
UML的统一性表现在哪些方面?
UML的统一性至少表现在以下几个方面:
1)随着OO技术的蓬勃发展,到上世纪90年代初OO方法已经多达50余种,它们之间既有很多共通之处也存在许多没有必要的细节差异,这妨碍了技术进步,不利于产业的发展。UML统一了多种互补的、最具代表性、最受业界欢迎的主流OO方法,这既是历史的必然,也OO方法成熟的一个重要标志。UML及与其配套的OO统一过程(RUP)在实现"合并同类项"的基础上又向前迈出了一大步,不愧为当代OO建模方法的集大成者。
2)UML适用于各个行业的信息化工程,包括电信、银行、保险、税务、办公自动化、电力、电子、国防、航天航空、制造、工业自动化、医疗、交通、商业、电子商务等诸多领域的业务建模和软件分析设计,尤其适合对大中型、复杂、分布式应用系统或软件产品建模,在这些广泛的领域中都可以统一使用一套标准的建模语言。
3)作为一种独立于具体实现的、抽象的表述方式,UML广泛地适用于各种现代程序设计语言、数据库和开发平台。
4)有了UML标准,面向各种不同的软件开发方法和过程(如重载/轻载,瀑布式/迭代递增式),在软件开发生命周期各个阶段的工作(如业务建模、需求分析、设计、实现、测试)中,都可以采用一套统一的概念和表示法,避免了语言转换的麻烦。
5)UML明确定义了一套公共的内部概念,建立了统一的关于建模语言的元模型,反映了在软件和信息建模技术领域的最新成果。
UML是如何诞生的?
识时务者为俊杰。为了突破上世纪90年代初OO方法论混战的局面,1994年杰出的Rational公司OO大师Grady Booch邀请通用电气公司著名OO大师James Rumbaugh博士加盟Rational,启动了OO方法的统一历程。他们于1995年发表了"统一方法0.8"。与此同时,另一位超一流OO大师、爱立信公司的Ivar Jacobson博士也在该年加入了二人的行列。
1996年,3人正式把他们的统一成果命名为"统一建模语言",UML于此诞生。同时,他们还做出了一个非常重要的决定――把UML提交到非赢利性的OMG进行标准化,让全世界的软件开发人员都可以自由地分享这一软件史上的重大成果。
于是,在全球软件界具有广泛影响力的国际对象管理组织OMG从此开展了一系列OO建模语言的标准化工作。1997年11月,UML 1.1经OMG各成员投票被正式采纳为行业标准。
UML是一家之言,或少数派的观点吗?
否,UML是全球工业界和学术界的领导者协同努力的成果。自从进入OMG程序后,UML就不再由Rational一家公司所有或由少数人控制,而成为凝结了百家之长的公共知识结晶。具有丰富企业信息系统和信息工程经验的OO大师、Martin/Odell方法的领军人物James Odell为此曾表明放弃自己的方法,并直接参与领导了UML 1.x系列标准的制订工作。另一位OO大师Coad/Yourdon方法的创始人之一Peter Coad,虽然没有直接参与UML制订,但却独具慧眼创办了TogetherSoft公司(已被Borland收购),开发了著名的UML集成开发环境Together ControlCenter,成为Rational Rose的有力竞争者。事实说明,UML的确促进了各种OO方法和流派的大融合,在OO建模语言领域具有不可替代的地位。
UML之父是谁?
UML之父有三位:他们是Grady Booch(Booch方法),James Rumbaugh(OMT方法)和Ivar Jacobson(OOSE方法)。人们亲切地称他们为"3 amigos"(即"三高",类似于大家给予合作举办世纪音乐会的,世界上3位顶尖男高音歌唱家的称谓)。
UML标准有哪些最新进展?
UML 1.x系列的最新版本是于2003年3月发布的1.5版本(http://www.uml.org/)。
OMG(http://www.omg.org/)从2000年起启动了UML 2.0标准的制定工作。U2P组织(UML2 Partners Consortium,http://www.u2-partners.org/)在UML 2.0标准的制定过程中发挥了主导作用。目前UML 2.0的上层结构(Superstructure)规范已经在2003年6月12日获得通过。
OMG已经与国际标准化组织ISO开展了合作,预示着UML将来有望成为ISO标准,值得关注。
初学者如何开始学习UML?
无外乎几种方式:读书、上网、实践、培训。
最近几年国内一窝蜂地出版了不少与UML有关的中英文书籍,但说实话,有点良莠不齐。初学者不管自学还是参加培训,选择合适的UML教材/读物是很重要的。
Craig Larman的《UML和模式应用》是一本非常好的内容丰富、真正实用的入门教材,在国际上也是用得最多的一本,无出其右者。纵观全书,以实案为中心,脉络清晰,组织老到,深浅适当,循循善诱,非常适合UML、UP、设计模式的初学者和一直对OOAD、UML的价值存有疑虑的结构化人士一读。对于熟悉OO的人来说,阅读此书也是再一次享受梳理知识、进行系统性训练的美妙体验。本人尤其推荐第2版(内容更新了不少,听说正在引进翻译,值得期待)。
如果希望与大师对话,全面深入地掌握UML的基本要领,通过领悟UML设计者的思想和意图来达到在实战中得心应手运用OO建模技术的目的,建议一定要阅读UML之父Grady Booch亲自撰写的《UML用户指南》。本书相当全面,偏重理论分析和概念阐释,这些内容和抽象技术对于真正理解UML是非常基本、必不可少的,所以它适合喜欢认真探究一切的读者。
用好UML离不开有好的过程作指导。RUP极其丰富的内容令人生畏,Ivar Jacobson大师在《统一软件开发过程》一书中从管理者和系统架构师的角度,通过实例分析系统地讲解了将UML用于分析设计实践的完整过程,深入浅出,言简意赅,可以说此书正是RUP的精华所在。带领自己的团队用好UML,得此书足矣。
此外,IT之源(http://www.iturls.com/)、UMLChina(http://www.umlchina.com/)等网站提供了非常丰富的学习资料和参考文章。
UML规范、《UML参考手册》内容深、篇幅大,主要面向UML工具开发者、专家和研究人员,不适合初学者阅读。
世界上有哪些著名的公司、组织参与了UML标准的制订?
历年来,参与UML标准制订的一些核心公司和组织包括(1.x、2.0):
全能型IT公司:HP, IBM, Unisys
大型软件公司:CA, Microsoft, Oracle
CASE厂商:I-Logix, Rational(已被IBM收购), Telelogic
电信供应商:Alcatel,Ericsson,Fujitsu,Motorola
行业组织:OMG
IT系统集成商:EDS
等等
UML在业界的影响力和地位由此可见一斑。
OpenOffice运行更快的技巧
首先,启动 OpenOffice.org Writer,在设置后,其他的 Calc、Impress、Draw 等组件也会生效。接着,执行"工具->选项"菜单命令,选择左边的"内存",设置要点如下:
在"撤销"选项,步骤数目建议设置为 20 或 30,总之应小于 100。
在"图形缓存"选项,OpenOffice.org 使用设置为 128MB,每个对象的内存设置为 20MB。
在"插入对象缓存"选项,设置对象数为 20。
选中"启用快速启动"选项。
然后,再选择左边的"Java",取消选择"使用 Java 运行环境"选项。最后点击"确定"保存你的设置。
现在你可以关闭 OpenOffice.org 并再次启动它以感受速度的变化了。
标准的makefile写法
(注意每一行的前面若有空格,是tab,不是space)
#Source file
SRC = ThreadQueue.cpp
#Object file
OBJ = $(SRC:.cpp=.o)
#Output execution file
PROGRAM = ThreadQueue
#Compiler
CC = g++
#Include
INCLUDE = -I/usr/include/ncurses -I/usr/include/
#Linker Parameter
LINKPARAM = -lpthread -lncurses
#Options for development
#CFLAGS = -ansi -g -Wall
#Options for release
CFLAGS = -ansi -O -Wall
all: $(PROGRAM)
$(PROGRAM): $(OBJ)
$(CC) -o $(PROGRAM) $(LINKPARAM) $(OBJ)
.SUFFIXES : .cpp
.cpp.o:
$(CC) $(INCLUDE) $(CFLAGS) -c $<
clean:
rm *.o
如果编译时出现了Makefile:8: *** 遗漏分隔符 停止的错误
原因是在编写makefile文件时:
gcc前的是tab分隔符,不能用空格,
make中规定每一Shell命令之前的开头必须使用
郑弘仪「教你投资脑袋致富八招」
1 乐于接近人们,学习新事物
2 大量阅读,成为杂食性阅读动物
3 每个月存3000 元,适当投资经营,退休将有六千万.(注:此处为台币,1元人民币大约等于4元新台币)
4 提早社会化,建立正确价值观
5 做自己擅长、喜欢的事,只做能力可负担的
6 不要太有自己的人际门坎,每个人都有值得学习的地方
7 如果没空研究股票,就别投资
8 学习阅读财务报表,收集信息、学习判断
2008年10月21日星期二
Verilog & Testbench学习笔记1
自动比较的原理就是用计算机代替人去比较实际响应和期望相应是否相同,然后把相关信息显示出来,从而减少了人工的干预,提高了验证效率。
实现方法:利用时钟同步验证,or经过一定数据包再异步比较验证。
2. 增强testbench的重用性,避免因为重新开发而带来的风险和额外的开发时间。
3. reg类型定义了一种暂存数据的变量,但其物理模型并不一定是一个寄存器,也可能仅是一根连线:只有在时序逻辑中它对应的才是寄存器;而在组合逻辑中,它则表示一根连线。
在实际应用中,凡是在initial(initial是行为级语法,不可综合)、always块中的被赋值的信号都必须定义成reg。
4. wire与reg的差异? 什么时候该用wire?什么时候又该用reg?
若参数出现在begin...end之内,则须使用reg;
使用wire,须搭配assign;
若wire和reg用错,compiler都会提醒,所以不必太担心。
需要注意:在Verilog中使用reg,并不表示综合后就是暂存器(register)。若在组合电路中使用reg,综合后仍只是net,唯有在时序电路中使用reg,合成后才会以flip-flop形式表示成register。
5. Verilog中出现小数,Modelsim编译时不会报错or警告,虽然小数是不可综合的。而且利用小数进行比较的时候,如cnt <>
linux下查找字符串命令
2. 查找并删除当前目录下小文件:
find . -type f -size -10k -exec rm {} \;
说明:
-type f 查找文件
-size -10k,
小于10k的。"+"是表示要求系统只列出大于指定大小的文件,而使用"-"则表示要求系统列出小于指定大小的文件。
3. 遍历文件夹grep一个字符串
find . -name "*c" | xargs grep "strings"
在当前文件夹下所有c文件中查找字符串“string”
4. 在某目录下文件中查找某字符串
grep -r youcode dir
例如:查找home下文件中查找hello
grep -r hello /home
例如:在当前所有目录下文件中查找hello,不区分大小写
grep -ir hello .
(转)Qt简介
公司出品,目前包括Qt, 基于 Framebuffer 的 Qt Embedded,快速开发工具 Qt
Designer,国际化工具 Qt Linguist 等部分 Qt 支持所有 Unix 系统,当然也包括
Linux,还支持 WinNT/Win2k,Win95/98 平台。
Trolltech 公司在 1994 年成立,但是在 1992 年,成立 Trolltech
公司的那批程序员 就已经开始设计 Qt 了,Qt 的第一个商业版本于 1995
年推出然后 Qt 的发展就很快了,下面是 Qt 发展史上的一 些里程碑:
1996 Oct KDE 组织成立
1998 Apr 05 Trolltech 的程序员在 5 天之内将 Netscape5.0 从 Motif 移植到 Qt
上
1998 Apr 08 KDE Free Qt 基金会成立
1998 Jul 09 Qt 1.40 发布
1998 Jul 12 KDE 1.0 发布
1999 Mar 04 QPL 1.0 发布
1999 Mar 12 Qt 1.44 发布
1999 Jun 25 Qt 2.0 发布
1999 Sep 13 KDE 1.1.2 发布
2000 Mar 20 嵌入式 Qt 发布
2000 Sep 06 Qt 2.2 发布
2000 Oct 05 Qt 2.2.1 发布
2000 Oct 30 Qt/Embedded 开始使用 GPL 宣言
2000 Sep 04 Qt free edition 开始使用 GPL
基本上,Qt 同 X Window 上的 Motif,Openwin,GTK 等图形界 面库和
Windows 平台上的 MFC,OWL,VCL,ATL 是同类型的东西,但是 Qt 具有下列优点:
优良的跨平台特性:
Qt支持下列操作系统: Microsoft Windows 95/98, Microsoft Windows NT,
Linux, Solaris, SunOS, HP-UX, Digital UNIX (OSF/1, Tru64), Irix,
FreeBSD, BSD/OS, SCO, AIX, OS390,QNX 等等。
面向对象
Qt 的良好封装机制使得 Qt
的模块化程度非常高,可重用性较好,对于用户开发来说是非常 方便的。 Qt
提供了一种称为 signals/slots 的安全类型来替代 callback,这使得各个元件
之间的协同工作变得十分简单。
丰富的 API
Qt 包括多达 250 个以上的 C++ 类,还替供基于模板的 collections,
serialization, file, I/O device, directory management, date/time
类。甚至还包括正则表达式的处理 功能。
支持 2D/3D 图形渲染,支持 OpenGL
大量的开发文档
XML 支持
但是真正使得 Qt 在自由软件界的众多 Widgets (如
Lesstif,Gtk,EZWGL,Xforms,fltk 等等)中脱颖而出的还是基于 Qt
的重量级软件 KDE 。 有趣的是,KDE 也是使得 Trolltech
公司承受巨大压力的一个原因。下面我们将来看看这场 著名的自由软件圣战 --
"KDE/QT .VS. Gnome/Gtk" 是怎么发生的。
在 Unix 的图形界面一向是以 MIT 的 X Window 系统为标准,
可是在商业应用上有两大流派,一派是以 Sun 公司领导的 Openlook 阵营,一派是
IBM/HP 领导的OSF (Open Software Foundation) 的 Motif,
双方经过多年竞争之后, Motif 最终 胜出,成为最普遍使用的界面库,
后来双方又妥协出一个 CDE(Common Desktop Enviroment)
作为一个标准的图形界面。 但是 Motif/CDER 的价格非常昂贵,在这同时微软的
Windows 图 形界面发展速度非常快,而 Unix 界的后起之秀 Linux
也急需一个可靠并且免费的图形界面。
1996 年 10 月,由开发图形排版工具Lyx的德国人 Matthias Ettrich 发起了
KDE 计划。 KDE 的全称为 K Desktop Environment,可以看出是针对 CDE。 KDE
本身是采用 GPL 宣言的,但是 KDE 却是使用 Qt 来作为其底层库,因为当时 Qt
已经将其 Unix 版 本自由发布了,但是 Qt 并不遵循 GPL, 因此 KDE
被很多自由软件的作者攻击,认为利用非自由软件开发违背了 GPL 的精神,于是
GNU 的狂热信徒兵分两路,一路是去制作 Harmonny,试 图重写一套兼容于 Qt
的替代品,另一路是由一个 26 岁的墨西哥程序员 Miguel De Icaza
领导下重新开发一套叫 GNOME(GNU Network Object Enviroment)来替代 KDE。
由于 Linux 界的老大 RedHat 不喜欢 KDE/Qt 的版权,因此 RedHat
甚至专门派出了几个全职程序员来加入 GNOME 进行开发工作,于是一场同 Motif VS
Openlook 相 似的圣战就这么打起来了。 Trolltech 为了 KDE 曾数次修改 Qt
的版权,从成立 KDE Free Qt 基金会到采用 QPL,可谓是费尽心机,但是 GNOME
采用的 GTK 一开始就是完全的 GPL,因此在这个方面 GNOME 有一定的优势,加上
Qt/KDE 采用 C++ 开发,入门的门槛比较高,而 GTK/Gnome 采用 C, 因此 GNOME
吸引了更多的自由软件开发者,但是 KDE 毕竟先走了一步, 推出的 KDE1.1.2
十分稳定, 而当时急忙中推出的 GNOME1.0 的系统稳定性奇差,有人甚至笑称
GNOME1.0 还没有 KDE 1.0 Alpha 稳定。但是 GNOME
后来发展比较快,大有迎头赶上的势头。 当时双方的开发者在网络
上炒得天翻地覆,连 Linux 之父 Linus 只是说了一句喜欢用 KDE 都倍受指责。
战争到了第三个年头,也就是2000年,可谓是风云突变,一个接
一个重大的事件先后发生: 首先是一批从 Apple 公司出来的工程师成立了一个叫
Eazel 的公司替 GNOME 设计界面,然后是一批 GNOME 程序员成立了一个 Helix
Code 公司替 GNOME 提供商业支持,而大家期待以久的 KDE 2.0
也终于发布了,这恐怕是目前最为庞大的自由软件了之一, 除了 KDE 本身,还包括
Koffice 套件,和集成开发环境 Kdevelop 等等大批软件,其 主力软件 Kounqueror
也是第一个可以同微软的 Internet Exploer 相抗衡的浏览器。 而 Sun 公司,Red
Hat 公司, Eazel 公司,Helix Code 等一批公司成立了一个GNOME 基金会, Sun
还宣布将把重量级办公软件 Star office 同 GNOME 集成, Trolltech
公司自然不能坐以待毙,于2000年 10 月 4 日将 Qt 的 free edition 变为 GPL
宣言,彻底解决了 KDE 的版权问题, 又推出了嵌入式 Qt ,给了 GNOME
阵营一个有力的回击。
到现在为止,这场战争还在继续, 相信我们不能很快看到结果。一般说来,
目前 GNOME 吸引的公司比较多,但是 KDE/Qt 的开发的效率和质量比 GNOME
高,而且在 Office/嵌入式 环境中先走一步,在一定时间内还将处于优势地位。
那么对于用户来说,如何在 Qt/GTK 中作出选择呢?一般来说,如果用户使用
C++,对库的稳定性,健壮性要求比较高,并且希望跨平台开发的话,那么使用 Qt
是较好的选择, 但是值得注意的是,虽然 Qt 的 Free Edition 采用了 GPL
宣言,但是如果你开发 Windows 上的 Qt 软件或者是 Unix
上的商业软件,还是需要向 Trolltech 公司支付版权费用的。
Linux启动过程步骤
二.Kernel引导入口
三.核心数据结构初始化--内核引导第一部分
四.外设初始化--内核引导第二部分
五.init进程和inittab引导指令
六.rc启动脚本
七.getty和login
八.bash
(转)深入理解switch-case语句
作者: 谢煜波
原地址:http://blog.csdn.net/xiaohan13916830/archive/2004/08/17/76724.aspx
前段时间在论坛上看见台湾李维在<<Borland传奇>>一书中对windows编程模式中,消息处理部分有如下的一些分析:
他说,在消息处理循环中,一般的形式是这样的
MSG msg ;
switch( msg ){
case WM_XXXXXXX :
....
case WM_XXXXXXX :
....
case WM_XXXXXXX :
....
} ;
李维说,这种模式是很低效的,因应经过汇编后,这种C代码会产生如下的汇编代码
cmp .... .....
jnz .... .....
cmp .... .....
jnz .... .....
cmp .... .....
jnz .... .....
如果你的 case
足够多,比如,你有一万条消息需要处理,而不幸的是你把一条最常用的消息
放在了最后一位,那么当这条消息要得到处理,会首先经过一万次的cmp与jnz,
李维认为,这
是非常非常低效的,实在是低效的忍无可忍,无需再忍~~:P
在起初,我也是这样认为的,但近来的阅读及实验却发现,这种看法非常片面,今天就来谈谈这个问题(
所有实验在 linux 平台下完成 )
首先看一到用 c 编写的程序
/* -------------------- filename : ta.c --------------- */
int switch_test_first( int x )
{
int res ;
switch( x ){
case 100 :
res = 1 ;
break ;
case 102 :
res = 2 ;
break ;
case 103 :
res = 3 ;
break ;
}
return res ;
}
然后,我们用 gcc 将它编译成汇编文件( 使用 -S 开关 )
gcc -S ta.c
将得到如下的汇编文件( ta.s )
.file "ta.c"
.text
.globl switch_test_first
.type switch_test_first,@function
switch_test_first:
pushl %ebp
movl %esp, %ebp
subl $8, %esp
movl 8(%ebp), %eax
.file "ta.c"
.text
.globl switch_test_first
.type switch_test_first,@function
switch_test_first:
pushl %ebp
movl %esp, %ebp
subl $8, %esp
movl 8(%ebp), %eax
movl %eax, -8(%ebp)
cmpl $102, -8(%ebp) // 1
je .L4 // 2
cmpl $102, -8(%ebp) // 3
jg .L8 // 4
cmpl $100, -8(%ebp) // 5
je .L3 // 6
jmp .L2 // 7
.L8:
cmpl $103, -8(%ebp)
je .L5
jmp .L2
.L3:
movl $1, -4(%ebp)
jmp .L2
.L4:
movl $2, -4(%ebp)
jmp .L2
.L5:
movl $3, -4(%ebp)
.L2:
movl -4(%ebp), %eax
leave
ret
.Lfe1:
.size switch_test_first,.Lfe1-switch_test_first
.ident "GCC: (GNU) 3.2.2 20030222 (Red Hat Linux 3.2.2-5)"
注意看文件中 // 1 ~ // 7
的部份,从这个部份,我们可以看出,gcc确实是把一些case语句转成了李维所说的那种方式进行处理,我们看见了代码中存在有众多的
cmpl 与 jmp 语句
这就相当于你使用if..else..一样,但是否总是这样呢?
我们下面改动一下 ta.c 这个文件,在里面再多加一些 case 语句
/* -------------- filename : new_ta.c ------------------- */
int switch_test_first( int x )
{
int res ;
switch( x ){
case 100 :
res = 1 ;
break ;
case 102 :
res = 2 ;
break ;
case 103 :
res = 3 ;
break ;
case 104 :
res = 4 ;
break ;
case 105 :
res = 5 ;
break ;
case 106 :
res = 6 ;
break ;
}
return res ;
}
这个 new_ta.c 与原来的 ta.c 在结构上完全相同,唯一不同的就是 case
语句的数量变多了,下面我们来编译一下这个文件
gcc -S new_ta.c
下面是我们产生的更新的汇编文件
.file "new_ta.c"
.text
.globl switch_test_first
.type switch_test_first,@function
switch_test_first:
pushl %ebp
movl %esp, %ebp
subl $8, %esp
movl 8(%ebp), %eax
subl $100, %eax
movl %eax, -8(%ebp)
cmpl $6, -8(%ebp)
ja .L2
movl -8(%ebp), %edx
movl .L9(,%edx,4), %eax
jmp *%eax
.section .rodata
.align 4
.align 4
.L9: // A
.long .L3
.long .L2
.long .L4
.long .L5
.long .L6
.long .L7
.long .L8
.text
.L3: // 1
movl $1, -4(%ebp)
jmp .L2
.L4: // 2
movl $2, -4(%ebp)
jmp .L2
.L5: // 3
movl $3, -4(%ebp)
jmp .L2 // 4
.L6:
movl $4, -4(%ebp)
jmp .L2 // 5
.L7:
movl $5, -4(%ebp) // 6
jmp .L2
.L8: // 7
movl $6, -4(%ebp)
.L2:
movl -4(%ebp), %eax
leave
ret
.Lfe1:
.size switch_test_first,.Lfe1-switch_test_first
.ident "GCC: (GNU) 3.2.2 20030222 (Red Hat Linux 3.2.2-5)"
仔细比较一下这个最新的 new_ta.s 与前面的 ta.s,精华全在里面了!
首先 new_ta.s 比前面的 ta.s 多了一个 .L9 部分,而且它的 // 1 ~ // 7
中没有了前面
ta.s 文件中所存在的众多的 cmpl 与 jmp
语句,那么,现在这样的代码又是怎么实现
switch 语句中的跳转的呢?我们来仔细分析一下它新多出来的 .L9 部份。
.section .rodata
.align 4
.align 4
.L9:
.long .L3
.long .L2
.long .L4
.long .L5
.long .L6
.long .L7
.long .L8
.text
显而易见,.L9
部份是一个我们最常见的数据结构——表,它的每一项都是一个标号,而这个标号,恰恰是每个
case 语句的入口标号!
这很容易让我们想到,它很可能是用了一张表来存放所有的 case
语句的入口,然后,在
执行 switch 语句的时候就从这个表中直接检出相应的 case
语句的入口地址,然后跳转
到相应的 case
语句去执行,就像hash_table似的。具体是不是这样呢?我们看看进入
switch 部份的代码:
pushl %ebp
movl %esp, %ebp
subl $8, %esp
movl 8(%ebp), %eax
subl $100, %eax
movl %eax, -8(%ebp)
cmpl $6, -8(%ebp)
ja .L2
movl -8(%ebp), %edx
movl .L9(,%edx,4), %eax // 1
jmp *%eax // 2
果然如此!首先在 // 1
处根据%edp的值(其值相当于表的下标)在.L9的表中找到相应
case 语句的入口地址,并把这个地址存到%eax中,然后通过 // 2
(这是一个间接跳转
语句)转到%eax存放的地址中,也即相应的case语句处。
C编译器,果然聪明!
通过这个分析我们可以知道如下两点:
1. 当 case 语句少的时候,C编译器将其转成 if..else..
类型进行处理,运用较多的
cmp 与 jmp 语句 ,而当 case
语句较多的时候,C编译器会出成一个跳转表,而直
接通过跳转表进行跳转,这让 switch 具有非常高的效律,而且效律几乎不会因为
case 语句的增长而减小,李维所担忧的问题是完全不会发生的
2. 可以问答下面几个问题:
1. 为什么 case 语句中需要的是整数类型而不能是其余的类型?
这是因为,case
语句中的这个值是用来做跳转表的下标的,因此,当然必须是整数
2. 为什么 case 语句在不加break的时候具有直通性?
这是因为跳转是在进入 switch
是计算出的,而不是在case语句中计算出的,整个
case
语句群就是一块完整而连续的代码,只是switch让其从不同的位置开始执行。
上面的内容,在《Computer Systems A Programmer's
Perspective》中有很详细的论述,
感兴趣可以去找来仔细看看~~~
既然,case 语句需要的是整数的常量值,那么我们是否可用 const
类型呢?比如下面
一段代码:
const int c_1 = 100 ;
const int c_2 = 102 ;
void test( int x )
{
switch( x ){
case c_1 :
++x ;
case c_2 :
--x ;
}
}
这段代码,用 c 编译器编译,编译器会提示错误,但在 c++
编译器中却不会,这主要是由于 c , 与 c++ 编译器对 const
这个东东的处理不同。我们来看看下面一段 c 程序
/*------------- filename : const_c.c -----------*/
const int a = 15 ;
void f( int x )
{
x = a ;
}
同样用 gcc 编译
gcc -S const_c.c
然后,来看看它的汇编文件
.file "const_c.c"
.globl a
.section .rodata
.align 4
.type a,@object
.size a,4
a: // 1
.long 15
.text
.globl f
.type f,@function
f:
pushl %ebp
movl %esp, %ebp
movl a, %eax // 2
movl %eax, 8(%ebp)
leave
ret
.Lfe1:
.size f,.Lfe1-f
.ident "GCC: (GNU) 3.2.2 20030222 (Red Hat Linux 3.2.2-5)"
注意 // 1 处,C 编译器为 a 分配了地址,并把它的值设为 15 ,而在 // 2
处,它是将
a 这个地址中的值赋给了 %eax,这同一般的普通变量而非const 变量赋值没什么两样
下面我们用 c++ 编译器来编译这段代码,它产生的汇编文件如下:
.file "const_cpp.cpp"
.text
.align 2
.globl _Z1fi
.type _Z1fi,@function
_Z1fi:
.LFB2:
pushl %ebp
.LCFI0:
movl %esp, %ebp
.LCFI1:
movl $15, 8(%ebp) // 1
leave
ret
.LFE2:
.Lfe1:
.size _Z1fi,.Lfe1-_Z1fi
.section .rodata
.align 4
.type a,@object
.size a,4
a:
.long 15
.ident "GCC: (GNU) 3.2.2 20030222 (Red Hat Linux 3.2.2-5)"
同样注意// 1 处,它以经把 a 的值用 15 来取代了,
也就是说,在c中const变量的行为更像一个非const变量,而在cpp中,const变量的行为就像是#define
由于 c++ 中,const 变量的值是在编译时就计算出来的,因此,它可以用在 case
语句中,而 c 中,const值在编译时只是一个变量的地址,因此,它无法用在 case
语句中.
-----------------------------------------------------------------------------
参考文献:<<Computer Systems A Programmer's Perspective>>
利用auto工具来快速生成Makefile
C程序开发,免不了用Makefile来编译,Makefile的规则定义学习容易,一段时间不用,难免会忘。现在有Automake工具,自动化生成Makefile,用好了那是事半功倍,相当的有效率。
下面将介绍 如何利用 GNU Autoconf 及 Automake 来协助『自动』产生
Makefile 文件,并且让开发出来的程序or软件,只要会 ``./configure'',
``make'', ``make install''
就可以安装到系统中。刚开始学,了解不多,介绍个例子,先掌握其流程:
1. 简介
Makefile 基本上就是『目标』(target), 『关连』(dependencies)
和『动作』三者所组成的一连串规则。而 make 就会根据 Makefile
的规则来决定如何编译 (compile) 和连结 (link) 。
Makefile
基本构造虽然简单,但是妥善运用这些规则就也可以变出许多不同的花招。却也因此,许多刚开始学习写
Makefile 时会感到没有规范可循,每个人写出来的 Makefile
长得都不太一样,不知道从何下手,而且只要环境变数不同或路径改一下,可能Makefile
就得跟着修改。虽然有 GNU Makefile Conventions (GNU Makefile 惯例)
订出一些使用 GNU 程序设计时撰写 Makefile
的一些标准和规范,但是内容很长而且很复杂,
并且经常做些调整,为了减轻软件开发者维护 Makefile
的负担,因此有了Automake。
软件编写人员只需写一些预先定义好的巨集 (macro),交给 Automake
处理后会产生一个可供Autoconf 使用的 Makefile.in 文件。再配合利用Autoconf
产生的自动设定文件 configure 即可产生一份符合 GNU Makefile惯例的 Makeifle
了。
2. 开始之前
在开始试着用 Automake 之前,请先确认你的系统已经安装以下的软件:
1. GNU Automake
2. GNU Autoconf
3. GNU m4
4. perl
5. GNU Libtool (生成shared library时需要)
3. 一个简单的例子
Automake 所产生的 Makefile
除了可以做到程序的编译和连结,也已经把如何产生程序文件(如 manual page,
info 文件及 dvi 文件)
的动作,还有把原始程序包装起来以供散的动作都考虑进去了,所以原始程序所存放的目录架构最好符合
GNU 的标准惯例,接下来我拿hello.c 来做为例子。
在工作目录下建立一个新的子目录 ``devel'',再在 devel
下建立一个``hello'' 的子目录,这个目录将作为我们存放 hello
这个程序及其相关文件的地方:
% mkdir devel
% cd devel
% mkdir hello
% cd hello
用编辑器写个 hello.c 文件,
#include stdio.h
int main(int argc, char** argv)
{
printf(``Hello, Slitaz! \n'');
return 0;
}
接下来就要用 Autoconf 及 Automake 来帮我们产生 Makefile 了,
1. 用 autoscan 产生一个 configure.in 的雏型,执行 autoscan
后会产生一个configure.scan 的文件,我们可以用它做为configure.in文件的蓝本。
% autoscan
% ls
configure.scan hello.c
2. 编辑 configure.scan 文件,如下所示,并且把它的文件名改成configure.in
#Process this file with autoconf to produce a con figure script.
AC_INIT(hello.c)
AM_INIT_AUTOMAKE(hello, 1.0)
# Checks for programs.
AC_PROG_CC
# Checks for libraries.
# Checks for header files.
# Checks for typedefs, structures, and compiler ch aracteristics.
# Checks for library functions.
AC_OUTPUT(Makefile)
3. 执行 aclocal 和 autoconf ,分别会产生 aclocal.m4 及 configure
两个文件
% aclocal
% autoconf
% ls
aclocal.m4 configure configure.in hello.c
4. 编辑 Makefile.am 文件,内容如下
AUTOMAKE_OPTIONS= foreign
bin_PROGRAMS= hello
hello_SOURCES= hello.c
5. 执行 automake --add-missing ,Automake 会根据 Makefile.am
文件产生一些文件,包含最重要的 Makefile.in
% automake --add-missing
automake: configure.in: installing `./install-sh'
automake: configure.in: installing `./mkinstalldirs'
automake: configure.in: installing `./missing'
6. 最后执行 ./configure ,
% ./configure
creating cache ./config.cache
checking for a BSD compatible install... /usr/bin/in stall -c
checking whether build environment is sane... yes
checking whether make sets ${MAKE}... yes
checking for working aclocal... found
checking for working autoconf... found
checking for working automake... found
checking for working autoheader... found
checking for working makeinfo... found
checking for gcc... gcc
checking whether the C compiler (gcc ) works... yes
checking whether the C compiler (gcc ) is a cross-co mpiler... no
checking whether we are using GNU C... yes
checking whether gcc accepts -g... yes
updating cache ./config.cache
creating ./config.status
creating Makefile
现在你的目录下已经产生了一个 Makefile 文件,下个 ``make''
指令就可以开始编译 hello.c 成执行文件,执行 ./hello 和 GNU 打声招呼吧!
% make
gcc -DPACKAGE="hello" -DVERSION="1.0" -I. -I. -g -O2 -c he llo.c
gcc -g -O2 -o hello hello.o
% ./hello
Hello,Slitaz!
你还可以试试 ``make clean'',''make install'',''make dist''
看看会有什么结果。
4. 一探究竟
上述产生 Makefile
的过程和以往自行编写的方式非常不一样,舍弃传统自行定义 make 的规则,使用
Automake 只需用到一些已经定义好的巨集即可。我们把巨集及目标 (target)写在
Makefile.am 文件内,Automake读入 Makefile.am
文件后会把这一串已经定义好的巨集展开并且产生对应的Makefile.in 文件,
然后再由 configure 这个 shell script 根据Makefile.in 产生适合的 Makefile。
在上面例子中可借由 Autoconf 及 Automake 工具所产生的文件有
configure.scan、aclocal.m4、configure、Makefile.in,需要我们加入设定者为
configure.in 及 Makefile.am。
4.1 编辑 configure.in 文件
Autoconf 是用来产生 'configure' 文件的工具。'configure' 是一个shell
script,它可以自动设定原始程序以符合各种不同平台上 Unix
系统的特性,并且根据系统叁数及环境产生合适的 Makefile 文件或是C 的标头文件
(header
file),让原始程序可以很方便地在这些不同的平台上被编译出来。Autoconf 会读取
configure.in 文件然后产生 'configure' 这个shell script。
configure.in 文件的内容是一连串 GNU m4 的巨集,这些巨集经过autoconf
处理后会变成检查系统特徵的 shell script。configure.in
内巨集的顺序并没有特别的规定,但是每一个 configure.in
文件必须在所有巨集前加入 AC_INIT 巨集,然后在所有巨集的最后面加上
AC_OUTPUT 巨集。我们可先用 autoscan 扫描原始文件以产生一个 configure.scan
文件,再对 configure.scan 做些修改成 configure.in
文件。在范例中所用到的巨集如下:
#
这个巨集后面的字不会被处理,可视为注解。
AC_INIT(FILE)
这个巨集用来检查原始码所在的路径,autoscan 会自动产生,我们不必修改它。
AM_INIT_AUTOMAKE(PACKAGE,VERSION)
这是使用 Automake 所必备的巨集,PACKAGE
是我们所要产生软件套件的名称,VERSION 是版本编号。
AC_PROG_CC
检查系统可用的 C 编译器,如果原始程序是用 C 写的就需要这个巨集。
AC_OUTPUT(FILE)
设定 configure 所要产生的文件,如果是 Makefile 的话,configure
便会把它检查出来的结果带入 Makefile.in 文件然后产生合适的 Makefile。
实际上,我们使用 Automake
时,还须要一些其它的巨集,这些额外的巨集我们用 aclocal来帮我们产生。执行
aclocal 会产生 aclocal.m4文件,如果没有特别的用途,我们可以不必修改它,用
aclocal 所产生的巨集会告诉 Automake 怎么做。
有了 configure.in 及 aclocal.m4 两个文件后,便可以执行 autoconf来产生
configure文件了。
4.2 编辑 Makefile.am 文件
接下来我们要编辑 Makefile.am 文件,Automake 会根据 configure.in
中的巨集把Makefile.am 转成 Makefile.in 文件。Makefile.am
文件定义我们所要产的目标:
AUTOMAKE_OPTIONS
设定 automake 的选项。Automake 主要是帮助开发 GNU
软件的人员维护软件套件,所以在执行 automake 时,会检查目录下是否存在标准
GNU 软件套件中应具备的文件文件,例如 'NEWS'、'AUTHOR'、'ChangeLog'
等文件文件。设成 foreign 时,automake 会改用一般软件套件的标准来检查。
bin_PROGRAMS
定义我们所要产生的执行文件文件名。如果要产生多个执行文件,每个文件名用空白字元隔开。
hello_SOURCES
定义 'hello' 这个执行文件所需要的原始文件。如果 'hello'
这个程序是由多个原始文件所产生,必须把它所用到的原始文件都列出来,以空白字元隔开。假设
'hello' 这个程序需要 'hello.c'、'main.c'、
'hello.h'
三个文件的话,则定义
hello_SOURCES= hello.c main.c hello.h
如果我们定义多个执行文件,则对每个执行文件都要定义相对的filename_SOURCES。
编辑好 Makefile.am 文件,就可以用 automake --add-missing
来产生Makefile.in。加上 --add-missing 选项是告诉 automake
顺便帮我们加入包装一个软件套件所必备的文件。Automake 产生出来的
Makefile.in文件是完全符合 GNU Makefile 的惯例,我们只要执行 configure
这个shell script 便可以产生合适的 Makefile 文件了。
4.3 使用 Makefile
利用 configure 所产生的 Makefile
文件有几个预设的目标可供使用,我们只拿其中几个简述如下:
make all
产生我们设定的目标,即此范例中的执行文件。只打 make
也可以,此时会开始编译原始码,然后连结,并且产生执行文件。
make clean
清除之前所编译的执行文件及目的文件 (object file, *.o)。
make distclean
除了清除执行文件和目的文件外,也把 configure 所产生的
Makefile也清除掉。
make install
将程序安装至系统中。如果原始码编译无误,且执行结果正确,便可以把程序安装至系统预设的执行文件存放路径。如果我们用bin_PROGRAMS
巨集的话,程序会被安装至 /usr/local/bin 这个目录。
make dist
将程序和相关的文件包装成一个压缩文件以供散播 (distribution)
。执行完在目录下会产生一个以 PACKAGE-VERSION.tar.gz 为名称的文件。PACKAGE
和 VERSION 这两个变数是根据 configure.in 文件中AM_INIT_AUTOMAKE(PACKAGE,
VERSION) 的定义。在此范例中会产生 'hello-1.0.tar.gz' 的文件。
make distcheck
和 make dist
类似,但是加入检查包装后的压缩文件是否正常。这个目标除了把程序和相关文件包装成
tar.gz 文件外,还会自动把这个压缩文件解开,执行 configure,并且进行 make
all 的动作,确认编译无误后,会显示这个 tar.gz
文件已经准备好可供散播了。这个检查非常有用,检查过关的套件,基本上可以给任何一个具备
GNU 发展境的人去重新编译。就 hello-1.tar.gz 这个范例而言,除了在 Red Hat
linux 上,在 FreeBSD 2.2.x 版也可以正确地重新编译。
要注意的是,利用 Autoconf 及 Automake
所产生出来的软件套件是可以在没有安装 Autoconf 及 Automake
的环境上使用的,因为 configure 是一个 shell script,它己被设计可以在一般
Unix 的 sh 这个 shell 下执行。但是如果要修改 configure.in 及 Makefile.am
文件再产生新的configure 及 Makefile.in 文件时就一定要有 Autoconf 及
Automake 了。
5. 说明
Autoconf 和 Automake 功能十分强大,你可以从它们所附的 info
文件找到详细的用法。你也可以从许多现存的 GNU 软件或 Open Source
软件中找到相关的 configure.in 或 Makefile.am 文件,它们是学习 Autoconf
及Automake 更多技巧的最佳范例。
本文只用到了 Autoconf 及 Automake
的皮毛罢了,希望这篇文件能帮助你对产生
Makefile有个简单的依据。其它有关开发 GNU程序或 C 程序设计及 Makefile
的详细运用及技巧,请参阅http://www.gnu.org上的说明文档。
有了Autoconf 及 Automake 的辅助,产生一个 Makefile
似乎不再像以前那么困难了,而使用 Autoconf 也使得我们在不同平台上或各家
Unix之间散播及编译程序变得简单,这对于在 Unix
/linux系统上开发程序的人员来说减轻了许多负担。妥善运用这些 GNU
的工具软件,可以帮助我们更容易去发展程序,而且更容易维护原始程序码。
输入法生效
./configure
make
make install
如果有特殊的安装需要,可以先
./configure --help
看看你需要改什么预定的值,一般来说安装过后可以用make
clean来清除编译过程中产生的临时文件。至于卸载的话,可以查查这个软件都有哪些文件,直接删除就是了,有的软件可以用make
uninstall来卸载。
安装完毕后重启,再对其进行设置
注意:输入法要正常运行,有三个条件,locale,XMODIFIERS和中文字体。locale是使用中文的基本条件,没有locale,就不能使用中文。XMODIFIERS是输入法和应用程序对话的通道,应用程序通过这个变量,来知道应该与哪个输入法(XIM)对话。
把隐藏文件夹给开起来….
如果你在安装系统的时候,选了简体中文,那么locale就已经设置好了,在你的用户目录下,找到.bashrc文件(是隐藏文件,所以前面有一个"."),在这个文件最后面加上:
export XMODIFIERS="@im=fcitx"
export XIM=fcitx
export XIM_PROGRAM=fcitx
C太厉害的Verilog会学不好
是很容易用C的思维去写Verilog而不自知,因为代码实在太像了。
Verilog只是语法跟C相似,但观念却是硬件电路的概念。
主要的区别有:
如以下的Verilog:
always@(posedge clk) begin
e <= a & b;
f <= c & d;
end
虽然看起来是 e <= a & b; 在 f <= c & d;前面,但实际e和f并没有先后之分,是并行的。
2. 硬件要循序,要靠clock和FSM
或许你会说,『我的算法就是要循序一步一步的做,如C语言那样,那怎么办?』,若Verilog要这样,就得靠clock并且搭配FSM,当一个state完成后,进入下一个state,这样就能依照clock的进行,而达成循序的要求。
3.Verilog程序代码没有先后之分
除了blocking assignment有先后执行顺序,而nonblocking assignment同时执行外,Verilog的程序没有前后顺序之分,所以才称为硬件『描述』语言,而非硬件『程序』语言,先写的不代表先执行,后写的也不代表后执行,只是代表硬件的架构的描述,也就是说,将原来的电路图,变成文字描述而已。
4.多用RTL Viewer和ModelSim观察自己写的code
Verilog写法小小的差异,合成出来的硬件就可能有天壤之别,多用RTL Viewer观察合成出来的硬件是否和自己预期的一样,并多用ModelSim观察跑出来的波形,这样会增加你对Verilog的掌握度。
Conclusion
很多人学了Verilog,还是把它当C语言写,事实上他们只是语法类似,但背后的思维并不一样,唯有『心中有硬件』,才能设计出好的电路。
正在读的书:
王钿、卓兴旺 2007,基于Verilog HDL的数字应用设计,国防工业出版社
modelsim的一些常用命令
vmap work work 映射
vlog div.v tdiv.v 编译verilog写的代码
vcom div.vhl tdiv.vhl 编译vhdl 写的代码
vsim work.module 仿真相应模块
view wave/dataflow 显示波形窗口/数据流窗口
restart -f
run -all
quit -sim 退出仿真
pwd
cd
add wave /tdiv/* 把tdiv中的所有信号都加到wave波形图中
当然在modelsim中也可以用批处理文件,比如有一批处理文件file.do,则可用如下命令来调用它
do file.do
关于do文件的编写,有一个快速学习的好方法:
你执行的每一步鼠标点击or选择操作,在modelsim的命令行都有相应的代码输
出,你可以仿照写出正确的do文件。
(转)永远不要因为工作不好而辞职
永远不要因为这个工作不好而辞职。一定要因为另一个工作更好而辞职。
人所拥有的「最后的」(last)自由是,我们可以选择我们的态度。
有一位老人,独自住在家里。他的儿女轮流回来照顾他。后来觉得最好还是住到老人院去比较好,因为他的眼睛已经完全看不见了。
迁入老人院的那一天,服务员牵着他的手告诉他,房间的样子,墙上的壁画,窗户外面是一大片草地,还有水池,这位老人回答说,真的好美,我想我在这里会很开心。服务员瞪着他,一脸讶异的说,你什么都看不见,你怎么知道美不美呢?
讲到这里,你大概已经知道这故事想要说的是什么了?
我们比那位老人的情况好多了。我们每天早上起来的时候有没有这么振奋,这么积极?
办公室里的事好像永远都做不完。烦恼的事不知道为什么总是那么多。房子、车子、小孩的学业,今天的早饭该吃什么,这些事从未间断过,就待会儿出
门,从车子开出去到抵达停车场,至少会发三次火:有人换车道没打信号灯;某段路塞车因为有人在路边并排停车;再有就是乱按喇叭。
想到这里,怪不得我们真的要做一选择。选择今天我要找到美好的事,还是要专注于烦恼的事。我们要选择感恩、宽容,抑或是要让抱怨、愤怒来折磨我。我们甚至可以在今天选择关心他人,对他人感兴趣的机会,而不要让冷漠习惯性的在心头。
30年前,我对当时的工作非常不满,时常抱怨,也多次口头叫嚷要辞职。有一天一位其它部门的年长主管跟我说,永远不要因为这个工作不好而辞职。一定要因为另一个工作更好而辞职。
这二句话对我很重要。影响也很大。卅年后的今天,回想起来,他说的真的很有道理。
现在的公司制度不好,下一个工作机构的体制多半也有缺陷。现在的公司不公平,谁能保证新的公司一切都很合理公道。现在的公司有派系,天知道多少公司有同样的权力斗争问题。跟现在的主管处不好,新工作的主管就一定处得好吗?
因而换工作不是解决办法。
根本的办法是改变态度。曾在集中营里住过,遭受过人类最悲惨的折磨的奥地利心理学家Victor
Frankel就认为,人所拥有的「最后的」(last)自由是,我们可以选择我们的态度。遭遇同样的打击,有的人选择的是绝望,有的人却选择了希望。
朋友,你选择的是什么?你准备怎样过这一天?
2008年10月20日星期一
ARM,DSP,FPGA,CPLD,SOPC差别
HDL来编程,一般CPLD使用乘积项技术,粒度粗些;FPGA使用查找表技术,粒度细些,适用触发器较多的逻辑。其实多数时候都忽略它们的差异,
一般在设计ASIC芯片时要用FPGA验证,然后再把VHDL等程序映射为固定的版图,制作ASIC芯片,
在设计VHDL程序时,有可能要使用C仿真。
SOC就是单片系统,主要是器件太多设计复杂,成本高,可靠性差等缺点,所以单片系统是一个发展趋势。
SOPC就是可编程芯片系统,就是可以用FPGA/CPLD实现一个单片系统,譬如altera的Nios软核处理器嵌入到Stratix中。
●FPGA与CPLD的区别
系统的比较:
FPGA和CPLD都是可编程ASIC器件,有很多共同特点,但由于CPLD和FPGA结构上的差异,具有各自的特点:
①CPLD更适合完成各种算法和组合逻辑,FP
GA更适合于完成时序逻辑。换句话说,FPGA更适合于触发器丰富的结构,而CPLD更适合于触发器有限而乘积项丰富的结构。
②CPLD的连续式布线结构决定了它的时序延迟是均匀的和可预测的,而FPGA的分段式布线结构决定了其延迟的不可预测性。
③在编程上FPGA比CPLD具有更大的灵活性。CPLD通过修改具有固定内连电路的逻辑功能来编程,FPGA主要通过改变内部连线的布线来编程;FP
GA可在逻辑门下编程,而CPLD是在逻辑块下编程。
④FPGA的集成度比CPLD高,具有更复杂的布线结构和逻辑实现。
⑤CPLD比FPGA使用起来更方便。CPLD的编程采用E2PROM或FASTFLASH技术,无需外部存储器芯片,使用简单。而FPGA的编程信息需存放在外部存储器上,使用方法复杂。
⑥CPLD的速度比FPGA快,并且具有较大的时间可预测性。这是由于FPGA是门级编程,并且CLB之间采用分布式互联,而CPLD是逻辑块级编程,并且其逻辑块之间的互联是集总式的。
⑦
在编程方式上,CPLD主要是基于E2PROM或FLASH存储器编程,编程次数可达1万次,优点是系统断电时编程信息也不丢失。CPLD又可分为在编程
器上编程和在系统编程两类。FPGA大部分是基于SRAM编程,编程信息在系统断电时丢失,每次上电时,需从器件外部将编程数据重新写入SRAM中。其优
点是可以编程任意次,可在工作中快速编程,从而实现板级和系统级的动态配置。
⑧CPLD保密性好,FPGA保密性差。
⑨一般情况下,CPLD的功耗要比FPGA大,且集成度越高越明显。
随
著复杂可编程逻辑器件(CPLD)密度的提高,数字器件设计人员在进行大型设计时,既灵活又容易,而且产品可以很快进入市场。许多设计人员已经感受到
CPLD容易使用、时序可预测和速度高等优点,然而,在过去由于受到CPLD密度的限制,他们只好转向FPGA和ASIC。现在,设计人员可以体会到密度
高达数十万门的CPLD所带来的好处。
CPLD
结构在一个逻辑路径上采用1至16个乘积项,因而大型复杂设计的运行速度可以预测。因此,原有设计的运行可以预测,也很可靠,而且修改设计也很容易。
CPLD在本质上很灵活、时序简单、路由性能极好,用户可以改变他们的设计同时保持引脚输出不变。与FPGA相比,CPLD的I/O更多,尺寸更小。
如
今,通信系统使用很多标准,必须根据客户的需要配置设备以支持不同的标准。CPLD可让设备做出相应的调整以支持多种协议,并随著标准和协议的演变而改变
功能。这为系统设计人员带来很大的方便,因为在标准尚未完全成熟之前他们就可以著手进行硬件设计,然后再修改代码以满足最终标准的要求。CPLD的速度和
延迟特性比纯软件方案更好,它的NRE费用低於ASIC,更灵活,产品也可以更快入市。CPLD可编程方案的优点如下:
●逻辑和存储器资源丰富(Cypress Delta39K200的RAM超过480 Kb)
●带冗余路由资源的灵活时序模型
●改变引脚输出很灵活
●可以装在系统上后重新编程
●I/O数目多
●具有可保证性能的集成存储器控制逻辑
●提供单片CPLD和可编程PHY方案
由于有这些优点,设计建模成本低,可在设计过程的任一阶段添加设计或改变引脚输出,可以很快上市
CPLD的结构
CPLD是属於粗粒结构的可编程逻辑器件。它具有丰富的逻辑资源(即逻辑门与寄存器的比例高)和高度灵活的路由资源。CPLD的路由是连接在一起的,而FPGA的路由是分割开的。FPGA可能更灵活,但包括很多跳线,因此速度较CPLD慢。
CPLD以群阵列(array of
clusters)的形式排列,由水平和垂直路由通道连接起来。这些路由通道把信号送到器件的引脚上或者传进来,并且把CPLD内部的逻辑群连接起来。
CPLD之所以称作粗粒,是因为,与路由数量相比,逻辑群要大得到。CPLD的逻辑群比FPGA的基本单元大得多,因此FPGA是细粒的。
CPLD的功能块
CPLD最基本的单元是宏单元。一个宏单元包含一个寄存器(使用多达16个乘积项作为其输入)及其它有用特性。
因为每个宏单元用了16个乘积项,因此设计人员可部署大量的组合逻辑而不用增加额外的路径。这就是为何CPLD被认为是"逻辑丰富"型的。
宏单元以逻辑模块的形式排列(LB),每个逻辑模块由16个宏单元组成。宏单元执行一个AND操作,然后一个OR操作以实现组合逻辑。
每个逻辑群有8个逻辑模块,所有逻辑群都连接到同一个可编程互联矩阵。
每个群还包含两个单端口逻辑群存储器模块和一个多端口通道存储器模块。前者每模块有8,192b存储器,后者包含4,096b专用通信存储器且可配置为单端口、多端口或带专用控制逻辑的FIFO。
CPLD有什么好处?
I/O数量多
CPLD的好处之一是在给定的器件密度上可提供更多的I/O数,有时甚至高达70%。
时序模型简单
CPLD优于其它可编程结构之处在于它具有简单且可预测的时序模型。这种简单的时序模型主要应归功于CPLD的粗粒度特性。
CPLD可在给定的时间内提供较宽的相等状态,而与路由无关。这一能力是设计成功的关键,不但可加速初始设计工作,而且可加快设计调试过程。
粗粒CPLD结构的优点
CPLD是粗粒结构,这意味著进出器件的路径经过较少的开关,相应地延迟也小。因此,与等效的FPGA相比,CPLD可工作在更高的频率,具有更好的性能。
CPLD的另一个好处是其软件编译快,因为其易于路由的结构使得布放设计任务更加容易执行。
细粒FPGA结构的优点
FPGA是细粒结构,这意味著每个单元间存在细粒延迟。如果将少量的逻辑紧密排列在一起,FPGA的速度相当快。然而,随著设计密度的增加,信号不得不通过许多开关,路由延迟也快速增加,从而削弱了整体性能。CPLD的粗粒结构却能很好地适应这一设计布局的改变。
灵活的输出引脚
CPLD的粗粒结构和时序特性可预测,因此设计人员在设计流程的后期仍可以改变输出引脚,而时序仍保持不变。
新的CPLD封装
CPLD有多种密度和封装类型,包括单芯片自引导方案。自引导方案在单个封装内集成了FLASH存储器和CPLD,无须外部引导单元,从而可降低设计复杂性并节省板空间。在给定的封装尺寸内,有更高的器件密度共享引脚输出。这就为设计人员提供了"放大"设计的便利,而无须更改板上的引脚输出。
●Arm是一种嵌入式芯片,比单片机功能强,可以针对需要增加外设。类似于通用cpu,但是不包括桌面计算机。
DSP主要用来计算,计算功能很强悍,一般嵌入式芯片用来控制,而DSP用来计算,譬如一般手机有一个arm芯片,主要用来跑界面,应用程序,DSP可能有两个,adsp,mdsp,或一个,主要是加密解密,调制解调等。
●ARM其实就是一个知识产权,ARM公司本身不生产芯片,但是向其它公司提供授权。
alterA有嵌入ARM内核的SOPC芯片。
如果自己设计一个ARM芯片,显然是不大可能的,即使设计出来嵌入式芯片,也不能叫ARM。
当然用FPGA设计简单的处理器芯片应该还是有可能的,好象外国大学都有这样的课程设计,有很多书籍介绍设计简单的处理器芯片的。处理器芯片主要就是把指令译码,分派给不同的功能部件来执行工作,如果加流水线,预测执行以及存储器、外设等等功能,应该工作量很大的。
●其实象工作量特别大的运算,一般还是用FPGA/ASIC来实现的,如在手机基带芯片中,码片级的运算,一般是用FPGA/ASIC,而比特级的运算,应该用DSP实现的多
关于u-boot代码结构
u-boot 是一个open source 的bootloader。u-boot 是在ppcboot 以及armboot
的基础上发展而来,已经在许多嵌入式系统开发过程中被采用。由于其开发源代码,其支持的开发板众多。
2. start.S 代码结构
1) 定义入口
一个可执行的Image
必须有一个入口点并且只能有一个唯一的全局入口,通常这个入口放在Rom(flash)的0x0
地址。例如start.S 中的
.globl _start
_start:
值得注意的是你必须告诉编译器知道这个入口,这个工作主要是修改连接器脚本文件(lds)。
2) 设置异常向量(Exception Vector)
异常向量表,也可称为中断向量表,必须是从0
地址开始,连续的存放。如下面的就包括了复位(reset),未定义处理(undef),软件中断(SWI),预去指令错误(Pabort),数据错误
(Dabort),保留,以及IRQ,FIQ 等。注意这里的值必须与uClinux 的vector_base
一致。这就是说如果uClinux
中vector_base(include/armnommu/proc-armv/system.h)定义为0x0c00
0000,则HandleUndef 应该在
0x0c00 0004。
b reset //for debug
ldr pc,=HandleUndef
ldr pc,=HandleSWI
ldr pc,=HandlePabort
ldr pc,=HandleDabort
b .
ldr pc,=HandleIRQ
ldr pc,=HandleFIQ
ldr pc,=HandleEINT0 /*mGA H/W interrupt vector table*/
ldr pc,=HandleEINT1
ldr pc,=HandleEINT2
ldr pc,=HandleEINT3
ldr pc,=HandleEINT4567
ldr pc,=HandleTICK /*mGA*/
b .
b .
ldr pc,=HandleZDMA0 /*mGB*/
ldr pc,=HandleZDMA1
ldr pc,=HandleBDMA0
ldr pc,=HandleBDMA1
ldr pc,=HandleWDT
ldr pc,=HandleUERR01 /*mGB*/
b .
b .
ldr pc,=HandleTIMER0 /*mGC*/
ldr pc,=HandleTIMER1
ldr pc,=HandleTIMER2
ldr pc,=HandleTIMER3
ldr pc,=HandleTIMER4
ldr pc,=HandleTIMER5 /*mGC*/
b .
b .
ldr pc,=HandleURXD0 /*mGD*/
ldr pc,=HandleURXD1
ldr pc,=HandleIIC
ldr pc,=HandleSIO
ldr pc,=HandleUTXD0
ldr pc,=HandleUTXD1 /*mGD*/
b .
b .
ldr pc,=HandleRTC /*mGKA*/
b .
b .
b .
b .
b . /*mGKA*/
b .
b .
ldr pc,=HandleADC /*mGKB*/
b .
b .
b .
b .
b . /*mGKB*/
b .
b .
ldr pc,=EnterPWDN
作为对照:请看以上标记的值:
.equ HandleReset, 0xc000000
.equ HandleUndef,0xc000004
.equ HandleSWI, 0xc000008
.equ HandlePabort, 0xc00000c
.equ HandleDabort, 0xc000010
.equ HandleReserved, 0xc000014
.equ HandleIRQ, 0xc000018
.equ HandleFIQ, 0xc00001c
/*the value is different with an address you think it may be.
*IntVectorTable */
.equ HandleADC, 0xc000020
.equ HandleRTC, 0xc000024
.equ HandleUTXD1, 0xc000028
.equ HandleUTXD0, 0xc00002c
.equ HandleSIO, 0xc000030
.equ HandleIIC, 0xc000034
.equ HandleURXD1, 0xc000038
.equ HandleURXD0, 0xc00003c
.equ HandleTIMER5, 0xc000040
.equ HandleTIMER4, 0xc000044
.equ HandleTIMER3, 0xc000048
.equ HandleTIMER2, 0xc00004c
.equ HandleTIMER1, 0xc000050
.equ HandleTIMER0, 0xc000054
.equ HandleUERR01, 0xc000058
.equ HandleWDT, 0xc00005c
.equ HandleBDMA1, 0xc000060
.equ HandleBDMA0, 0xc000064
.equ HandleZDMA1, 0xc000068
.equ HandleZDMA0, 0xc00006c
.equ HandleTICK, 0xc000070
.equ HandleEINT4567, 0xc000074
.equ HandleEINT3, 0xc000078
.equ HandleEINT2, 0xc00007c
.equ HandleEINT1, 0xc000080
.equ HandleEINT0, 0xc000084
3) 初始化CPU 相关的pll,clock,中断控制寄存器
依次为关闭watch dog timer,关闭中断,设置LockTime,PLL(phase lock
loop),以及时钟。
这些值(除了LOCKTIME)都可从Samsung 44b0 的手册中查到。
ldr r0,WTCON //watch dog disable
ldr r1,=0x0
str r1,[r0]
ldr r0,INTMSK
ldr r1,MASKALL //all interrupt disable
str r1,[r0]
/*****************************************************
* Set clock control registers *
*****************************************************/
ldr r0,LOCKTIME
ldr r1,=800 // count = t_lock * Fin (t_lock=200us, Fin=4MHz) = 800
str r1,[r0]
ldr r0,PLLCON /*temporary setting of PLL*/
ldr r1,PLLCON_DAT /*Fin=10MHz,Fout=40MHz or 60MHz*/
str r1,[r0]
ldr r0,CLKCON
ldr r1,=0x7ff8 //All unit block CLK enable
str r1,[r0]
4) 初始化内存控制器
内存控制器,主要通过设置13 个从1c80000 开始的寄存器来设置,包括总线宽度,
8 个内存bank,bank 大小,sclk,以及两个bank mode。
/*****************************************************
* Set memory control registers *
*****************************************************/
memsetup:
adr r0,SMRDATA
ldmia r0,{r1-r13}
ldr r0,=0x01c80000 //BWSCON Address
stmia r0,{r1-r13}
5) 将rom 中的程序复制到RAM 中
首先利用PC 取得bootloader 在flash
的起始地址,再通过标号之差计算出这个程序代
码的大小。这些标号,编译器会在连接(link)的时候生成正确的分布的值。取得正
确信息后,通过寄存器(r3 到r10)做为复制的中间媒介,将代码复制到RAM 中。
relocate:
/*
* relocate armboot to RAM
*/
adr r0, _start /* r0 <- current position of code */
ldr r2, _armboot_start
ldr r3, _armboot_end
sub r2, r3, r2 /* r2 <- size of armboot */
ldr r1, _TEXT_BASE /* r1 <- destination address */
add r2, r0, r2 /* r2 <- source end address */
/*
* r0 = source address
* r1 = target address
* r2 = source end address
*/
copy_loop:
ldmia r0!, {r3-r10}
stmia r1!, {r3-r10}
cmp r0, r2
ble copy_loop
6) 初始化堆栈
进入各种模式设置相应模式的堆栈。
InitStacks:
/*Don't use DRAM,such as stmfd,ldmfd......
SVCstack is initialized before*/
mrs r0,cpsr
bic r0,r0,#0X1F
orr r1,r0,#0xDB /*UNDEFMODE|NOINT*/
msr cpsr,r1 /*UndefMode*/
ldr sp,UndefStack
orr r1,r0,#0XD7 /*ABORTMODE|NOINT*/
msr cpsr,r1 /*AbortMode*/
ldr sp,AbortStack
orr r1,r0,#0XD2 /*IRQMODE|NOINT*/
msr cpsr,r1 /*IRQMode*/
ldr sp,IRQStack
orr r1,r0,#0XD1 /*FIQMODE|NOINT*/
msr cpsr,r1 /*FIQMode*/
ldr sp,FIQStack
bic r0,r0,#0XDF /*MODEMASK|NOINT*/
orr r1,r0,#0X13
msr cpsr,r1 /*SVCMode*/
ldr sp,SVCStack
7) 转到RAM 中执行
使用指令ldr,pc,RAM 中C 函数地址就可以转到RAM 中去执行。
5. 系统初始化部分
1. 串口部分
串口的设置主要包括初始化串口部分,值得注意的串口的Baudrate 与时钟MCLK
有很大关系,是通过:rUBRDIV0=( (int)(MCLK/16./(gd ->baudrate) + 0.5) -1
)计算得出。这可以在手册中查到。其他的函数包括发送,接收。这个时候没有中断,是通过循环等待来判断是否动作完成。
例如,接收函数:
while(!(rUTRSTAT0 & 0x1)); //Receive data read
return RdURXH0();
2. 时钟部分
实现了延时函数udelay。
这里的get_timer 由于没有使用中断,是使用全局变量来累加的。
3. flash 部分
flash 作为内存的一部分,读肯定没有问题,关键是flash 的写部分。
Flash 的写必须先擦除,然后再写。
unsigned long flash_init (void)
{
int i;
u16 manId,devId;
//first we init it as unknown,even if you forget assign it below,it's not
a problem
for (i=0; i < CFG_MAX_FLASH_BANKS; ++i){
flash_info[i].flash_id = FLASH_UNKNOWN;
flash_info[i].sector_count=CFG_MAX_FLASH_SECT;
}
/*check manId,devId*/
_RESET();
_WR(0x555,0xaa);
_WR(0x2aa,0x55);
_WR(0x555,0x90);
manId=_RD(0x0);
_WR(0x555,0xaa);
_WR(0x2aa,0x55);
_WR(0x555,0x90);
devId=_RD(0x1);
_RESET();
printf("flashn");
printf("Manufacture ID=%4x(0x0004), Device ID(0x22c4)=%4xn",manId,devId);
if(manId!=0x0004 && devId!=0x22c4){
printf("flash check faliluren");
return 0;
}else{
for (i=0; i < CFG_MAX_FLASH_BANKS; ++i){
flash_info[i].flash_id=FLASH_AM160T;/*In fact it is fujitu,I only don't
want to
modify common files*/
}
}
/* Setup offsets */
flash_get_offsets (CFG_FLASH_BASE, &flash_info[0]);
/* zhangyy comment
#if CFG_MONITOR_BASE >= CFG_FLASH_BASE
//onitor protection ON by default
flash_protect(FLAG_PROTECT_SET,
CFG_MONITOR_BASE,
CFG_MONITOR_BASE+monitor_flash_len-1,
&flash_info[0]);
(转)BusyBox 简化嵌入式 Linux 系统
BusyBox 简化嵌入式 Linux 系统
BusyBox 是很多标准 Linux® 工具的一个单个可执行实现。BusyBox
包含了一些简单的工具,例如 cat 和
echo,还包含了一些更大、更复杂的工具,例如 grep、find、mount 以及
telnet(不过它的选项比传统的版本要少);有些人将 BusyBox 称为 Linux
工具里的瑞士军刀。本文将探索 BusyBox
的目标,它是如何工作的,以及为什么它对于内存有限的环境来说是如此重要。
BusyBox 的诞生
BusyBox 最初是由 Bruce Perens 在 1996 年为 Debian GNU/Linux
安装盘编写的。其目标是在一张软盘上创建一个可引导的 GNU/Linux
系统,这可以用作安装盘和急救盘。一张软盘可以保存大约 1.4-1.7MB
的内容,因此这里没有多少空间留给 Linux 内核以及相关的用户应用程序使用。
BusyBox 揭露了这样一个事实:很多标准 Linux
工具都可以共享很多共同的元素。例如,很多基于文件的工具(比如 grep 和
find)都需要在目录中搜索文件的代码。当这些工具被合并到一个可执行程序中时,它们就可以共享这些相同的元素,这样可以产生更小的可执行程序。实际
上, BusyBox 可以将大约 3.5MB 的工具包装成大约 200KB
大小。这就为可引导的磁盘和使用 Linux
的嵌入式设备提供了更多功能。我们可以对 2.4 和 2.6 版本的 Linux 内核使用
BusyBox。
BusyBox 是如何工作的?
为了让一个可执行程序看起来就像是很多可执行程序一样,BusyBox 为传递给 C 的
main 函数的参数开发了一个很少使用的特性。回想一下 C 语言的 main
函数的定义如下:
清单 1. C 的 main 函数
int main( int argc, char *argv[] )
在这个定义中,argc 是传递进来的参数的个数(参数数量),而 argv
是一个字符串数组,代表从命令行传递进来的参数(参数向量)。argv 的索引 0
是从命令行调用的程序名。
清单 2 给出的这个简单 C 程序展示了 BusyBox 的调用。它只简单地打印 argv
向量的内容。
清单 2. BusyBox 使用 argv[0] 来确定调用哪个应用程序
// test.c
#include <stdio.h>
int main( int argc, char *argv[] )
{
int i;
for (i = 0 ; i < argc ; i++) {
printf("argv[%d] = %s\n", i, argv[i]);
}
return 0;
}
调用这个程序会显示所调用的第一个参数是该程序的名字。我们可以对这个可执行程序重新进行命名,此时再调用就会得到该程序的新名字。另外,我们可以创建一个到可执行程序的符号链接,在执行这个符号链接时,就可以看到这个符号链接的名字。
清单 3. 在使用新命令更新 BusyBox 之后的命令测试
$ gcc -Wall -o test test.c
$ ./test arg1 arg2
argv[0] = ./test
argv[1] = arg1
argv[2] = arg2
$ mv test newtest
$ ./newtest arg1
argv[0] = ./newtest
argv[1] = arg1
$ ln -s newtest linktest
$ ./linktest arg
argv[0] = ./linktest
argv[1] = arg
BusyBox 使用了符号链接以便使一个可执行程序看起来像很多程序一样。对于
BusyBox
中包含的每个工具来说,都会这样创建一个符号链接,这样就可以使用这些符号链接来调用
BusyBox 了。BusyBox 然后可以通过 argv[0] 来调用内部工具。
配置并编译 BusyBox
我 们可以从 BusyBox 的 Web 站点上下载最新版本的 BusyBox(请参看 参考资料
一节的内容)。与大部分开放源码程序一样,它是以一个压缩的 tarball
形式发布的,我们可以使用清单 4
给出的命令将其转换成源代码树。(如果我们下载的版本不是
1.1.1,那就请在这个命令中使用适当的版本号以及特定于这个版本号的命令。)
清单 4. 展开 BusyBox
$ tar xvfz busybox-1.1.1.tar.gz
$
结果会生成一个目录,名为 busybox-1.1.1,其中包含了 BusyBox
的源代码。要编译默认的配置(其中包含了几乎所有的内容,并禁用了调试功能),请使用
defconfig make 目标:
清单 5. 编译默认的 BusyBox 配置
$ cd busybox-1.1.1
$ make defconfig
$ make
$
结 果是一个相当大的 BusyBox
映像,不过这只是开始使用它的最简单的方法。我们可以直接调用这个新映像,这会产生一个简单的
Help
页面,里面包括当前配置的命令。要对这个映像进行测试,我们也可以对一个命令调用
BusyBox 来执行,如清单 6 所示。
清单 6. 展示 BusyBox 命令的执行和 BusyBox 中的 ash shell
$ ./busybox pwd
/usr/local/src/busybox-1.1.1
$ ./busybox ash
/usr/local/src/busybox-1.1.1 $ pwd
/usr/local/src/busybox-1.1.1
/usr/local/src/busybox-1.1.1 $ exit
$
在这个例子中,我们调用了 pwd(打印工作目录)命令,使用 BusyBox 进入了 ash
shell,并在 ash 中调用了 pwd。
手工配置
如 果您正在构建一个具有特殊需求的嵌入式设备,那就可以手工使用 menuconfig
make 目标来配置 BusyBox 的内容。如果您熟悉 Linux
内核的编译过程,就会注意到 menuconfig 与配置 Linux
内核的内容所使用的目标相同。实际上,它们都采用了相同的基于 ncurses
的应用程序。
使 用手工配置,我们可以指定在最终的 BusyBox 映像中包含的命令。我们也可以对
BusyBox 环境进行配置,例如包括对 NSA(美国国家安全代理)的安全增强
Linux(SELinux),指定要使用的编译器(用来在嵌入式环境中进行交叉编译)以及
BusyBox 应该静态编译还是动态编译。使用 menuconfig可以为 BusyBox
配置的不同类型的应用程序(applet)。
多体系结构支持
可以简单地为 BusyBox 指定交叉编译器意味着我们可以为很多体系结构编译
BusyBox。要为您的目标体系结构编译
BusyBox,我们需要一个交叉编译器和一个已经为特定目标体系结构编译好的 C
库(uClibc 或 glibc)。
要手工配置 BusyBox,请使用下面的命令:
清单 7. 手工配置 BusyBox
$ make menuconfig
$ make
$
这为我们提供了可以调用的 BusyBox 的二进制文件。下一个步骤是围绕 BusyBox
构建一个环境,包括将标准 Linux 命令重定向到 BusyBox
二进制文件的符号链接。我们可以使用下面的命令简单地完成这个过程:
清单 8. 构建 BusyBox 环境$ make install
$
默 认情况下,这会创建一个新的本地子目录 _install,其中包含了基本的 Linux
环境。在这个根目录中,您会找到一个链接到 BusyBox 的 linuxrc 程序。这个
linuxrc
程序在构建安装盘或急救盘(允许提前进行模块化的引导)时非常有用。同样是在这个根目录中,还有一个包含操作系统二进制文件的
/sbin 子目录。还有一个包含用户二进制文件的 /bin
目录。在构建软盘发行版或嵌入式初始 RAM 磁盘时,我们可以将这个 _install
目录迁移到目标环境中。我们还可以使用 make 程序的 PREFIX
选项将安装目录重定向到其他位置。例如,下面的代码就使用 /tmp/newtarget
根目录来安装这些符号链接,而不是使用 ./_install 目录:
清单 9. 将符号链接安装到另外一个目录中$ make PREFIX=/tmp/newtarget install
$
使 用 install make 目标创建的符号链接都来自于 busybox.links
文件。这个文件是在编译 BusyBox
时创建的,它包含了已经配置的命令清单。在执行 install 时,就会检查
busybox.links 文件确定要创建的符号链接。
到 BusyBox 的命令行链接也可以使用 BusyBox
在运行时动态创建。CONFIG_FEATURE_INSTALLER
选项就可以启用这个特性,在运行时可以这样执行:
清单 10. 在运行时创建命令链接$ ./busybox --install -s
$
-s 选项强制创建这些符号链接(否则就创建硬链接)。这个选项要求系统中存在
/proc 文件系统。
BusyBox 编译选项
BusyBox 包括了几个编译选项,可以帮助为我们编译和调试正确的 BusyBox。
表 1. 为 BusyBox 提供的几个 make 选项
make目标 说明help 显示 make 选项的完整列表
defconfig 启用默认的(通用)配置
allnoconfig 禁用所有的应用程序(空配置)
allyesconfig 启用所有的应用程序(完整配置)
allbareconfig 启用所有的应用程序,但是不包括子特性
config 基于文本的配置工具
menuconfig N-curses(基于菜单的)配置工具
all 编译 BusyBox 二进制文件和文档(./docs)
busybox 编译 BusyBox 二进制文件
clean 清除源代码树
distclean 彻底清除源代码树
sizes 显示所启用的应用程序的文本/数据大小
在定义配置时,我们只需要输入 make 就可以真正编译 BusyBox
二进制文件。例如,要为所有的应用程序编译 BusyBox,我们可以执行下面的命令:
清单 11. 编译 BusyBox 二进制程序$ make allyesconfig
$ make
$
压缩 BusyBox
如果您非常关心对 BusyBox 映像的压缩,就需要记住两件事情:
1.
永远不要编译为静态二进制文件(这会将所有需要的库都包含到映像文件中)。相反,如果我们是编译为一个共享映像,那么它会使用其他应用程序使用的库(例如
/lib/libc.so.X)。
2. 使用 uClibc 进行编译,这是一个对大小进行过优化的 C
库,它是为嵌入式系统开发的;而不要使用标准的 glibc (GNU C 库)来编译。
BusyBox 命令中支持的选项
BusyBox
中的命令并不支持所有可用选项,不过这些命令都包含了常用的选项。如果我们需要知道一个命令可以支持哪些选项,可以使用
--help 选项来调用这个命令,如清单 12 所示。
清单 12. 使用 --help 选项调用命令$ ./busybox wc --help
BusyBox v1.1.1 (2006.04.09-15:27+0000) multi-call binary
Usage: wc [OPTION]... [FILE]...
Print line, word, and byte counts for each FILE, and a total line if
more than one FILE is specified. With no FILE, read standard input.
Options:
-c print the byte counts
-l print the newline counts
-L print the length of the longest line
-w print the word counts
$
这些特定的数据只有在启用了 CONFIG_FEATURE_VERBOSE_USAGE
选项时才可以使用。如果没有这个选项,我们就无法获得这些详细数据,但是这样可以节省大约
13 KB 的空间。
向 BusyBox 中添加新命令
向 BusyBox
添加一个新命令非常简单,这是因为它具有良好定义的体系结构。第一个步骤是为新命令的源代码选择一个位置。我们要根据命令的类型(网络,shell
等)来选择位置,并与其他命令保持一致。这一点非常重要,因为这个新命令最终会在
menuconfig 的配置菜单中出现(在下面的例子中,是 Miscellaneous Utilities
菜单)。
对于这个例子来说,我将这个新命令称为 newcmd,并将它放到了 ./miscutils
目录中。这个新命令的源代码如清单 13 所示。
清单 13. 集成到 BusyBox 中的新命令的源代码#include "busybox.h"
int newcmd_main( int argc, char *argv[] )
{
int i;
printf("newcmd called:\n");
for (i = 0 ; i < argc ; i++) {
printf("arg[%d] = %s\n", i, argv[i]);
}
return 0;
}
接下来,我们要将这个新命令的源代码添加到所选子目录中的 Makefile.in
中。在本例中,我更新了 ./miscutils/Makefile.in
文件。请按照字母顺序来添加新命令,以便维持与现有命令的一致性:
清单 14. 将命令添加到 Makefile.in 中MISCUTILS-$(CONFIG_MT) += mt.o
MISCUTILS-$(CONFIG_NEWCMD) += newcmd.o
MISCUTILS-$(CONFIG_RUNLEVEL) += runlevel.o
接下来再次更新 ./miscutils
目录中的配置文件,以便让新命令在配置过程中是可见的。这个文件名为
Config.in,新命令是按照字母顺序添加的:
清单 15. 将命令添加到 Config.in 中config CONFIG_NEWCMD
bool "newcmd"
default n
help
newcmd is a new test command
这 个结构定义了一个新配置项(通过 config
关键字)以及一个配置选项(CONFIG_NEWCMD)。新命令可以启用,也可以禁用,因此我们对配置的菜单属性使用了
bool (Boolean)值。这个命令默认是禁用的(n 表示
No),我们可以最后放上一个简短的 Help 描述。在源代码树的
./scripts/config/Kconfig-language.txt
文件中,我们可以看到配置语法的完整文法。
接下来需要更新 ./include/applets.h
文件,使其包含这个新命令。将下面这行内容添加到这个文件中,记住要按照字母顺序。维护这个次序非常重要,否则我们的命令就会找不到。
清单 16. 将命令添加到 applets.h 中USE_NEWCMD(APPLET(newcmd, newcmd_main,
_BB_DIR_USER_BIN, _BB_SUID_NEVER))
这定义了命令名(newcmd),它在 Busybox
源代码中的函数名(newcmd_main),应该在哪里会为这个新命令创建链接(在这种情况中,它在
/usr/bin 目录中),最后这个命令是否有权设置用户 id(在本例中是 no)。
倒数第二个步骤是向 ./include/usage.h
文件中添加详细的帮助信息。正如您可以从这个文件的例子中看到的一样,使用信息可能非常详细。在本例中,我只添加了一点信息,这样就可以编译这个新命令了:
清单 17. 向 usage.h 添加帮助信息#define newcmd_trivial_usage "None"
#define newcmd_full_usage "None"
最后一个步骤是启用新命令(通过 make menuconfig,然后在 Miscellaneous
Utilities 菜单中启用这个选项)然后使用 make 来编译 BusyBox。
使用新的 BusyBox,我们可以对这个新命令进行测试,如清单 18 所示。
清单 18. 测试新命令$ ./busybox newcmd arg1
newcmd called:
arg[0] = newcmd
arg[1] = arg1
$ ./busybox newcmd --help
BusyBox v1.1.1 (2006.04.12-13:47+0000) multi-call binary
Usage: newcmd None
None
就是这样!BusyBox 开发人员开发了一个优秀但非常容易扩展的工具。
结束语
BusyBox 是为构建内存有限的嵌入式系统和基于软盘系统的一个优秀工具。BusyBox
通过将很多必需的工具放入一个可执行程序,并让它们可以共享代码中相同的部分,从而对它们的大小进行了很大程度的缩减,BusyBox
对于嵌入式系统来说是一个非常有用的工具,因此值得我们花一些时间进行探索。
(转)在 Linux 系统上源码安装 GTK+ 2.0
==================================================
Keywords: GTK+, Install, Linux, Source
Author: whyglinux (whyglinux AT hotmail DOT com)
Date: 2007-01-07
==================================================
目录
0. 前言
1. 二进制安装和源码安装
2. GTK+ 依赖软件包
3. 查看软件的版本号
4. 安装规划
4.1 系统上未安装 GTK+
4.2 系统上已安装 GTK+
5. 软件下载
6. 库的安装
6.1 安装顺序
6.2 安装过程
6.2.1 解包
6.2.2 配置
6.2.3 构建
6.2.4 安装
6.2.5 设置
6.2.5.1 搜索路径
6.2.5.2 编译和连接界面
6.2.5.3 pkg-config
6.2.5.4 GTK+ 及其依赖库的设置
6.2.5.4.1 以编译和连接为目的的设置
6.2.5.4.2 以连接和执行为目的的设置
6.3 其它库的安装
6.3.1 安装 Atk
6.3.2 安装 Cairo
6.3.3 安装 Pango
6.3.4 安装 Gtk+
7. 库的使用
7.1 库使用之前的设置
7.2 库文档
-----------------------------------------------------------------------------
0. 前言
GTK+ 2.0
依赖的软件包(程序和库)比较多,版本的更新也比较频繁,所以如果想从 GTK+
提供的源码软件包中构建一套较新或最新版本的 GTK+
库来使用的话,通常需要首先更新或者安装一系列新版本的依赖程序或库。同时,由于软件包之间存在着依赖关系,对软件包的版本和安装顺序都有一定的要求,一般还需要对安装后的库进行一些必要的设置才能使用库。因而,可以说源码安装
GTK+ 是一项不小的工程。如果没有源码安装 GTK+
的经验,在安装过程中很容易遇到一些问题。对于新手来说,出现了安装问题时却往往不知道如何去解决。
本文试图对 GTK+
的源码安装提供一套可行的解决方案,介绍一些安装和使用库方面的背景知识,对安装过程中容易出现问题的地方做了强调说明,以使安装过程能够顺利进行。这样,即使是一个从来没有安装过
GTK+ 的新手也能根据这里的说明顺利地安装上 GTK+。
如果你发现了本篇中的错误,或者对本文有什么感想或者建议,可通过 whyglinux
AT hotmail DOT com 邮箱和作者联系。
1. 二进制安装和源码安装
需要首先说明的的是:对于 Linux 系统、特别是较新版本的 Linux
系统来说,其发行版中已经包含了 GTK+
和所有的支撑软件,一般来说默认安装后就可以直接使用 GTK+
了。如果在安装的时候没有选择安装 GTK+,也可以用系统提供的安装工具将 GTK+
添加到系统中来,或者下载已经编译好的 GTK+ 进行版本升级。
上面的安装方式使用的是已经编译好的软件包。由于这种安装一般会自动解决各个软件包之间的依赖关系,进而安装或者更新相应的软件包,所以与源码安装方式相比,二进制包的安装节省了编译代码所需要的时间,避免了源码安装的种种繁琐易错之处,对于安装者的要求也较低,因此是安装
GTK+ 的首选方式。
二进制安装方式简单快捷,但也有其力所不及的地方:通常一个软件的二进制包的版本更新要落后于其最新版本,有些软件也可能没有二进制包提供。这样,要使用最新的版本很可能源码安装就是唯一可以选择的方式了。有时人们也想体验或学习
GTK+
的源码安装方式,毕竟在开源盛世的今天,对于程序员来说源码安装也是必须要过的一关。
2. GTK+ 依赖软件包
GTK+
的安装需要下面程序或者库的支持(可在列出的链接中找到各个软件包的下载地址):
1. C 编译器(如 GCC。GCC 的网站)
2. X 窗口系统库(网站)
3. pkg-config 工具(网站)
4. GNU make 工具(网站)
5. JPEG、PNG 以及 TIFF 图形库(下载页面 的 GTK+ Source 中的
dependencies 目录)
6. FreeType(网站)
7. fontconfig 库(网站)
8. GNU libiconv 库(当系统上没有 iconv() 函数的时候需要)(网站)
9. GNU gettext 软件包(当系统上没有 gettext()
函数的时候需要)([url=http://www.gnu.org/software/gettext/网站[/url])
10. GLib 库(下载页面 的 GLib Source)
11. ATK 库(下载页面 的 GTK+ Source 中的 dependencies 目录)
12. Cairo 库(下载页面 的 GTK+ Source 中的 dependencies 目录)
13. Pango 库(下载页面 的 Pango Source)
14. GTK+ 库(下载页面 的 GTK+ Source)
目前(写此文时)最新的 GTK+ 是 2.10.6
版,我们就以这个版本为例介绍。当你看到这篇文章的时候,可能 GTK+
又有了新的版本,所以要注意下载安装新版本的软件包。
其中,以上 1~9 各项是一些比较通用的软件,和 GTK+
的关系也没有那么紧密--它们不但被 GTK+
使用,也被其它程序或者库使用。即使系统上没有安装
GTK+,它们也可能已经在系统中存在了。
10~13 各项和 GTK+ 关系密切,更新也较快,通常一个 GTK+
的版本会依赖于这些库的一些特定的版本。由于这些原因,在本文中说明 GTK+
安装的时候认为 1~9 项已经安装好了,所以只涉及到 10~14
项的安装。也就是说,GTK+ 的安装实际上主要是 GLib、Atk、Cairo、Pango 和
Gtk+ 这五个库的安装。
当然,在你的系统 1~9
各项中也可能存在没有安装的情况,也可能存在由于版本过低从而使 GTK+
不能顺利安装的情况。当遇到这些情况的时候,应该参考各自的网站中的安装说明对软件进行安装或者升级。可以使用二进制包直接安装,也可以使用源码方式安装。在本文中对这些软件的安装将不再叙述。
根据经验,只要系统中已经有了 1~9 各项,而且系统也较新的话,为了安装 GTK+
一般没有必要把它们都升级到最新版本,除了其中的 pkg-config 工具。pkg-config
的变动较大,新版本的 GTK+ 的安装需要新版 pkg-config
的支持,否则可能会使安装过程失败。因此,要在安装 GTK+ 之前检查 pkg-config
的版本号。如果版本过低,一定要对它进行版本更新。至于 GTK+ 安装时对
pkg-config 的最低版本要求,可以在 GTK+ 下载目录的 dependencies
目录中找到对应的 pkg-config 软件包,从软件包上提供的版本信息中获得确认。
3. 查看软件的版本号
查看已经安装的软件的版本号的目的有二:
* 检查软件是否存在
*
获得软件的版本号,从中可以了解软件的新旧程度,是决定软件是否需要更新的依据
软件包大致可分为两种类型:程序和库。类型不同,查看版本号的方式也不同。
对于可运行的程序命令来说,查看版本号的方式是在执行命令后加上 --version
参数。例如,对于 pkg-config 来说,其过程是这样的:
$ pkg-config --version
上面的"$"符号表示命令行提示符。
注:你现在应该执行上面的命令查看 pkg-config
的版本号,并按照上面所述检查是否符合安装相应的 GTK+
的最低版本要求。如果不符合要求,在进行下面的 GTK+
及其依赖库的安装之前应该首先安装和更新 pkg-config。
对于库来说,如果它支持使用 pkg-config,则可以使用 pkg-config
来查看其版本号。例如,对于 GTK+ 2.0 库来说,可以这样:
$ pkg-config --modversion gtk+-2.0
注:不妨执行上面的命令看看 GTK+
库是否已经在系统存在了;如果已经存在,注意它的版本号。还可以执行下面的命令查看使用
GTK+ 库时的编译和连接选项:
$ pkg-config --cflags --libs gtk+-2.0
通过显示出来的信息中的 -I 后面的路径可以大体知道 GTK+
及其依赖库的安装位置。看看它们是不是都位于 /usr 目录下。
4. 安装规划
4.1 系统上未安装 GTK+
通过上面的检查,如果发现系统上没有安装 GTK+,那问题就变得简单了:直接将
GTK+ 及其依赖库安装到 /usr 目录下即可(至于如何把各个库的安装目录设置为
/usr,可参看下面有关的安装说明)。这样做的好处是:由于 /usr
是系统目录,几乎不需要对安装的库进行什么设置就能够马上使用它们。
/usr
是一个重要的系统目录,应该尽量避免对这个目录进行写操作。因此,建议源码安装
GTK+ 不要将它安装在 /usr
等系统目录下;可另选择一其它目录(具体参见下面的相关说明)。
4.2 系统上已安装 GTK+
如果系统中已经安装有 GTK+,要安装新版本的 GTK+
时需要考虑的问题就多一些了。在 Linux 系统上使用的很多软件都是在 GTK+
库的支持下运行的(比如 GNOME 桌面)。如果相关的 GTK+
库发生损坏,或者库的版本发生了变化,轻微的可造成某些程序不能正常运行,严重的可能会给系统运行带来障碍(比如进入不了桌面环境,等等。)
因此,新版本的 GTK+ 的安装应该避免对原来的 GTK+
造成影响,以保证系统的正常运行。这一点很容易做到:新版 GTK+
的安装目录要避免和已经存在的 GTK+ 的目录一致。比如,如果旧版的 GTK+ 安装在
/usr 目录下,新版 GTK+ 在设置安装目录的时候最好就不要设置为 /usr 了。
一些人由于不了解这些情况,或者图方便,直接就把 GTK+ 安装在 /usr
中、从而把原来的 GTK+ 库给替换了。由于 GTK+
及其兼容库版本的变化以及可能在安装过程中产生的错误,很容易出现上面提到的问题,所以建议在安装新版
GTK+ 时,最好避开旧版 GTK+ 所在的目录。
GTK+
安装在什么目录中为好呢?其实,这没有什么定论,可自行设置安装的目录。不过,一般的源码软件包默认的安装目录是
/usr/local,所以可以把这个目录设置为 GTK+
的安装目录,也可以是其它你认为合适的目录。在下面的示例安装中,我们使用的安装目录是
/opt/gtk,GTK+ 及其依赖库都将安装在这个目录下。
将 GTK+ 及其依赖库设置安装到同一个目录下(如
/opt/gtk)、而不是每一个库占用一个不同的目录,可以给以后的库的设置带来方便。而且,在将来不再需要这个版本的
GTK+ 及其依赖库的时候可以通过删除这个目录(如 /opt/gtk)将它们简单地去除。
和安装到 /usr
目录中不同,如果将库安装到一个非系统目录中(比如我们将要使用的 /opt/gtk
目录),只将库安装完成还是不够的,还必须要进行一些必要的设置才能使用这个新安装好的库。在下面的相关章节中讲对库的设置作具体说明。
5. 软件下载
按照上面"依赖软件包"一节中提供的说明和地址分别下载
GLib、Atk、Cairo、Pango、Gtk+ 这五个库。
在各自的下载目录中,通常列出了各种版本的软件包,而且一般每个版本都有
.tar.gz 和 .tar.bz2
两种不同压缩格式。要注意根据各个软件包的版本号或者日期选择一个最新的版本下载,有的库的下载目录下面也用一个
LATEST-xxx 的文件名告诉目前的最新版本是多少。由于 .tar.bz2
压缩格式的文件较小,推荐下载这种软件包;如果没有,再下载 .tar.gz 格式的包。
下面是目前各个库的最新版本的软件包:
* glib-2.12.5.tar.bz2
* atk-1.9.1.tar.bz2
* cairo-1.2.0.tar.gz
* pango-1.14.8.tar.bz2
* gtk+-2.10.6.tar.bz2
可以新建一个目录,用于存放以上这些下载的软件包。
由于这些软件包都是使用 GNU Autotools
工具创建的,所以各个软件包的构建和安装界面是相同的,都是 ./configure &&
make && make install。因此,我们重点介绍 Glib 库的安装,对包括 GTK+
在内的其它库只作简单说明;在安装其它库的时候,可比照 Glib
库的安装过程进行。
6. 库的安装
6.1 安装顺序
根据依赖关系的要求,库的安装要按照这样的先后顺序进行:GLib、Atk、Cairo、Pango、Gtk+。
上述各个库在安装的时候,都会自动检查其依赖的库是否已经正确安装;如果依赖库没有安装,或者安装不成功,或者没有正确进行设置等都会导致安装终止,并显示出相应的错误提示。不过,只要按照上面的顺序安装各个库,并严格按照下面的步骤操作,一般很容易在不出现任何错误的情况下顺利地完成各个库的安装。
6.2 安装过程
源码安装软件包的过程可划分为以下几个步骤:
* 解包
* 配置
* 构建
* 安装
* 设置
下面以 Glib 的安装为例分别具体介绍库安装的各个过程。
6.2.1 解包
解包就是将软件包解压还原的过程。首先要进入软件包所在的目录,根据根据软件包的类型是
.tar.gz 还是 .tar.bz2,选择相应的解包命令。
.tar.bz2 格式软件包的解压还原:
$ tar xjvf glib-2.12.5.tar.bz2
如果软件包是 .tar.gz 格式的话,应该这样解压还原:
$ tar xzvf glib-2.12.5.tar.gz
上面的解包命令执行之后,会在当前工作目录下生成一个名为 glib-2.12.5
的目录,Glib 软件包的内容都存放在这个目录下。
其它软件包的解包过程与上面类似,只要把上面命令中的软件包名替换即可。各个软件包解包之后生成的目录名一般是将软件包名中的
.tar.bz2 或者 .tar.gz 去除之后的名称,其格式是:库名-版本号。
6.2.2 配置
配置(configure)的目的和结果是获得软件构建和安装所需要的
Makefile。为此,在配置过程中将对当前系统进行检测,获得程序构建和安装所需要的一些信息并最终记录在
Makefile
中。其中的一些内容也可以通过命令行参数进行指定,比如软件包的安装路径(如果不特意指定安装路径的话,将默认使用
/usr/local 作为安装路径。)
在前面已经规划好了:我们要将所有的软件包都安装在 /opt/gtk
目录下面,所以可以这样做:
首先进入要安装的软件包目录。例如,如果是 Glib,可以执行 cd glib-2.12.5
命令进入目录。
其次,执行下面的命令进行配置(以后安装的各个软件包的配置命令也是下面的形式):
$ ./configure --prefix=/opt/gtk
其中,configure 是在软件包中包含的一个脚本文件(是由 GNU Autotools
工具产生的),./configure 是执行这个脚本文件,用 --prefix
指明软件包的安装目录。这样,在随后的安装过程中(make
install)会把相应的文件拷贝到它后面指定的目录下(/opt/gtk)。
注:可以用 ./configure --help
命令查看各个软件包中配置时提供的不同的参数选项和各个参数的意义。
注:一个库可以有两种存在形态:共享库(.so)和静态库(.a)。对于 GTK+
及其依赖库,在源码安装的时候其默认设置是只生成共享库;如果需要静态库,应该在配置各个软件包的时候分别加上
--enable-static 参数(参见 ./configure --help)。开发 GTK
程序时一般应使用其共享库,可不安装静态库。
由于 Glib
只依赖于一些最基本的系统库,所以在执行配置的过程中应该不会出现任何问题才是。然而,对于
GTK+和其它依赖库,如果在配置过程中发现需要的程序或者库不存在,或者版本不符合要求,都会显示相应的错误提示后异常中止配置过程。如果配置不成功,则不能继续进行下面的程序构建过程。
对新手来说,他们通常不清楚什么样的配置结果是成功的,什么是失败的。下面提供两种简单的检查配置是否成功的方法:
* 配置过程中输出的信息,除了显示在屏幕上之外,还记录在一个名为
config.log 的文件中。检查这个文件中是否有 configure: exit 0
这样的一句话(一般位于文件的后面部分或者最后一行)。如果是,说明配置成功;如果不是(比如
configure: exit 1)说明配置过程中出现了错误,配置失败。
* 在 ./configure 命令执行完毕后立即执行 echo $?
命令,检查它的输出结果。如果输出是 0,说明配置成功;0
之外的数字说明配置失败。在 Linux
系统上,可以用这个方法检查一个命令或程序在其结束后返回给系统的值是多少。一般
0 代表成功,非 0 表示程序异常退出。
6.2.3 构建
从源代码生成程序的过程称为构建(Build)。这里所说的"程序"是一个广义的概念:既可以是其一般意义上的二进制可执行程序(Program),也可以是一个文本形式的可执行脚本(Script),还可以是库(Library)、头文件(Header)、数据(Data)等等。一个软件包中往往包含以上一种或者多种形式的程序构建,其中以二进制可执行程序和库最为常见。
对于用编译型语言(如 C 或者 C++)写的程序来说(GTK+ 和它的一些依赖库就是用
C 语言写成的),软件的构建过程主要是编译和连接的过程。在 Linux
系统上,构建是通过执行 make 命令实现的:
$ make
make 是根据 Makefile 的内容来决定如何构建程序的,而这个 Makefile
就是上面配置的产物。执行 make
命令之后,程序的编译过程就开始了。这是一个比较耗时的过程,特别是对于一些大型的软件包(如
GTK+ 及其依赖库)来说更是这样。
make 结束后,也可以执行 echo $? 命令检查 make
是否执行成功。一般只要配置通过了,make 应该不会出现什么问题才是。
make
的结果,对于程序来说,主要生成的是可执行程序文件;对于库来说,主要生成的是库文件。下面的安装过程将把需要的文件拷贝到在配置时指定的安装目录中去。
6.2.4 安装
构建成功的软件包的安装是通过带 install 参数的 make 进行的:
$ make install
需要在此说明的是:在 Linux 系统,除了 root
用户和具有相应权限的用户之外,一般用户只有在自己的用户目录下才有写权限;对于用户目录之外的其它目录和文件,一般只能读而不能写。我们在配置的时候将设置的安装目录是
/opt/gtk,对于一般用户来说是只读的。如果是这样的话,上面的 make install
虽然被执行,但是由于没有写的权限,不能向这个目录中拷贝文件,所以安装是不成功的。
一般需要如下面这样先切换到 root 用户,然后再进行安装:
$ su
# make install
上面的"#"符号表示处于 root 状态下的命令行提示符。
在执行完 make install 之后,也可以用 echo $? 检查是否执行安装成功。
如果此时查看 /opt/gtk 目录,你会发现这个目录下又有几个子目录,如
bin、include、lib、share。这是因为每个库(如
Glib)又根据使用目的不同将安装文件进行了划分:bin 是执行文件目录,include
是头文件目录,lib 是库文件目录,share
是库的公用目录,包括本地翻译文件、各种格式的说明文档和例子程序等。
安装完成后,应该立即退出 root 用户,返回到原来的用户状态:
# exit
root 用户权限应该仅在切实需要的时候才使用。很多初学者无论做什么都是以 root
进行,以为这样方便。其实对于新手而言这最是要不得,很容易由于误操作而损坏系统。即使只有你一个人使用一个
Linux 系统,也应该注册一个普通用户、平时以一个普通用户的身份使用系统。
6.2.5 设置
新手往往不清楚为什么要对库进行设置,要进行什么样的设置。为此,在下面介绍了一些有关库的设置的背景知识。如果已经了解了这部分内容,或者急于进行实际的设置操作,可直接转到最后一小节"GTK+
及其依赖库的设置"。
6.2.5.1 搜索路径
上面的安装已经把库的各类文件拷贝到指定的安装目录中了,这个库也就可以被其它程序或者库来使用了。库的使用主要包括两方面的内容:对库的头文件的使用以及对库文件(静态库或共享库)的使用。相应地,库的设置也就是如何对这两类文件进行定位的问题。对文件进行定位通常是用设置文件的搜索路径的方法来解决的。在使用的过程中按照搜索路径的先后顺序查找,第一个找到文件将被使用。
库的头文件在程序中被包含使用,而且仅仅用在程序编译阶段,所以头文件的默认搜索路径是由编译器提供的。处于默认搜索路径内的头文件不需要进行搜索路径的设置即可直接使用。虽然每个编译器提供的头文件的默认搜索路径不尽相同,但是都把
/usr/include
作为默认的搜索路径之一。使用处于默认搜索路径之外的头文件需要在编译的时候通过编译命令的
-I 参数指定其路径。这是对头文件进行定位的方式。
库文件在连接(静态库和共享库)和运行(仅限于使用共享库的程序)时被使用,其搜索路径是在系统中进行设置的。一般
Linux 系统把 /lib 和 /usr/lib
两个目录作为默认的库搜索路径,所以使用这两个目录中的库时不需要进行设置搜索路径即可直接使用。对于处于默认库搜索路径之外的库,需要将库的位置添加到库的搜索路径之中。设置库文件的搜索路径有下列两种方式,可任选其一使用:
1. 在环境变量 LD_LIBRARY_PATH 中指明库的搜索路径。
2. 在 /etc/ld.so.conf 文件中添加库的搜索路径。
需要注意的是:第二种搜索路径的设置方式对于程序连接时的库(包括共享库和静态库)的定位已经足够了,但是对于使用了共享库的程序的执行还是不够的。这是因为为了加快程序执行时对共享库的定位速度,避免使用搜索路径查找共享库的低效率,所以是直接读取库列表文件
/etc/ld.so.cache 从中进行搜索的。/etc/ld.so.cache
是一个非文本的数据文件,不能直接编辑,它是根据 /etc/ld.so.conf
中设置的搜索路径由 /sbin/ldconfig
命令将这些搜索路径下的共享库文件集中在一起而生成的(ldconfig 命令要以 root
权限执行)。因此,为了保证程序执行时对库的定位,在 /etc/ld.so.conf
中进行了库搜索路径的设置之后,还必须要运行 /sbin/ldconfig 命令更新
/etc/ld.so.cache 文件之后才可以。
在程序连接时,对于库文件(静态库和共享库)的搜索路径,除了上面的设置方式之外,还可以通过
-L 参数显式指定。因为用 -L
设置的路径将被优先搜索,所以在连接的时候通常都会以这种方式直接指定要连接的库的路径。
有的使用了共享库的程序,在编译和连接时都很顺利,但是在运行时却发生了找不到共享库的问题,其原因就是库的搜索路径没有设置,或者设置不正确。
6.2.5.2 编译和连接界面
一般来说,如果库的头文件不在 /usr/include 目录中,那么在编译的时候需要用
-I
参数指定其路径。由于同一个库在不同系统上可能位于不同的目录下,用户安装库的时候也可以将库安装在不同的目录下,所以即使使用同一个库,由于库的路径的不同,造成了用
-I
参数指定的头文件的路径也可能不同,其结果就是造成了编译命令界面的不统一。如果使用
-L
参数,也会造成连接界面的不统一。编译和连接界面不统一会为库的使用带来麻烦。
为了解决编译和连接界面不统一的问题,人们找到了一些解决办法。其基本思想就是:事先把库的位置信息等保存起来,需要的时候再通过特定的工具将其中有用的信息提取出来供编译和连接使用。这样,就可以做到编译和连接界面的一致性。其中,目前最为常用的库信息提取工具就是下面介绍的
pkg-config。
6.2.5.3 pkg-config
pkg-config 是通过库提供的一个 .pc
文件获得库的各种必要信息的,包括版本信息、编译和连接需要的参数等。这些信息可以通过
pkg-config 提供的参数单独提取出来直接供编译器和连接器使用。
在默认情况下,每个支持 pkg-config 的库对应的 .pc
文件在安装后都位于安装目录中的 lib/pkgconfig
目录下。例如,我们在上面已经将 Glib 安装在 /opt/gtk 目录下了,那么这个
Glib 库对应的 .pc 文件是 /opt/gtk/lib/pkgconfig 目录下一个叫 glib-2.0.pc
的文件(不妨看看这个文件的内容来获得对 .pc 文件的一些感性认识。)
使用 pkg-config 的 --cflags 参数可以给出在编译时所需要的选项,而 --libs
参数可以给出连接时的选项。例如,假设一个 sample.c 的程序用到了 Glib
库,就可以这样编译:
$ gcc -c `pkg-config --cflags glib-2.0` sample.c
然后这样连接:
$ gcc sample.o -o sample `pkg-config --libs glib-2.0`
或者上面两步也可以合并为以下一步:
$ gcc sample.c -o sample `pkg-config --cflags --libs glib-2.0`
可以看到:由于使用了 pkg-config
工具来获得库的选项,所以不论库安装在什么目录下,都可以使用相同的编译和连接命令,带来了编译和连接界面的统一。
使用 pkg-config 工具提取库的编译和连接参数有两个基本的前提:
1. 库本身在安装的时候必须提供一个相应的 .pc
文件。不这样做的库说明不支持 pkg-config 工具的使用。
2. pkg-config 必须知道要到哪里去寻找此 .pc 文件。
GTK+ 及其依赖库支持使用 pkg-config 工具,所以剩下的问题就是如何告诉
pkg-config 到哪里去寻找库对应的 .pc 文件,这也是通过设置搜索路径来解决的。
6.2.5.4 GTK+ 及其依赖库的设置
6.2.5.4.1 以编译和连接为目的的设置
对于支持 pkg-config 工具的 GTK+
及其依赖库来说,库的头文件的搜索路径的设置变成了对 .pc
文件搜索路径的设置。.pc 文件的搜索路径是通过环境变量 PKG_CONFIG_PATH
来设置的,pkg-config 将按照设置路径的先后顺序进行搜索,直到找到指定的 .pc
文件为止。
Linux 中环境变量的设置方式和使用的 shell 有关。在这里是以 bash
为例进行说明的。如果你发现这里的环境变量的设置方法不能成功的话,应该检查你在当前的终端中使用的是什么
shell:
$ ps --no-headers --format comm $$
注:如果 ps 被定义为别名,可能需要执行 \ps --no-headers --format comm $$
才行。
然后根据这种 shell 的环境变量的设置方法进行设置。如果系统中存在有 bash
的话,也可以将 shell 切换为 bash:
$ bash
这样就可以按照下面介绍的方法设置环境变量了。
安装完 Glib 后,在 bash 中应该进行如下设置:
$ export PKG_CONFIG_PATH=/opt/gtk/lib/pkgconfig:$PKG_CONFIG_PATH
可以执行下面的命令检查是否 /opt/gtk/lib/pkgconfig 路径已经设置在
PKG_CONFIG_PATH 环境变量中:
$ echo $PKG_CONFIG_PATH
这样设置之后,使用 Glib 库的其它程序或库在编译的时候 pkg-config
就知道首先要到 /opt/gtk/lib/pkgconfig 这个目录中去寻找 glib-2.0.pc
了(GTK+ 和其它的依赖库的 .pc
文件也将拷贝到这里,也会首先到这里搜索它们对应的 .pc 文件)。之后,通过
pkg-config 就可以把其中库的编译和连接参数提取出来供程序在编译和连接时使用。
另外还需要注意的是:环境变量的设置只对当前的终端窗口有效。如果到了没有进行上述设置的终端窗口中,pkg-config
将找不到新安装的 glib-2.0.pc 文件、从而可能使后面进行的安装(如 Glib
之后的 Atk 的安装)无法进行。
6.2.5.4.2 以连接和执行为目的的设置
前面已经说明过了,库搜索路径的设置有两种方式:在环境变量 LD_LIBRARY_PATH
中设置以及在 /etc/ld.so.conf 文件中设置。
其中,第二种设置方式需要 root 权限,以改变 /etc/ld.so.conf 文件并执行
/sbin/ldconfig 命令。而且,当系统重新启动后,所有的基于 GTK2
的程序在运行时都将使用新安装的 GTK+ 库。不幸的是,由于 GTK+
版本的改变,这有时会给应用程序带来兼容性的问题,造成某些程序运行不正常。
为了避免出现上面的这些情况,在 GTK+
及其依赖库的安装过程中对于库的搜索路径的设置将采用第一种方式进行。这种设置方式不需要
root 权限,设置也简单:
$ export LD_LIBRARY_PATH=/opt/gtk/lib:$LD_LIBRARY_PATH
可以用下面的命令查看 LD_LIBRAY_PATH 的设置内容:
$ echo $LD_LIBRARY_PATH
至此,库的两种设置就完成了。
由于我们将 GTK+ 及其依赖库设置安装在同一目录中,所以上面的对环境变量
PKG_CONFIG_PATH 和 LD_LIBRAY_PATH
的设置在一个终端窗口中只要进行一次就可以了,以后安装其它库的时候不需要再行设置。
经过以上设置之后,使用了 Glib 的程序(如下面要安装的 Atk)就能够根据在
PKG_CONFIG_PATH 和 LD_LIBRAY_PATH 中设置的搜索路径找到新安装的 Glib
库了。如果不进行上面的设置,或者设置有误,可能找到的是旧版的
Glib,也可能出现找不到 Glib 的错误。
现在,可以执行下面的命令检查 Glib 的版本号:
$ pkg-config --modversion glib-2.0
如果显示的版本号和你进行安装的软件包中的版本号一致,那么恭喜你!
你已经成功地完成了 Glib 库的安装和设置,可以继续进行其它库的安装了。
6.3 其它库的安装
在确认已经成功安装了 Glib 之后,可以顺次安装其它的库。
6.3.1 安装 Atk
参考"安装
Glib"一节中的操作进行。如果始终在同一个终端窗口中操作的话,最后的设置过程可不执行。检查
Atk 的版本号:
$ pkg-config --modversion atk
6.3.2 安装 Cairo
参考"安装
Glib"一节中的操作进行。如果始终在同一个终端窗口中操作的话,最后的设置过程可不执行。检查
Cairo 的版本号:
$ pkg-config --modversion cairo
6.3.3 安装 Pango
参考"安装
Glib"一节中的操作进行。如果始终在同一个终端窗口中操作的话,最后的设置过程可不执行。检查
Pango 的版本号:
$ pkg-config --modversion pango
注意:配置 Pango 成功的另外一个标志是:在 ./configure
最后显示出来的一行信息 backends: FreeType X Xft Cairo 中应该有 Cairo
字样的出现。如果没有,比如象 backends: FreeType X Xft 这样,说明 Pango
的配置不成功;Pango 配置不成功,说明其依赖库 Cairo 没有安装或者 Cairo
库的设置不正确。
6.3.4 安装 Gtk+
参考"安装
Glib"一节中的操作进行。如果始终在同一个终端窗口中操作的话,最后的设置过程可不执行。检查
GTK+ 的版本号:
$ pkg-config --modversion gtk+-2.0
7. 库的使用
7.1 库使用之前的设置
在我们采用的安装方案中,由于是使用环境变量对 GTK+
及其依赖库进行的设置,所以当系统重新启动、或者新开一个终端窗口之后,如果想使用新安装的
GTK+ 库,需要如上面那样重新设置 PKG_CONFIG_PATH 和 LD_LIBRARY_PATH
环境变量。
这种使用 GTK+
的方法,在使用之前多了一个对库进行设置的过程。虽然显得稍微繁琐了一些,但却是一种最安全的使用
GTK+ 库的方式,不会对系统上已经存在的使用了 GTK+ 库的程序(比如 GNOME
桌面)带来任何冲击。
为了使库的设置变得简单一些,可以把下面的这两句设置保存到一个文件中(比如
set_gtk-2.10 文件):
export PKG_CONFIG_PATH=/opt/gtk/lib/pkgconfig:$PKG_CONFIG_PATH
export LD_LIBRARY_PATH=/opt/gtk/lib:$LD_LIBRARY_PATH
之后,就可以用下面的方法进行库的设置了(其中的 source 命令也可以用 .
代替):
$ source set_gtk-2.10
只有在用新版的 GTK+ 库开发应用程序、或者运行使用了新版 GTK+
库的程序的时候,才有必要进行上述设置。
如果想避免使用 GTK+
库之前上述设置的麻烦,可以把上面两个环境变量的设置在系统的配置文件中(如
/etc/profile)或者自己的用户配置文件中(如 ~/.bash_profile)
;库的搜索路径也可以设置在 /etc/ld.so.conf
文件中,等等。这种设置在系统启动时会生效,从而会导致使用 GTK+
的程序使用新版的 GTK+
运行库,这有可能会带来一些问题。当然,如果你发现用新版的 GTK+
代替旧版没有什么问题的话,使用这种设置方式是比较方便的。
7.2 库文档
使用一个库免不了要参考库的文档。GTK+
及其依赖库的各个库的参看文档也被安装,具体位置在安装目录的
share/gtk-doc/html 目录下分别存放。可以用浏览器分别打开每个目录中的
index.html,然后将其添加到网络书签中以便随时参考。