大学被指商业化公司 入学到毕业流程系生产线

xue

新周刊2010009期:可怕的大学

可怕的大学

恢复高考33年来,中国共选拔了5438万名大学生。

千军万马过的不再是独木桥,大学也不再是从前的大学。

近三十年来,中国的大学经历了市场化(如取消毕业分配和实行收费制)、国际化(如“建世界一流大学”和大规模合并)、产业化(如疯狂扩招和建大学城)三大高潮。

它变得像混合了政府和企业功能的奇怪公司:是公共服务,却由家长们高额支出;是产业经营,却背负了2500亿元债务;出售产品,却没有售后服务;是投资,却不保证你的回报。

大学的理念越来越混乱,而其行政管理、评估体系、课程、老师和学生,都出了问题。大学的定位和专业设置同质化严重,从教授到学生的造假舞弊令学术成为笑话。大学的腐败、两性和安全乱象总在社会新闻版出现。中国的大学不再精心培养能独立思考的“人”,而热衷于培养“就业员”。

大学从残酷社会中的象牙塔,堕落成了生产不合格产品的坏公司,这是可怕的事!少数好教师和好学生在此瑜不掩瑕,更多人的青春在此虚掷,梦想没来得及描绘就已挫败,这是可怕的事!大学不教新生代做“人”和做事,只教他们听话和考试,这是可怕的事!大学生相信关系和潜规则,甚于相信知识和能力,这是可怕的事!

2010年,中国957万名考生参加了高考,其中657万人有机会成为大学新生。

中国的大学,将如何面对他们?

它不比收费公路好多少 大学是间坏公司

大学这间公司最“妙”的地方是,它既是公共服务,又是产业经营;它既面向社会,又针对个人。它出售产品,却没有售后服务;它是投资,却并不保证你的回报。

文/陈漠

“我们不需要教育,我们不需要思想控制,教室里不要有黑暗的挖苦。老师,放过那些孩子……”平克·弗洛伊德在音乐专辑《迷墙》中这样愤怒地唱道,那是1979年。

放到如今我们的教育体制中,这些激进的话语已经不太合适了。高中以前,或许还有学生苦闷地处于墙内;而一进入大学,整个环境早就已经完全商业化,所有的一切都按照一个商业目标运转起来——如何把学生作为商品出售出去。

我们的大学更像一间公司,无论好坏。

这间公司是怎么做市场的

一个高三学生如果有幸连续看过这十多年的高考志愿填报手册,一定会晕掉。十多年来风水轮流转,各种热门专业层出不穷,如果大学是公司,那它们就是深圳华强北那些山寨手机厂商,iPhone流行时就做HiPhone,什么流行就一窝蜂仿它、山寨它、做滥它。

早些年流行经济类专业,各个大学学院纷纷开设国际贸易、货币银行学、金融学、应用经济学等等;后来又流行法学,人人都想着进公检法,过“律考”;接下来是工商行政管理、公共关系学、广告学、市场与营销等市场管理专业;没几年风头又转到了IT行业,于是计算机类学科汗牛充栋;然后是影视、艺术、表演、播音与主持类专业挤破了头,连工科院校都敢设影视学院;最后是物流管理、电子商务、艺术品投资管理、房地产经营管理、物业管理、动漫设计等面向新时代的专业热得烫手。

看看十多年来的大学热门专业,几乎就可以看出一个国家的走向。经济数字虽然高亢,可经济学的学生大多不过在当会计和营业员。“律考”通过率低得可以媲美公务员考试,而公检法早就人满为患,没有后台你想都别想。学商科的去卖保健品、搞传销,学计算机的在网吧里当管理员。学表演的如今连潜规则都不一定捞得上了,转年又有一茬水灵的新人。播音主持、影视编导想去电视台?你等着去当栏目聘的编外民工吧。物流管理去快递公司,电子商务在淘宝卖外贸货,动漫设计的要么在熬命,要么在画山寨动画。唯有房地产红旗不倒,如果你运气好早点去卖楼,你就算赢了。

十多年的大学专业热潮,每一次大学里的专业热潮之后都预示社会上这个行业的烂掉。你必须相信这一点,当下最热的专业,等你毕业的时候一定会烂掉,中国的大学有这个能力。因为它们是山寨公司,它们看重的是“快速反应能力”,谁能短平快地抓住热点,大量吃进原料囤积,迅速做出仿版,就算战略上的成功。

这间公司是怎么做运营的

你总算入学了,来到学校之后你才发现,你所在的校区居然离照片上那个著名的学校大门有几十公里远。这还算好的,要是你在挂着一个城市名前缀的校区里,你会发现它在地图上和本校相距上千公里,要用谷歌地图才能览其全貌。

现在的大学流行开分公司,所有大学都到一个地方去开子公司,于是称作大学城。整合资源、辐射效应、集约模式、融资管理、引领发展、促进转型,这些在大学城建设中经常出现的词汇,就如同商业教科书。大学既然是一间公司,大学城自然应该是一盘生意,这盘生意的体量大得惊人,占地动辄几十平方公里,建筑面积动辄几百万上千万平方米,投资金额是天量,流水账目自然也是天量。

有知名大学的生意好做,没有知名大学的城市也有新招,便由政府出面撮合当地二三流学校与外地知名大学联办分校。对于当地来说,可算得上是招商引资,对于外地大学则算是拓展业务。负责一点的,隔三差五有本校教师飞行执教,算是连锁经营;不好的则是自生自灭,留个招牌而已,只能算是授权加盟。

有分自然有合。大学公司的生意还有一种是兼并。院校合并算是我国教育界的一项盛事,学院合并升格为大学,专业性大学合并升格为综合大学,理工大学有人文学院,科技大学有影视学院,更不用说如今几乎每所大学都有医学院。合并风潮据说目的是为了集中师资力量、加强学科水平,为合出几所世界级大学而努力。最高目标自然是全国合为一所大学,如此一来,世界大学排名榜必然会有一所仰之弥高的中国大学,再不济,学生人数也是世界前列。

兼并重组到最后,难道就是公司运营手法的尽头了?不,大学们不久后一定会发现资产剥离、拆分上市其实是资本运营的更好办法,这样合并的大学又可以拆开来各自挂牌,再来一次资产评估、征地搬迁、结构调整、资源重组、院系学科调整。

这间公司的收入与支出

既令人惊讶又在意料之中的是,大学这间公司运用了如此多的手法,最后它的资产负债表却如此不堪入目。

大学负上巨债已经不是新闻,燕山大学、浙江大学城市学院、吉林大学等学校都欠下了几亿、十几亿乃至几十亿的债务,曾有媒体报道的数据说,我国高校负债实际的数字可能在4000亿到5000亿元之间。

这真是任何一个职业经理人的噩梦。但好在大学这间公司,有着不同凡响的翻身策略:“扩招负债,卖地偿还”。以平价征得的教育用地,再以数十倍的价格拍卖出去,偿还债务不成问题。别的公司负债搞到破产,大学这间公司负债还会微赚。

也有大学校长提出,因为“大学不印钞票,也不卖产品,大学的主要产品是人才,但是大学人才送出去是不收费的……培养的人才是无偿供应给社会的”,所以大学的债务应该全社会承担。有总经理如此,董事局主席当笑而不语。

大学固然不印钞票,可学生们的钞票却是源源不断地流进来,教育体制改革中的诸多口号中,高等教育产业化是执行得最有力的,这直接就意味着学费连年上涨,扩招年年实行。大学这间公司的生产方式本来就特殊,先收钱,后办事,赢得口碑继而趋之若鹜。如果,学生进大学要交学费,毕业后有公司买走还要花钱,如同机场高速公路一样,进出都收费,那真算是一大经营创新,世界企业史的一朵奇葩了。

大学这间公司美妙的地方是,它的收入既来自政府拨款,又来自学费等自创营收,它的产出既算是公共服务,又算是商品。这样哭穷、赖债时两头都有道理,排列组合一下有不少选择可用。所以我们看到,既有校长呼吁增加拨款,又有校长抱怨学费太低,硬件不够时怨钱少,就业率低时怪社会,负债时它说自己公共服务,收钱时它又成了产业经营。

当然,最美妙的还在于,它永远不用对资产负债表负责,永远不用对股东负责,永远不用对产品质量负责,永远不用对客户负责。作为普通公民的我们,以纳税人的身份已经为这间公共服务公司缴过税,再为子女入学向这间经营性公司交一次费,然后毕业就业再由自己解决,最后它负债还要所有人一起承担。

即使这样,我们也永远看不到它的账单。

大学的企业文化

没有哪间公司的新闻比大学这间公司更像娱乐新闻了。

这里有学术抄袭,从博导到校长,学术抄袭新闻的频发程度比起音乐圈歌曲抄袭来,有过之而无不及。这里有男女关系的潜规则,从艺术院校到外语院校,年龄跨度、爆料程度比起影视圈来也不遑多让。这里有骂战,有炒作,有勒索,有杀人,有指桑骂槐,有暗箱操作,这里不仅有情色故事,居然还有推理小说。

这就是为什么大学新闻总是能上网站新闻头条——猛料迭出,随便拎个元素出来就是上好的标题。

历数一下近年的大学,从招生、教学、考研、学术、收费、征地、贷款、就业乃至师生关系、校园生活、社会活动,无不是丑闻连连。一间公司做到整条生产线从头到尾全都有新闻亮点,不能不说是企业文化培育的经典案例。

一名学生,自入学到毕业,从原材料到成品,从产品继而变为员工,经历过这间公司完整的生产线。你以学费为投资,试图换取一个未来,终于——恭喜你,你毕业了。

————————–摘录于ifeng.com——————–

linux下单一网卡设置多个IP的方法

MikroTik ROS里的网卡无法设置多个IP段(只有通过实体网卡进行分段管理),但是海蜘蛛的网卡可以实现多个IP段的扩展,偶尔的情况下看了一下海蜘蛛里面的网络卡的配置,

hisprit

Linux下可以实现单一网卡设置多个IP,具体的设置如下:

这里以ubuntu为例,实际地为单一网卡设置多个IP地址:

leekwen@git:~$ lspci
00:00.0 Host bridge: Intel Corporation 440BX/ZX/DX - 82443BX/ZX/DX Host bridge (rev 01)
00:01.0 PCI bridge: Intel Corporation 440BX/ZX/DX - 82443BX/ZX/DX AGP bridge (rev 01)
00:07.0 ISA bridge: Intel Corporation 82371AB/EB/MB PIIX4 ISA (rev 08)
00:07.1 IDE interface: Intel Corporation 82371AB/EB/MB PIIX4 IDE (rev 01)
00:07.3 Bridge: Intel Corporation 82371AB/EB/MB PIIX4 ACPI (rev 08)
00:07.7 System peripheral: VMware Virtual Machine Communication Interface (rev 10)
00:0f.0 VGA compatible controller: VMware SVGA II Adapter
00:10.0 SCSI storage controller: BusLogic BT-946C (BA80C30) [MultiMaster 10] (rev 01)
00:11.0 PCI bridge: VMware PCI bridge (rev 02)
00:15.0 PCI bridge: VMware PCI Express Root Port (rev 01)
00:15.1 PCI bridge: VMware PCI Express Root Port (rev 01)
00:15.2 PCI bridge: VMware PCI Express Root Port (rev 01)
00:15.3 PCI bridge: VMware PCI Express Root Port (rev 01)
00:15.4 PCI bridge: VMware PCI Express Root Port (rev 01)
00:15.5 PCI bridge: VMware PCI Express Root Port (rev 01)
00:15.6 PCI bridge: VMware PCI Express Root Port (rev 01)
00:15.7 PCI bridge: VMware PCI Express Root Port (rev 01)
00:16.0 PCI bridge: VMware PCI Express Root Port (rev 01)
00:16.1 PCI bridge: VMware PCI Express Root Port (rev 01)
00:16.2 PCI bridge: VMware PCI Express Root Port (rev 01)
00:16.3 PCI bridge: VMware PCI Express Root Port (rev 01)
00:16.4 PCI bridge: VMware PCI Express Root Port (rev 01)
00:16.5 PCI bridge: VMware PCI Express Root Port (rev 01)
00:16.6 PCI bridge: VMware PCI Express Root Port (rev 01)
00:16.7 PCI bridge: VMware PCI Express Root Port (rev 01)
00:17.0 PCI bridge: VMware PCI Express Root Port (rev 01)
00:17.1 PCI bridge: VMware PCI Express Root Port (rev 01)
00:17.2 PCI bridge: VMware PCI Express Root Port (rev 01)
00:17.3 PCI bridge: VMware PCI Express Root Port (rev 01)
00:17.4 PCI bridge: VMware PCI Express Root Port (rev 01)
00:17.5 PCI bridge: VMware PCI Express Root Port (rev 01)
00:17.6 PCI bridge: VMware PCI Express Root Port (rev 01)
00:17.7 PCI bridge: VMware PCI Express Root Port (rev 01)
00:18.0 PCI bridge: VMware PCI Express Root Port (rev 01)
00:18.1 PCI bridge: VMware PCI Express Root Port (rev 01)
00:18.2 PCI bridge: VMware PCI Express Root Port (rev 01)
00:18.3 PCI bridge: VMware PCI Express Root Port (rev 01)
00:18.4 PCI bridge: VMware PCI Express Root Port (rev 01)
00:18.5 PCI bridge: VMware PCI Express Root Port (rev 01)
00:18.6 PCI bridge: VMware PCI Express Root Port (rev 01)
00:18.7 PCI bridge: VMware PCI Express Root Port (rev 01)
02:00.0 USB Controller: Intel Corporation 82371AB/EB/MB PIIX4 USB
02:01.0 Ethernet controller: Advanced Micro Devices [AMD] 79c970 [PCnet32 LANCE] (rev 10)
02:02.0 USB Controller: VMware USB2 EHCI Controller
leekwen@git:~$ ifconfig eth0
eth0      Link encap:Ethernet  HWaddr 00:0c:29:e4:30:eb
          inet addr:192.168.2.6  Bcast:192.168.2.255  Mask:255.255.255.0
          inet6 addr: fe80::20c:29ff:fee4:30eb/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:69541 errors:3 dropped:0 overruns:0 frame:0
          TX packets:70750 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:28858018 (28.8 MB)  TX bytes:7091220 (7.0 MB)
          Interrupt:19 Base address:0x2000


leekwen@git:~$ sudo vi /etc/network/interfaces
[sudo] password for leekwen:
leekwen@git:~$ cat /etc/network/interfaces
auto lo
iface lo inet loopback
auto eth0
iface eth0 inet static
address 192.168.2.6
gateway 192.168.2.1
netmask 255.255.255.0
network 192.168.2.0
broadcast 192.168.2.255
###Add VM eth0:0 netcard####
auto eth0:0
iface eth0:0 inet static
address 192.168.10.2
netmask 255.255.255.0
network 192.168.10.0
broadcast 192.168.10.255


leekwen@git:~$ sudo /etc/init.d/networking restart
[sudo] password for leekwen:
 * Reconfiguring network interfaces...            
 RTNETLINK answers: No such process
                                                          [ OK ]
leekwen@git:~$ ifconfig -a
eth0      Link encap:Ethernet  HWaddr 00:0c:29:e4:30:eb
          inet addr:192.168.2.6  Bcast:192.168.2.255  Mask:255.255.255.0
          inet6 addr: fe80::20c:29ff:fee4:30eb/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:70050 errors:3 dropped:0 overruns:0 frame:0
          TX packets:71159 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:28909729 (28.9 MB)  TX bytes:7137583 (7.1 MB)
          Interrupt:19 Base address:0x2000 

eth0:0    Link encap:Ethernet  HWaddr 00:0c:29:e4:30:eb
          inet addr:192.168.10.2  Bcast:192.168.10.255  Mask:255.255.255.0
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          Interrupt:19 Base address:0x2000 

lo        Link encap:Local Loopback
          inet addr:127.0.0.1  Mask:255.0.0.0
          inet6 addr: ::1/128 Scope:Host
          UP LOOPBACK RUNNING  MTU:16436  Metric:1
          RX packets:92 errors:0 dropped:0 overruns:0 frame:0
          TX packets:92 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0
          RX bytes:9008 (9.0 KB)  TX bytes:9008 (9.0 KB) 

leekwen@git:~$ ping 192.168.10.2
PING 192.168.10.2 (192.168.10.2) 56(84) bytes of data.
64 bytes from 192.168.10.2: icmp_seq=1 ttl=64 time=0.785 ms
64 bytes from 192.168.10.2: icmp_seq=2 ttl=64 time=0.057 ms
^C
--- 192.168.10.2 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1002ms
rtt min/avg/max/mdev = 0.057/0.421/0.785/0.364 ms
leekwen@git:~$ ping 192.168.2.6
PING 192.168.2.6 (192.168.2.6) 56(84) bytes of data.
64 bytes from 192.168.2.6: icmp_seq=1 ttl=64 time=0.077 ms
64 bytes from 192.168.2.6: icmp_seq=2 ttl=64 time=0.029 ms
^C
--- 192.168.2.6 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1000ms
rtt min/avg/max/mdev = 0.029/0.053/0.077/0.024 ms
leekwen@git:~$ route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
192.168.2.0     0.0.0.0         255.255.255.0   U     0      0        0 eth0
192.168.10.0    0.0.0.0         255.255.255.0   U     0      0        0 eth0
169.254.0.0     0.0.0.0         255.255.0.0     U     1000   0        0 eth0
0.0.0.0         192.168.2.1     0.0.0.0         UG    100    0        0 eth0

MikroTik ROS 中IP的设置方法

[admin@MikroTik]> /ip address add address=192.168.2.8/24 interface=wan;         
[admin@MikroTik] > /ip address add address=192.168.16.1/24 interface=lan;        
[admin@MikroTik] > /ip address print         
Flags: X – disabled, I – invalid, D – dynamic
 #         ADDRESS               NETWORK         BROADCAST       INTERFACE                  
 0   192.168.2.8/24     192.168.2.0     192.168.2.255         wan                        
 1   192.168.16.1/24    192.168.16.0    192.168.16.255     lan    

[admin@MikroTik] /ip> address print
Flags: X – disabled, I – invalid, D – dynamic
 #         ADDRESS            NETWORK            BROADCAST       INTERFACE                  
 0   192.168.2.8/24     192.168.2.0        192.168.2.255        wan                        
 1   192.168.16.1/24    192.168.16.0    192.168.16.255       lan                        
[admin@MikroTik] /ip> address remove 0; address remove 1;
[admin@MikroTik] /ip> address
[admin@MikroTik] /ip address> add address=192.168.2.8/24 interface=wan; add address=192.168.16.1/24 interface=lan;
[admin@MikroTik] /ip address> print
Flags: X – disabled, I – invalid, D – dynamic
 #           ADDRESS               NETWORK         BROADCAST       INTERFACE                  
 0   192.168.2.8/24     192.168.2.0         192.168.2.255        wan                        
 1   192.168.16.1/24    192.168.16.0      192.168.16.255      lan

MikroTik-ROS的interface使用方法

安装好的ROS,因为没有设置IP地址,所以第一次只能通过终端或者实体机本身去配置。

这里使用winbox来进入:

先进行搜索,看到有mac地址及对应的MikroTik信息的时候就可以点击它进入winbox界面。

进入之后,如果第一次没有跳出License的话,说明破解已经OK了。

然后启动一个Terminal:

[admin@MikroTik] > /interface printFlags: D - dynamic, X - disabled, R - running, S - slave 							
 #     NAME          TYPE             MTU   L2MTU							
 0  R  ether1        ether            1500 							
 1  R  ether2        ether            1500 							
[admin@MikroTik] > /interface monitor-traffic ether1;							
  rx-packets-per-second: 6							
    rx-drops-per-second: 0							
   rx-errors-per-second: 0							
     rx-bits-per-second: 4.2kbps							
  tx-packets-per-second: 7							
    tx-drops-per-second: 0							
   tx-errors-per-second: 0							
     tx-bits-per-second: 12.9kbps							
-- [Q quit|D dump|C-z pause]
[admin@MikroTik] > /interface set ether1 name=lan;
[admin@MikroTik] > /interface set ether2 name=wan;
[admin@MikroTik] > /interface print ;
Flags: D - dynamic, X - disabled, R - running, S - slave 
 #     NAME          TYPE             MTU   L2MTU
 0  R  lan           ether            1500 
 1  R  wan           ether            1500 
[admin@MikroTik] > /interface enable 0;
[admin@MikroTik] > /interface enable 1;
[admin@MikroTik] > /interface enable lan;
[admin@MikroTik] > /interface enable wan; 
[admin@MikroTik] > /interface enable lan ;
[admin@MikroTik] > /interface disable lan ;

[admin@MikroTik] > /interface 
[admin@MikroTik] /interface> set local name=lan;set public name=wan;
[admin@MikroTik] /interface> /
[admin@MikroTik] > /interface 
[admin@MikroTik] /interface> ..
[admin@MikroTik] > 

[admin@ROSv3.30] > interface ethernet print detail 							
Flags: X - disabled, R - running, S - slave 							
 0 R  name="Local" mtu=1500 mac-address=00:0C:29:BD:24:78 arp=enabled 							
      disable-running-check=yes auto-negotiation=yes full-duplex=yes 							
      cable-settings=default speed=100Mbps 							

 1 R  name="Public" mtu=1500 mac-address=00:0C:29:BD:24:82 arp=enabled 							
      disable-running-check=yes auto-negotiation=yes full-duplex=yes 							
      cable-settings=default speed=100Mbps 							
[admin@ROSv3.30] /interface ethernet> monitor Local,Public 							
                   status: link-ok  link-ok							
    default-cable-setting: standard standard							
[admin@ROSv3.30] /interface ethernet> set Local mac-address=00:11:00:11:11:3C[admin@ROSv3.30] /interface ethernet> print detail 							
Flags: X - disabled, R - running, S - slave 							
 0 R  name="Local" mtu=1500 mac-address=00:11:00:11:11:3C arp=enabled 							
      disable-running-check=yes auto-negotiation=yes full-duplex=yes 							
      cable-settings=default speed=100Mbps 							

 1 R  name="Public" mtu=1500 mac-address=00:0C:29:BD:24:82 arp=enabled 							
      disable-running-check=yes auto-negotiation=yes full-duplex=yes 							
      cable-settings=default speed=100Mbps 							
[admin@ROSv3.30] /interface ethernet> set Local mac-address=00:0C:29:BD:24:78							
[admin@ROSv3.30] /interface ethernet> print detail 							
Flags: X - disabled, R - running, S - slave 							
 0 R  name="Local" mtu=1500 mac-address=00:0C:29:BD:24:78 arp=enabled 							
      disable-running-check=yes auto-negotiation=yes full-duplex=yes 							
      cable-settings=default speed=100Mbps 							

 1 R  name="Public" mtu=1500 mac-address=00:0C:29:BD:24:82 arp=enabled 							
      disable-running-check=yes auto-negotiation=yes full-duplex=yes 							
      cable-settings=default speed=100Mbps 

                       
在interface里可以做以下的操作:修改网卡的名称,MAC地址,启用或禁用网络接口卡等操作!

MikroTik ROS 的安装

MikroTik ROS是一个软路由的操作系统,如果你是windows的忠实FANs的话,建议不要使用,因为这个东西与Linux有不解之缘,所以如果你是windows的FNAs还是使用中国自己的海蜘蛛比较好。推荐你使用海蜘蛛6.15 ISP破解版,由EYOO ROUTER提供的光盘,由vision的Muddyboot破解工具破解注册(实际上这个版本也是在6.10的基础上进行的修改),网络上有很多都是提供这个下载,所以请使用这个。后门的话,请修改muddyboot的密码并屏蔽2345号端口即可,网络上关于这个的使用及破解方法有详细的说明,我在这里就不用多说了。

这里主要说一下MikroTik ROS的安装,网络上关于这个的也有很多破解版,经典的有2.9.27,3.20和3.30,这里我使用的是3.30 img自动重启注册版,这个自动算号不用手工去输入root –key去手工算号了.

下面简单说一下安装的方法:

这是一个实验的环境,真实的环境与此环境相同。

1.ROS的选择与安装   
   ROS的版本的选择,现在网上比较流利的两个版本号为ROSv2.9.27及ROSv3.30
   这里我们选择ROSv3.30来进行安装和使用。
2.所需软件:
    PE工具盘    <—-这里选择的是深度PEv4.0
    ROSv3.30    <—-这里选择的是ROSv3.30_256.img
    Winbox工具  <—-这里选择的是WinBox3.30中英双语版
    putty工具   
    Vmware虚拟机软件环境
    UltraISO
3.Vmware虚拟环境创建
3.1 增加两张网卡,并将其设置为桥接模式;
3.2 修改深度PE盘,增加ROSv3.30安装镜像ROSv3.3_256M.img及写盘工具phywritedisk.exe
3.3 设置CDROM引导(ISO引导的方式)
3.4 进入PE后,调用cmd命令:
  cmd > cd ROS-install
  cmd > phywritedisk -u ROSv3.3_256M.img
  选择磁盘磁道:0
3.5 关闭虚拟机,移除CDROM
3.6 启动虚拟机,稍等片刻后(此时正在算号,算号完毕后会再次重启),虚拟机会再次重启,至此破解的ROSv3.30安装完毕。

Ubuntu默认启动进入文本模式

ubuntu默认进入文本模式的修改方法:

1。首先,查看自己所安装的ubuntu版本:

leekwen@kwenty:~$ cat /etc/issue
Ubuntu 9.10 \n \l
2。根据不同的版本,操作方法有所不同,具体如下:

ubuntu 10.10以前的操作方法:

2.1 第一步,具体命令及操作如下:

leekwen@kwenty:~$ sudo vi /etc/init/rc-sysinit.conf

env DEFAULT_RUNLEVEL=3   <——将原来的env DEFAULT_RUNLEVEL=2修改为env DEFAULT_RUNLEVEL=3

2.2 第二步,具体命令及操作如下:

leekwen@kwenty:~$ sudo vi /etc/init/gdm.conf

start on runlevel [245]   <——增加此行
stop on runlevel [0136]  <—–将原来的stop on runlevel [016]修改为stop on runlevel [0136]

2.3 第三步,具体命令及操作如下:

leekwen@kwenty:~$ sudo reboot <———–重新启动即可

如果在本地机器上进入后,想切换到图形界面,可直接输入startx即可。

ubuntu 10.10以后的操作方法:

1): 运行 sudo gedit /etc/default/grub

2): 找到 GRUB_CMDLINE_LINUX_DEFAULT=”quiet splash”

3): 改为 GRUB_CMDLINE_LINUX_DEFAULT=”quiet splash text”

4): 运行 sudo update-grub

上面把“GRUB_CMDLINE_LINUX_DEFAULT=”quiet splash””中的”quiet splash”改为text也是文本登陆的!

外国网站上的方法:

#start on (filesystem
#       and started dbus
#          and (graphics-device-added fb0 PRIMARY_DEVICE_FOR_DISPLAY=1
#             or drm-device-added card0 PRIMARY_DEVICE_FOR_DISPLAY=1
#            or stopped udevtrigger))start on runlevel [24]stop on runlevel [0136]

git使用说明

Linus为Linux Kernel Project发起的版本控制项目。

HEAD代表当前最新状态。

tag为某个状态的标签。

SHA1为每个提交日志的唯一标识。

ubuntu install git :

apt-get install git-core

git clone:

git仓库可以使用git clone获得:

git clone git://url

也可以通过浏览器浏览。

http://url/gitweb/

通过git pull更新仓库,使用git init初始化自己的仓库。

config:

开发人员需要为git仓库配置相关信息,这样在提交代码时,这些信息会自动

反映在git仓库的日志中。

git config user.name "your name"

git config user.email yourname@email_server

git config core.editor vim

git config core.paper "less -N"

git config color.diff true

git config alias.co checkout

git config alias表示,可以用git co代表git checkout。git var -l可以查看已经设置的配置。

diff:

开发人员在本地进行开发后,可以使用git diff查看改动。

除了直接比较当前开发后的改动外,git diff还可以:

git diff tag                    比较tag和HEAD之间的不同。

git diff tag file               比较一个文件在两者之间的不同。

git diff tag1..tag2             比较两个tag之间的不同。

git diff SHA11..SHA12           比较两个提交之间的不同。

git diff tag1 tag2 file or

git diff tag1:file tag2:file    比较一个文件在两个tag之间的不同。

ORIG_HEAD用于指向前一个操作状态,因此在git pull之后如果想得到pull的

内容就可以:

git diff ORIG_HEAD

git diff --stat                 用于生成统计信息。

git diff --stat ORIG_HEAD

apply:

git apply相当于patch命令。

--check 检查能否正常打上补丁,-v verbose模式, -R reverse模式,反打补丁。

log:

git log file                    查看一个文件的改动。

git log -p                      查看日志和改动。

git log tag1..tag2              查看两个tag之间的日志。

git log -p tag1..tag2 file      查看一个文件在两个tag之间的不同。

git log tag..                   查看tag和HEAD之间的不同。

commit:

git commit -a -e        提交全部修改文件,并调用vim编辑提交日志。

git reset HEAD^ or

git reset HEAD~1        撤销最后一次提交。

git reset --hard HEAD^  撤销最后一次提交并清除本地修改。

git reset SHA1          回到SHA1对应的提交状态。

add/delete/ls:

git add -a              添加所有文件。除了.gitignore文件中的文件。

git rm file             从git仓库中删除文件。

git commit              添加或是删除后要提交。

git ls-files -m         显示修改过的文件。

git ls-files            显示所有仓库中的文件。

git中有四种对象:blob、tree、commit、tag。

blob代表文件,tree代表目录,commit代表提交历史,tag代表标签。

这四种对象都是由SHA1值表示的。在仓库的.git目录中保存了git管理仓库

所需要的全部信息。

git ls-tree HEAD file   显示file在HEAD中的SHA1值。

git cat-file -t SHA1    显示一个SHA1的类型。

git cat-file type SHA1  显示一个SHA1的内容。type是blob、tree、commit、tag之一。

patch:

git format-patch -1     生成最后一个提交对应的patch文件。

git am < patch          把一个patch文件加入git仓库中。

git am --resolved       如果有冲突,在解决冲突后执行。

git am --skip           放弃当前git am所引入的patch。

conflict:

git merge               用于合并两个分支。

git diff                如果有冲突,直接使用diff查看,

冲突代码用<<<和>>>表示。手动修改冲突代码。

git update-index        更新修改后的文件状态。

git commit -a -e        提交为解决冲突而修改的代码。

branch:

git branch -a           查看所有分支。

git branch new_branch   创建新的分支。

git branch -d branch    删除分支。

git checkout branch     切换当前分支。-f参数可以覆盖未提交内容。

daemon:

有时更新公共代码仓库使用patch的方式,或者直接

用git pull git://ip/repo branch

的方式更新每个人的代码。使用git pull的方式需要

提交代码的机器运行:

git daemon --verbose --export-all --enable=receive-pack --base-path=/repo

request-pull:

git request-pull start url      用于产生本次pull请求的统计信息。

clean:

git clean -dxf          用于清除未跟踪文件。

git clean -dnf          可以显示需要删除的文件,但不包括被.gitignore忽略的。

git reset --hard HEAD   用于清除跟踪文件的修改。

<<<<<<<<<<<<<<<<<<<<<<<<<<<摘录于welfear>>>>>>>>>>>>>>>>>>>>>>>>>>>>

数据结构(C语言清华版)第十四讲

上接第十三讲:

上次讲到了数组的转置,在转置的时候,最重要的一点就是确定新的转置的数组的第一个行号位序的问题.

如果能够确定这个转置数据的位序,下一步就是直接地将数据放置到对应的位序上即可.

下面讲一下位序的确定:

如图:

image 

M.data的第一行,对应的应该是T.data的第二行的第一个非零元,在T.data的位序应该为3.

为什么为3呢?是因为在M.data里面的列号为1的非零元只有两个,所以应该从这两个后面紧跟着排列.

所以对M.data数据元素的分析就可以确定在T.data的各个行的非零元的位序.

在M.data里面的以行号为主序对应T.data里面的应该是以列号为辅序.

在M.data里面的以列号为辅序的对应到T.data里面的应该是以行号为主序.

在M.data中以列号相同,行号在前的非零元,在T.data里面就应该是同一行的第一个非零元.

而且再根据其在M.data里面的个数来确定在T.data里面的位序位置,从而实现M到T的一一对应.

要分析M.data的列的个数来确定T.data的行和行号.

列号为1的第一个非零元素———–>转置过去为T.data第一行的第一个非零元

找出在M.data第一次出现的元素,在转置数组里当然为第一行的第一个元素,所以放在第一个位置;

列号为2的第一个非零元素———–>转置过去为T.data第二行的第一个非零元

先计算M.data数组中列号为2的元素的个数,将其加上第二行的第一个元素的位序,就得到转置数组第二行的第一个元素的位序.

列号为3的第一个非零元素———–>转置过去为T.data第三行的第一个非零元

先计算M.data数组中列号为3的元素的个数,将其个数加1,得到转置数组第二行的第一个元素的位序.

依次类推就可以得到转置数组T.data的各个行的首元素的位序.

具体的算法如下:

Status FastTransposeSmatrix(TSMatrix M, TSMatrix &T){

T.mu = M.nu;     T.nu = M.mu;    T.tu = M. tu;

if (T.tu){

for (col=1; col<=M.nu; ++col)  num[col]=0; //统计每一列非零元的个数,初值为0

for ( t = 1; t<= M.tu; ++t) ++num[M.data[t],[j];

cpot[1] = 1;

for (col = 2; col<= M.nu; ++col)

    cpot[col] = cpot[col-1]+ num[col-1]; //转置矩阵中每一行第一个非零元素的位序

for(p=1; p<=M.tu; ++p) {

…………………        

}//for

}//if

return ok;

}//FastTransposeSMatrix

算法的时间复杂度是与这个矩阵的非零的个数成正比的.

三元组顺序表又称有序的双下标法,它的特点是,非零元在表中按行序有序存储,因此便于进行依行顺序处理的矩阵运算。然而,若需随机存取某一行中的非零元,则需从头开始进行查找,相对来说,随机存取的时候,是很不方便的,所以修改三元组顺序表的结构,也就是以行逻辑链接的顺序表。

二、行逻辑链接的顺序表

修改前述的稀疏矩阵的结构定义,增加一个为数据成员rpos,其值在稀疏矩阵的初始化函数中确定。

#define MAXMN 500

typedef sturct {

Triple data[MAXSIZE+1];

int rpos [MAXMN+1];

int mu,nu,tu;

}RLSMatrix;  //行逻辑链接顺序表类型

下面以两个矩阵相乘的例子来看一下行逻辑链接顺序表的好处:

矩阵乘法的经典算法:

for( i=1; i<=m1; ++i)

    for(j=1; j<=n2; ++j){

       Q[i][j] = 0;

    for(k=1; k<=n1; ++k)

       Q[i][j] += M[i][k] * N[k][j];

}

分析上述算法,得到下列的结果:

1)、如果Q[i][j]不为零,那么M[i][j] 和N[k][j]其中一个不为零,所以只去操作M[i][j]和N[k][j]的非零元即可。

2)、如果得到Q[i][j]的结果的话,只需要操作M里的[i]行[k]列元素与N里的[k]行[j]列元素即可。

3)、在对M矩阵处理的时候,是按顺序进行处理的。如果想得到Q里的第[i]行第[j]列,只需要得到M里的第[i]行,N里的第[j]列,不能依次得到Q里的元素,而只能通过累加的方式得到Q里的元素。

image

image这个算法的时间复杂度为:O(m1*n2*n1)

两个稀疏矩阵相乘(Q=M*N)的过程大致描述如下:

Q 初始化:

if Q是非零矩阵{  //逐行求积

  for (arow =1; arow <=M.mu; ++arow){   //处理M的每一行

ctemp[]=0;  //累加器清零

计算Q中第arrow行的积并存入ctemp[]中;

将ctemp[]中非零元压缩存储到Q.data;

     } //for arow

}//if

Status MultSMatrix

(RLSMatrix M,RLSMatrix N,RLSMatrix&Q) {

if (M.nu!= N.mu) return ERROR;

Q.mu = M.nu;  Q.nu = N.nu; Q.tu = 0;

     if (M.tu*N.tu != 0) {

for (arow =1; arow <=M.mu; ++arow) {   //处理每一行的非零元

ctemp[]=0;

Q.rpos[arow] = Q.tu+1;

for (p=M.rpos[arow]; p<M.rpos[arow+1]; ++p){ //对当前行中第一个非零元

brow = M.data[p].j;

if (brow <N.nu) t = N.rpos[brow+1];

      else  { t = N.tu+1 };

for (q = N.rpos[brow]; q<t; ++q){

           ccol = N.data[q].j;

           ctemp[ccol]+= M.data[p].e*N.data[q].e;

}//for q

}// 求得Q中第crow(=arow)行的非零元

for (ccol = 1; ccol <=Q.nu; ++ccol)

if (ctemp[ccol]){

if (++Q.tu > MAXSIZE) return ERROR;

Q.data[Q.tu] = {arow,ccol,ctemp[ccol]};

}//if

        }//for arow

    }//if

    return OK;

}//MultSMatrix

分析上述算法的时间复杂度

累加器ctemp初始化的时间复杂度为:O(M.mu*N.nu); //M.行数*N.列数

求Q的所有非零元的时间复杂度为:O(M.tu*N.tu/N.mu)//M.非零元个数*N.非零元个数/N.行数

进行压缩存储的时间复杂度为:O(M.mu*N.nu);

总的时间复杂度就是:O(M.mu*N.nu+M.tu*N.tu/N.mu)

O(M.mu*N.nu)

若M是m行n列的稀疏矩阵,N是n行p列的稀疏矩阵,

则M中非零元的个数M.tu = δm*m*n;

   N中非零元的个数N.tu = δN*n*p;

相乘算法的时间复杂度就是:O(m*p*(1+n*δm*δn)),

当δm<0.05和δn<0.05及n<1000时,相乘的算法的时间复杂度就相当于O(m*p).

————————————–第十四讲完毕———————————————-

Linux2.6 内核的 Initrd 机制解析

1.什么是 Initrd

initrd 的英文含义是 boot loader initialized RAM disk,就是由 boot loader 初始化的内存盘。在 linux内核启动前, boot loader 会将存储介质中的 initrd 文件加载到内存,内核启动时会在访问真正的根文件系统前先访问该内存中的 initrd 文件系统。在 boot loader 配置了 initrd 的情况下,内核启动被分成了两个阶段,第一阶段先执行 initrd 文件系统中的"某个文件",完成加载驱动模块等任务,第二阶段才会执行真正的根文件系统中的 /sbin/init 进程。这里提到的"某个文件",Linux2.6 内核会同以前版本内核的不同,所以这里暂时使用了"某个文件"这个称呼,后面会详细讲到。第一阶段启动的目的是为第二阶段的启动扫清一切障爱,最主要的是加载根文件系统存储介质的驱动模块。我们知道根文件系统可以存储在包括IDE、SCSI、USB在内的多种介质上,如果将这些设备的驱动都编译进内核,可以想象内核会多么庞大、臃肿。

Initrd 的用途主要有以下四种:

1. linux 发行版的必备部件

linux 发行版必须适应各种不同的硬件架构,将所有的驱动编译进内核是不现实的,initrd 技术是解决该问题的关键技术。Linux 发行版在内核中只编译了基本的硬件驱动,在安装过程中通过检测系统硬件,生成包含安装系统硬件驱动的 initrd,无非是一种即可行又灵活的解决方案。

2. livecd 的必备部件

同 linux 发行版相比,livecd 可能会面对更加复杂的硬件环境,所以也必须使用 initrd。

3. 制作 Linux usb 启动盘必须使用 initrd

usb 设备是启动比较慢的设备,从驱动加载到设备真正可用大概需要几秒钟时间。如果将 usb 驱动编译进内核,内核通常不能成功访问 usb 设备中的文件系统。因为在内核访问 usb 设备时, usb 设备通常没有初始化完毕。所以常规的做法是,在 initrd 中加载 usb 驱动,然后休眠几秒中,等待 usb设备初始化完毕后再挂载 usb 设备中的文件系统。

4. 在 linuxrc 脚本中可以很方便地启用个性化 bootsplash。


2.Linux2.4内核对 Initrd 的处理流程

为了使读者清晰的了解Linux2.6内核initrd机制的变化,在重点介绍Linux2.6内核initrd之前,先对linux2.4内核的initrd进行一个简单的介绍。Linux2.4内核的initrd的格式是文件系统镜像文件,本文将其称为image-initrd,以区别后面介绍的linux2.6内核的cpio格式的initrd。 linux2.4内核对initrd的处理流程如下:

1. boot loader把内核以及/dev/initrd的内容加载到内存,/dev/initrd是由boot loader初始化的设备,存储着initrd。

2. 在内核初始化过程中,内核把 /dev/initrd 设备的内容解压缩并拷贝到 /dev/ram0 设备上。

3. 内核以可读写的方式把 /dev/ram0 设备挂载为原始的根文件系统。

4. 如果 /dev/ram0 被指定为真正的根文件系统,那么内核跳至最后一步正常启动。

5. 执行 initrd 上的 /linuxrc 文件,linuxrc 通常是一个脚本文件,负责加载内核访问根文件系统必须的驱动, 以及加载根文件系统。

6. /linuxrc 执行完毕,真正的根文件系统被挂载。

7. 如果真正的根文件系统存在 /initrd 目录,那么 /dev/ram0 将从 / 移动到 /initrd。否则如果 /initrd 目录不存在, /dev/ram0 将被卸载。

8. 在真正的根文件系统上进行正常启动过程 ,执行 /sbin/init。 linux2.4 内核的 initrd 的执行是作为内核启动的一个中间阶段,也就是说 initrd 的 /linuxrc 执行以后,内核会继续执行初始化代码,我们后面会看到这是 linux2.4 内核同 2.6 内核的 initrd 处理流程的一个显著区别。


3.Linux2.6 内核对 Initrd 的处理流程

linux2.6 内核支持两种格式的 initrd,一种是前面第 3 部分介绍的 linux2.4 内核那种传统格式的文件系统镜像-image-initrd,它的制作方法同 Linux2.4 内核的 initrd 一样,其核心文件就是 /linuxrc。另外一种格式的 initrd 是 cpio 格式的,这种格式的 initrd 从 linux2.5 起开始引入,使用 cpio 工具生成,其核心文件不再是 /linuxrc,而是 /init,本文将这种 initrd 称为 cpio-initrd。尽管 linux2.6 内核对 cpio-initrd和 image-initrd 这两种格式的 initrd 均支持,但对其处理流程有着显著的区别,下面分别介绍 linux2.6 内核对这两种 initrd 的处理流程。

cpio-initrd 的处理流程

1. boot loader 把内核以及 initrd 文件加载到内存的特定位置。

2. 内核判断initrd的文件格式,如果是cpio格式。

3. 将initrd的内容释放到rootfs中。

4. 执行initrd中的/init文件,执行到这一点,内核的工作全部结束,完全交给/init文件处理。

image-initrd的处理流程

1. boot loader把内核以及initrd文件加载到内存的特定位置。

2. 内核判断initrd的文件格式,如果不是cpio格式,将其作为image-initrd处理。

3. 内核将initrd的内容保存在rootfs下的/initrd.image文件中。

4. 内核将/initrd.image的内容读入/dev/ram0设备中,也就是读入了一个内存盘中。

5. 接着内核以可读写的方式把/dev/ram0设备挂载为原始的根文件系统。

6. .如果/dev/ram0被指定为真正的根文件系统,那么内核跳至最后一步正常启动。

7. 执行initrd上的/linuxrc文件,linuxrc通常是一个脚本文件,负责加载内核访问根文件系统必须的驱动, 以及加载根文件系统。

8. /linuxrc执行完毕,常规根文件系统被挂载

9. 如果常规根文件系统存在/initrd目录,那么/dev/ram0将从/移动到/initrd。否则如果/initrd目录不存在, /dev/ram0将被卸载。

10. 在常规根文件系统上进行正常启动过程 ,执行/sbin/init。

通过上面的流程介绍可知,Linux2.6内核对image-initrd的处理流程同linux2.4内核相比并没有显著的变化, cpio-initrd的处理流程相比于image-initrd的处理流程却有很大的区别,流程非常简单,在后面的源代码分析中,读者更能体会到处理的简捷。

4.cpio-initrd同image-initrd的区别与优势

没有找到正式的关于cpio-initrd同image-initrd对比的文献,根据笔者的使用体验以及内核代码的分析,总结出如下三方面的区别,这些区别也正是cpio-initrd的优势所在:

cpio-initrd的制作方法更加简单

cpio-initrd的制作非常简单,通过两个命令就可以完成整个制作过程

#假设当前目录位于准备好的initrd文件系统的根目录下
bash# find . | cpio -c -o > ../initrd.img
bash# gzip ../initrd.img

而传统initrd的制作过程比较繁琐,需要如下六个步骤

#假设当前目录位于准备好的initrd文件系统的根目录下

bash# dd if=/dev/zero of=../initrd.img bs=512k count=5

bash# mkfs.ext2 -F -m0 ../initrd.img

bash# mount -t ext2 -o loop ../initrd.img  /mnt

bash# cp -r  * /mnt

bash# umount /mnt

bash# gzip -9 ../initrd.img

 

本文不对上面命令的含义作细节的解释,因为本文主要介绍的是linux内核对initrd的处理,对上面命令不理解的读者可以参考相关文档。

cpio-initrd的内核处理流程更加简化

通过上面initrd处理流程的介绍,cpio-initrd的处理流程显得格外简单,通过对比可知cpio-initrd的处理流程在如下两个方面得到了简化:

1. cpio-initrd并没有使用额外的ramdisk,而是将其内容输入到rootfs中,其实rootfs本身也是一个基于内存的文件系统。这样就省掉了ramdisk的挂载、卸载等步骤。

2. cpio-initrd启动完/init进程,内核的任务就结束了,剩下的工作完全交给/init处理;而对于image-initrd,内核在执行完/linuxrc进程后,还要进行一些收尾工作,并且要负责执行真正的根文件系统的/sbin/init。通过图1可以更加清晰的看出处理流程的区别:

图1内核对cpio-initrd和image-initrd处理流程示意图

image001 

cpio-initrd的职责更加重要

如图1所示,cpio-initrd不再象image-initrd那样作为linux内核启动的一个中间步骤,而是作为内核启动的终点,内核将控制权交给cpio-initrd的/init文件后,内核的任务就结束了,所以在/init文件中,我们可以做更多的工作,而不比担心同内核后续处理的衔接问题。当然目前linux发行版的cpio-initrd的/init文件的内容还没有本质的改变,但是相信initrd职责的增加一定是一个趋势。


5.linux2.6内核initrd处理的源代码分析

上面简要介绍了Linux2.4内核和2.6内核的initrd的处理流程,为了使读者对于Linux2.6内核的initrd的处理有一个更加深入的认识,下面将对Linuxe2.6内核初始化部分同initrd密切相关的代码给予一个比较细致的分析,为了讲述方便,进一步明确几个代码分析中使用的概念:

rootfs: 一个基于内存的文件系统,是linux在初始化时加载的第一个文件系统,关于它的进一步介绍可以参考文献[4]。

initramfs: initramfs同本文的主题关系不是很大,但是代码中涉及到了initramfs,为了更好的理解代码,这里对其进行简单的介绍。Initramfs是在 kernel 2.5中引入的技术,实际上它的含义就是:在内核镜像中附加一个cpio包,这个cpio包中包含了一个小型的文件系统,当内核启动时,内核将这个cpio包解开,并且将其中包含的文件系统释放到rootfs中,内核中的一部分初始化代码会放到这个文件系统中,作为用户层进程来执行。这样带来的明显的好处是精简了内核的初始化代码,而且使得内核的初始化过程更容易定制。Linux 2.6.12内核的 initramfs还没有什么实质性的东西,一个包含完整功能的initramfs的实现可能还需要一个缓慢的过程。对于initramfs的进一步了解可以参考文献[1][2][3]。

cpio-initrd: 前面已经定义过,指linux2.6内核使用的cpio格式的initrd。

image-initrd: 前面已经定义过,专指传统的文件镜像格式的initrd。

realfs: 用户最终使用的真正的文件系统。

内核的初始化代码位于 init/main.c 中的 static int init(void * unused)函数中。同initrd的处理相关部分函数调用层次如下图,笔者按照这个层次对每一个函数都给予了比较详细的分析,为了更好的说明,下面列出的代码中删除了同本文主题不相关的部分:

图2 initrd相关代码的调用层次关系图

image002 

init函数是内核所有初始化代码的入口,代码如下,其中只保留了同initrd相关部分的代码。

static int init(void * unused){
[1]	populate_rootfs();
	
[2]	if (sys_access((const char __user *) "/init", 0) == 0)
		execute_command = "/init";
	else
		prepare_namespace();
[3]	if (sys_open((const char __user *) "/dev/console", O_RDWR, 0) < 0)
	printk(KERN_WARNING "Warning: unable to open an initial console.\n");
	(void) sys_dup(0);
	(void) sys_dup(0);
[4]	if (execute_command)
		run_init_process(execute_command);
	run_init_process("/sbin/init");
	run_init_process("/etc/init");
	run_init_process("/bin/init");
	run_init_process("/bin/sh");
	panic("No init found.  Try passing init= option to kernel.");
}

代码[1]:populate_rootfs函数负责加载initramfs和cpio-initrd,对于populate_rootfs函数的细节后面会讲到。

代码[2]:如果rootfs的根目录下中包含/init进程,则赋予execute_command,在init函数的末尾会被执行。否则执行prepare_namespace函数,initrd是在该函数中被加载的。

代码[3]:将控制台设置为标准输入,后续的两个sys_dup(0),则复制标准输入为标准输出和标准错误输出。

代码[4]:如果rootfs中存在init进程,就将后续的处理工作交给该init进程。其实这段代码的含义是如果加载了cpio-initrd则交给cpio-initrd中的/init处理,否则会执行realfs中的init。读者可能会问:如果加载了cpio-initrd, 那么realfs中的init进程不是没有机会运行了吗?确实,如果加载了cpio-initrd,那么内核就不负责执行realfs的init进程了,而是将这个执行任务交给了cpio-initrd的init进程。解开fedora core4的initrd文件,会发现根目录的下的init文件是一个脚本,在该脚本的最后一行有这样一段代码:

………..
switchroot --movedev /sysroot

就是switchroot语句负责加载realfs,以及执行realfs的init进程。

对cpio-initrd的处理

对cpio-initrd的处理位于populate_rootfs函数中。

void __init populate_rootfs(void){
[1]  char *err = unpack_to_rootfs(__initramfs_start,
			 __initramfs_end - __initramfs_start, 0);
[2]	if (initrd_start) {
[3]		err = unpack_to_rootfs((char *)initrd_start,
			initrd_end - initrd_start, 1);
	
[4]		if (!err) {
			printk(" it is\n");
			unpack_to_rootfs((char *)initrd_start,
				initrd_end - initrd_start, 0);
			free_initrd_mem(initrd_start, initrd_end);
			return;
		}
[5]		fd = sys_open("/initrd.image", O_WRONLY|O_CREAT, 700);
		if (fd >= 0) {
			sys_write(fd, (char *)initrd_start,
					initrd_end - initrd_start);
			sys_close(fd);
			free_initrd_mem(initrd_start, initrd_end);
		}
}

代码[1]:加载initramfs, initramfs位于地址__initramfs_start处,是内核在编译过程中生成的,initramfs的是作为内核的一部分而存在的,不是 boot loader加载的。前面提到了现在initramfs没有任何实质内容。

代码[2]:判断是否加载了initrd。无论哪种格式的initrd,都会被boot loader加载到地址initrd_start处。

代码[3]:判断加载的是不是cpio-initrd。实际上 unpack_to_rootfs有两个功能一个是释放cpio包,另一个就是判断是不是cpio包, 这是通过最后一个参数来区分的, 0:释放 1:查看。

代码[4]:如果是cpio-initrd则将其内容释放出来到rootfs中。

代码[5]:如果不是cpio-initrd,则认为是一个image-initrd,将其内容保存到/initrd.image中。在后面的image-initrd的处理代码中会读取/initrd.image。

对image-initrd的处理在prepare_namespace函数里,包含了对image-initrd进行处理的代码,相关代码如下:

void __init prepare_namespace(void){
[1]	if (initrd_load())
		goto out;
out:
		umount_devfs("/dev");
[2]		sys_mount(".", "/", NULL, MS_MOVE, NULL);
		sys_chroot(".");
		security_sb_post_mountroot();
		mount_devfs_fs ();
}

代码[1]:执行initrd_load函数,将initrd载入,如果载入成功的话initrd_load函数会将realfs的根设置为当前目录。

代码[2]:将当前目录即realfs的根mount为Linux VFS的根。initrd_load函数执行完后,将真正的文件系统的根设置为当前目录。

initrd_load函数负责载入image-initrd,代码如下:

int __init initrd_load(void)

{

[1] if (mount_initrd) {

create_dev("/dev/ram", Root_RAM0, NULL);

[2] if (rd_load_image("/initrd.image") && ROOT_DEV != Root_RAM0) {

sys_unlink("/initrd.image");

handle_initrd();

return 1;

}

}

sys_unlink("/initrd.image");

return 0;

}

代码[1]:如果加载initrd则建立一个ram0设备 /dev/ram。

代码[2]:/initrd.image文件保存的就是image-initrd,rd_load_image函数执行具体的加载操作,将image-nitrd的文件内容释放到ram0里。判断ROOT_DEV!=Root_RAM0的含义是,如果你在grub或者lilo里配置了 root=/dev/ram0 ,则实际上真正的根设备就是initrd了,所以就不把它作为initrd处理 ,而是作为realfs处理。

handle_initrd()函数负责对initrd进行具体的处理,代码如下:

static void __init handle_initrd(void){
[1]	real_root_dev = new_encode_dev(ROOT_DEV);
[2]	create_dev("/dev/root.old", Root_RAM0, NULL);
	mount_block_root("/dev/root.old", root_mountflags & ~MS_RDONLY);
[3]	sys_mkdir("/old", 0700);
	root_fd = sys_open("/", 0, 0);
	old_fd = sys_open("/old", 0, 0);
	/* move initrd over / and chdir/chroot in initrd root */
[4]	sys_chdir("/root");
	sys_mount(".", "/", NULL, MS_MOVE, NULL);
	sys_chroot(".");
	mount_devfs_fs ();
[5]	pid = kernel_thread(do_linuxrc, "/linuxrc", SIGCHLD);
	if (pid > 0) {
		while (pid != sys_wait4(-1, &i, 0, NULL))
			yield();
	}
	/* move initrd to rootfs' /old */
	sys_fchdir(old_fd);
	sys_mount("/", ".", NULL, MS_MOVE, NULL);
	/* switch root and cwd back to / of rootfs */
[6]	sys_fchdir(root_fd);
	sys_chroot(".");
	sys_close(old_fd);
	sys_close(root_fd);
	umount_devfs("/old/dev");
[7]	if (new_decode_dev(real_root_dev) == Root_RAM0) {
		sys_chdir("/old");
		return;
	}
[8]	ROOT_DEV = new_decode_dev(real_root_dev);
	mount_root();
[9]	printk(KERN_NOTICE "Trying to move old root to /initrd ... ");
	error = sys_mount("/old", "/root/initrd", NULL, MS_MOVE, NULL);
	if (!error)
		printk("okay\n");
	else {
		int fd = sys_open("/dev/root.old", O_RDWR, 0);
		printk("failed\n");
		printk(KERN_NOTICE "Unmounting old root\n");
		sys_umount("/old", MNT_DETACH);
		printk(KERN_NOTICE "Trying to free ramdisk memory ... ");
		if (fd < 0) {
			error = fd;
		} else {
			error = sys_ioctl(fd, BLKFLSBUF, 0);
			sys_close(fd);
		}
		printk(!error ? "okay\n" : "failed\n");
	}

handle_initrd函数的主要功能是执行initrd的linuxrc文件,并且将realfs的根目录设置为当前目录.

代码[1]:real_root_dev,是一个全局变量保存的是realfs的设备号。

代码[2]:调用mount_block_root函数将initrd文件系统挂载到了VFS的/root下。

代码[3]:提取rootfs的根的文件描述符并将其保存到root_fd。它的作用就是为了在chroot到initrd的文件系统,处理完initrd之后要,还能够返回rootfs。返回的代码参考代码[7]。

代码[4]:chroot进入initrd的文件系统。前面initrd已挂载到了rootfs的/root目录。

代码[5]:执行initrd的linuxrc文件,等待其结束。

代码[6]:initrd处理完之后,重新chroot进入rootfs。

代码[7]:如果real_root_dev在 linuxrc中重新设成Root_RAM0,则initrd就是最终的realfs了,改变当前目录到initrd中,不作后续处理直接返回。

代码[8]:在linuxrc执行完后,realfs设备已经确定,调用mount_root函数将realfs挂载到root_fs的 /root目录下,并将当前目录设置为/root。

代码[9]:后面的代码主要是做一些收尾的工作,将initrd的内存盘释放。

到此代码分析完毕。


6.结束语

通过本文前半部分对cpio-initrd和imag-initrd的阐述与对比以及后半部分的代码分析,我相信读者对Linux 2.6内核的initrd技术有了一个较为全面的了解。在本文的最后,给出两点最重要的结论:

1. 尽管Linux2.6既支持cpio-initrd,也支持image-initrd,但是cpio-initrd有着更大的优势,在使用中我们应该优先考虑使用cpio格式的initrd。

2. cpio-initrd相对于image-initrd承担了更多的初始化责任,这种变化也可以看作是内核代码的用户层化的一种体现,我们在其它的诸如FUSE等项目中也看到了将内核功能扩展到用户层实现的尝试。精简内核代码,将部分功能移植到用户层必然是linux内核发展的一个趋势。


参考资料

从下面三篇文章中,可以获得更多的关于initramfs的知识:

[1]http://tree.celinuxforum.org/pubwiki/moin.cgi/EarlyUserSpace

[2]http://lwn.net/Articles/14776/

[3]http://www.ussg.iu.edu/hypermail/linux/kernel/0211.0/0341.html

从下面这篇文章中读者可以了解到关于linux VSF、rootfs的相关知识:

[4] http://www.ibm.com/developerworks/cn/linux/l-vfs/

下面是一些initrd的参考资料:

[5] http://www.die.net/doc/linux/man/man4/initrd.4.html

关于作者

李大治,软件工程师,目前从事Linux平台下网络安全产品的开发工作,您可以通过dazhi.li@gmail.com同他取得联系。

Android相关知识

今天没有太多的事情,闲来无事将adnroid的livecd拿来玩一下.

在android-x86-1.6-r2.iso的根目录下,有六个文件,一个isolinux目录

六个文件分别是:

1.  initrd.img

2.  install.img

3.  ramdisk.img

4.  kernel

5.  system.sfs

6.  TRANS.TBL

首先将这个iso里面的所有文件解压后上传到linux平台上,然后进入此目录.

实际上这几个img文件全为cpio的格式,用gzip压缩过的.

所以通过这种逆向的方法,我们可以将其解压,然后修改后再重新压缩回去.

这里以ramdisk为例,讲述一下具体的操作方法:

[root@localhost livecd]# ls -l
total 52928
-rw-r–r–  1 root root  1304385 Mar 18 16:33 initrd.img
-rw-r–r–  1 root root   612292 Mar 18 16:33 install.img
drwxr-xr-x  2 root root     4096 Jun  5 10:41 isolinux
-rw-r–r–  1 root root  2065136 Mar 18 16:39 kernel
-rw-r–r–  1 root root   148562 Mar 18 16:41 ramdisk.img
-rw-r–r–  1 root root 49979392 Mar 18 16:45 system.sfs
-rw-r–r–  1 root root     1328 Mar 18 16:45 TRANS.TBL

[root@localhost livecd]# file *.img
initrd.img:  gzip compressed data, from Unix, max compression
install.img: gzip compressed data, from Unix, max compression
ramdisk.img: gzip compressed data, from Unix
[root@localhost livecd]# file kernel
kernel: x86 boot sector
[root@localhost livecd]# file system.sfs
system.sfs: data
[root@localhost livecd]# file TRANS.TBL
TRANS.TBL: ASCII text
[root@localhost livecd]# strings TRANS.TBL
F initrd.img                                                                                                                                                                                                         initrd.img
F install.img                                                                                                                                                                                                        install.img
D isolinux                                                                                                                                                                                                           isolinux
F kernel                                                                                                                                                                                                             kernel
F ramdisk.img                                                                                                                                                                                                        ramdisk.img
F system.sfs                                                                                                                                                                                                         system.sfs
[root@localhost livecd]# cd isolinux
[root@localhost isolinux]# ls
android-x86.png  boot.cat  isolinux.bin  isolinux.cfg  TRANS.TBL  vesamenu.c32
[root@localhost isolinux]# strings TRANS.TBL
F android-x86.png                                                                                                                                                                                                    android-x86.png
F boot.cat                                                                                                                                                                                                           boot.cat
F isolinux.bin                                                                                                                                                                                                       isolinux.bin
F isolinux.cfg                                                                                                                                                                                                       isolinux.cfg
F vesamenu.c32                                                                                                                                                                                                       vesamenu.c32

[root@localhost isolinux]# cd ..

[root@localhost livecd]# mkdir tmp
[root@localhost livecd]# cp ramdisk.img tmp/
[root@localhost livecd]# cd tmp/
[root@localhost tmp]# ls -l
total 152
-rw-r–r–  1 root root 148562 Jun  5 11:32 ramdisk.img
[root@localhost tmp]# mv ramdisk.img ramdisk.cpio.gz
[root@localhost tmp]# ls
ramdisk.cpio.gz
[root@localhost tmp]# gzip -d ramdisk.cpio.gz
[root@localhost tmp]# ls
ramdisk.cpio
[root@localhost tmp]# cpio -i -F ramdisk.cpio
584 blocks
[root@localhost tmp]# ls -l
total 468
drwxrwx–x  2 root root   4096 Jun  5 11:33 data
-rw-r–r–  1 root root    118 Jun  5 11:33 default.prop
drwxr-xr-x  2 root root   4096 Jun  5 11:33 dev
-rwxr-x—  1 root root 125592 Jun  5 11:33 init
-rwxr-x—  1 root root    509 Jun  5 11:33 init.eeepc.rc
-rwxr-x—  1 root root   9454 Jun  5 11:33 init.rc
drwxr-xr-x  2 root root   4096 Jun  5 11:33 proc
-rw-r–r–  1 root root 298752 Jun  5 11:32 ramdisk.cpio
drwxr-x—  2 root root   4096 Jun  5 11:33 sbin
drwxr-xr-x  2 root root   4096 Jun  5 11:33 sys
drwxr-xr-x  2 root root   4096 Jun  5 11:33 system

如果修改之后再压缩回去的话,也是可以的.

删除ramdisk.cpio文件,然后将所有的修改都在tmp目录下进行,修改完成后利用下列命令压缩回去:

# cpio -i -t -F ../ramdisk.cpio | cpio -o -H newc -O ../ramdisk_new.cpio

————————————————————————————————————-

从上面可以看出,在livecd里的几个img全为gzip和cpio处理过的文件,而里面的TBL的文件为目录的结构文件,用的是*.TBL: ASCII text,可以用strings来进行查看,以F开关的表示file文件,以D开头的表示为dir目录.

对于linux的目录结构,我们可以通过下面的链接去访问更权威的资料:

Linux2.6 内核的 Initrd 机制解析

http://www.ibm.com/developerworks/cn/linux/l-k26initrd/