中新浙江网11月6日电 近日,备受关注的浙江湖州市南浔区三人围追堵截偷车贼,致使小偷跳河溺水身亡的不作为间接故意杀人案,终于尘埃落定。
昨天,湖州市南浔区法院对三人一审判决颜克于犯故意杀人罪判处有期徒刑3年9个月;廖红军犯故意杀人罪判处有期徒刑3年3个月;韩应龙犯故意杀人罪判处有期徒刑3年,缓刑4年。
知道这个南浔区,也就是南浔镇么?俺大学里同一宿舍的兄弟就是那旮沓的,他嘴里边儿号称的中国四大名镇之一,真的假的俺没有考证过,从他嘴里说相声般崩出的
刘镛、张颂贤、徐迟、庞云曾、顾福昌等等等这一长串名字来看,所言应该非虚。不过上边儿的这则新闻说的不是南浔的旖旎风光和人文历史,而是告诉咱们,要用一颗平常心去看待偷车贼,要用一颗平常心去看代码。道儿上混的人都知道,变态的代码和偷车贼都是我们生活中不得不应对的很平常的一部分。说到这里你应该能够猜得出,接下来咱们就要看具体的代码了。
由Kconfig这张地图的分布来看,pci这块儿的代码应该分布在两个地方,drivers/pci和arch/i386/pci,使用wc –l命令分别看一下多少行,具体数字俺就不说了,反正吓死人了,所以还是紧赶的看看另一张地图Makefile,看能不能缩小一下目标,先看drivers/pci下边儿的
2 # Makefile for the PCI bus specific drivers.
5 obj-y += access.o bus.o probe.o remove.o pci.o quirks.o /
6 pci-driver.o search.o pci-sysfs.o rom.o setup-res.o
7 obj-$(CONFIG_PROC_FS) += proc.o
9 # Build PCI Express stuff if needed
10 obj-$(CONFIG_PCIEPORTBUS) += pcie/
12 obj-$(CONFIG_HOTPLUG) += hotplug.o
14 # Build the PCI Hotplug drivers if we were asked to
15 obj-$(CONFIG_HOTPLUG_PCI) += hotplug/
17 # Build the PCI MSI interrupt support
18 obj-$(CONFIG_PCI_MSI) += msi.o
20 # Build the Hypertransport interrupt support
21 obj-$(CONFIG_HT_IRQ) += htirq.o
24 # Some architectures use the generic PCI setup functions
26 obj-$(CONFIG_X86) += setup-bus.o
27 obj-$(CONFIG_ALPHA) += setup-bus.o setup-irq.o
28 obj-$(CONFIG_ARM) += setup-bus.o setup-irq.o
29 obj-$(CONFIG_PARISC) += setup-bus.o
30 obj-$(CONFIG_SUPERH) += setup-bus.o setup-irq.o
31 obj-$(CONFIG_PPC32) += setup-irq.o
32 obj-$(CONFIG_PPC64) += setup-bus.o
33 obj-$(CONFIG_MIPS) += setup-bus.o setup-irq.o
34 obj-$(CONFIG_X86_VISWS) += setup-irq.o
37 # ACPI Related PCI FW Functions
39 obj-$(CONFIG_ACPI) += pci-acpi.o
41 # Cardbus & CompactPCI use setup-bus
42
obj-$(CONFIG_HOTPLUG) += setup-bus.o
48
ifeq ($(CONFIG_PCI_DEBUG),y)
49
EXTRA_CFLAGS += -DDEBUG
5行,一看到obj-y,后面儿那一堆文件就是必须得关注的,不容置疑。
7行,关于proc文件系统的,咱们都得忙着发展自己口袋里的经济,精力有限,proc.c文件就不用理睬了。
10行,CONFIG_PCIEPORTBUS有没有选要看你都对Kconfig做了些什么,不过不管你做了什么,这里都不打算过多的关注PCI Express,所以整个pcie/目录就可以飘过了。
12~15行,热插拔有关的,关于PCI的热插拔,走到目前这一步时,俺还不清楚要对它讲到什么程度,hotplug目录还是暂且飘过吧。
18行,如果Kconfig那里你选上了CONFIG_PCI_MSI,文件msi.c就必须得关注了。
21行,没想过要聊到Hypertransport有关的东东,htirq.c仍然飘过。
23~34行,与体系架构相关,前面已经说过,与架构有关的代码都只会看X86上的,所以只用关注setup-bus.c文件,setup-irq.c就不用理睬了。
39行,现在电源管理一般都是用的ACPI了,为了响应十一五建设节约型社会的号召,还是应该关注一下。
44行,既然要看的X86上的,syscall.c就可以忽略不计了。
现在可以来统计一下咱们接下来需要关注的的文件,有access.c,bus.c,probe.c,remove.c,
pci.c,quirks.c,pci-driver.c,search.c,pci-sysfs.c,rom.c,setup-res.c,hotplug.c,msi.c,setup-bus.c,pci-acpi.c,就这些了,说多不多说少不少,使用wc –l命令统计一下,7382 total,还处于一个勉强可以接受的范围。不过不要忘了,drivers/pci下面的代码只是其中一部分,还有arch/i386/pci,再看看它里面的Makefile
3 obj-$(CONFIG_PCI_BIOS) += pcbios.o
4 obj-$(CONFIG_PCI_MMCONFIG) += mmconfig.o direct.o mmconfig-shared.o
5 obj-$(CONFIG_PCI_DIRECT) += direct.o
8 pci-$(CONFIG_ACPI) += acpi.o
9 pci-y += legacy.o irq.o
11 pci-$(CONFIG_X86_VISWS) := visws.o fixup.o
12 pci-$(CONFIG_X86_NUMAQ) := numa.o irq.o
14 obj-y += $(pci-y) common.o early.o
这个Makefile地图所描述的代码版图大小取决于pci-y是怎么定义的,这里玩了个花活儿,先在7~9这几行预先定义pci-y的值,然后在11行和12行判断CONFIG_X86_VISWS或者CONFIG_X86_NUMAQ有没有被定义,如果定义了这两个中的任何一个,就会重定义pci-y的值。一定要注意到11~12这两行里的是“
:=”,而不是“
+=”。至于CONFIG_X86_VISWS和CONFIG_X86_NUMAQ,你使用make menuconfig的时候可以在“Processor type and features”下的子菜单“Subarchitecture Type”里看到它们,仍然是道单项选择题,我这里选的是第一项“PC-compatible”,所以它们两个就与俺没啥关系了,所以visws.c、numa.c、irq.c这几个文件就可以华丽丽的飘过了。现在统计一下剩下的那些需要咱们去关注的的文件,2753 total。
两岸三地都属于一个中国,不管是drivers/pci那儿的,还是arch/i386/pci那儿的,也都只属于一个PCI子系统,本着一个中国的原则,咱们要统筹的全面的考察分析位于两个地方的代码,于是,7382和2753这两个数字加起来就是咱们接下来需要关心的部分了,这个突破了五位数的数字左看右看横看竖看都显得那么的阴森恐怖,不过实话告诉你,已经比看到Makefile之前少了很多了,人家咋说也是整个一PCI子系统,就像走在T台上的芙蓉姐姐和杨二车那姆一样,看起来恐怖但也是很有内涵的,岂能够让人三眼两眼三言两语就给看透了说透了?
那现在咱们就高瞻远瞩统筹全面的扫视一下这两个地方的代码,根据在USB Core那里得来的经验,可以推测对于USB、PCI这样的子系统都应该有一个subsys_initcall这样的入口,咱们得先找到它。朱德庸在《关于上班这件事》里说了,要花前半生找入口,花后半生找出口。可见寻找入口对于咱们这一生,对于看内核代码这件事儿都是无比重要的,当然寻找subsys_initcall这个入口是不用花前半生那么久的,要是那么久,俺宁愿回家卖红薯也不在这儿看代码了,俺们豫剧里的七品芝麻官都知道当官不为民作主,不如回家卖红薯。下边儿俺就把找到的给列出来,为什么说“列”出来?难道还会有很多么?你猜对了,PCI这边儿入口格外多,而且是有预谋有组织成系列的,不单单有subsys_initcall,还有arch_initcall、postcore_initcall等等等等。
文件
|
函数
|
入口
|
内存位置
|
arch/i386/pci/ acpi.c
|
pci_acpi_init
|
subsys_initcall
|
.initcall4.init
|
arch/i386/pci/ common.c
|
pcibios_init
|
subsys_initcall
|
.initcall4.init
|
arch/i386/pci/ i386.c
|
pcibios_assign_resources
|
fs_initcall
|
.initcall5.init
|
arch/i386/pci/ legacy.c
|
pci_legacy_init
|
subsys_initcall
|
.initcall4.init
|
drivers/pci/ pci-acpi.c
|
acpi_pci_init
|
arch_initcall
|
.initcall3.init
|
drivers/pci/ pci- driver.c
|
pci_driver_init
|
postcore_initcall
|
.initcall2.init
|
drivers/pci/ pci- sysfs.c
|
pci_sysfs_init
|
late_initcall
|
.initcall7.init
|
drivers/pci/ pci.c
|
pci_init
|
device_initcall
|
.initcall6.init
|
drivers/pci/ probe.c
|
pcibus_class_init
|
postcore_initcall
|
.initcall2.init
|
drivers/pci/ proc.c
|
pci_proc_init
|
__initcall
|
.initcall6.init
|
arch/i386/pci/ init.c
|
pci_access_init
|
arch_initcall
|
.initcall3.init
|
看看那一列入口,形尽而意不同的种种xxx_initcall让人眼花缭乱的,真不知道该从哪儿下手,应了keso那句话:所有的痛苦都来自选择,所谓幸福,就是没有选择。像usb子系统那样子简简单单一个subsys_initcall,没得选择,傻强都知道怎么走。不过你迷惘一阵儿就可以了,可别真的被绕进去了。要知道“多少事,从来急;天地转,光阴迫。一万年太久,只争朝夕。四海翻腾云水怒,五洲震荡风雷激。要看清一切入口,全无敌。”咱们要只争朝夕看清一切入口的,因此到include/linux/init.h文件里碰碰运气
112
* A "pure" initcall has no dependencies on anything else, and purely
113
* initializes variables that couldn't be statically initialized.
115
* This only exists for built-in code, not for modules.
这些入口有个共同的特征,它们都是用
__define_initcall宏定义的,其实,如果你是从USB Core那儿一路走过来的话,看清楚它们并不难,如果你实在忘记了或者说没见到过也没关系,不用你去蓦然回首了,俺会在这里再说一下,因为菩萨说俺今年要多做善事。
内核对子系统或者模块的初始化其实包括了两个方面,一是对各种参数的解析,一是调用上面的各种入口函数。先来说说参数的解析,这里所谓的参数包括了内核参数和模块参数,什么是内核参数?打开你的grub文件找找kernel打头儿的那些行,比如
kernel /boot/vmlinuz-2.6.18-kdb root=/dev/sda1 ro splash=silent vga=0x314
这行里边儿的root,splash,vga等等就都是内核参数,USB那边儿咱们已经见过一个类似的东东,就是nousb,PCI这边儿那?当然也有,具体的可以到Documentation/kernel-parameters.txt文件里找找
1302
pci=option[,option...][PCI] various PCI subsystem options:
1303
off [IA-32] don't probe for the PCI bus
1304
bios [IA-32] force use of PCI BIOS, don't access
1305
the hardware directly. Use this if your machine
1306
has a non-standard PCI host bridge.
1307
nobios [IA-32] disallow use of PCI BIOS, only direct
1308
hardware access methods are allowed. Use this
1309
if you experience crashes upon bootup and you
1310
suspect they are caused by the BIOS.
1311
conf1 [IA-32] Force use of PCI Configuration
1313
conf2 [IA-32] Force use of PCI Configuration
1315
nommconf [IA-32,X86_64] Disable use of MMCONFIG for PCI
1317
nomsi [MSI] If the PCI_MSI kernel config parameter is
1318
enabled, this kernel boot option can be used to
1319
disable the use of MSI interrupts system-wide.
1320
nosort [IA-32] Don't sort PCI devices according to
1321
order given by the PCI BIOS. This sorting is
1322
done to get a device order compatible with
1324
biosirq [IA-32] Use PCI BIOS calls to get the interrupt
1325
routing table. These calls are known to be buggy
1326
on several machines and they hang the machine
1327
when used, but on other computers it's the only
1328
way to get the interrupt routing table. Try
1329
this option if the kernel is unable to allocate
1330
IRQs or discover secondary PCI buses on your
1332
rom [IA-32] Assign address space to expansion ROMs.
1333
Use with caution as certain devices share
1334
address decoders between ROMs and other
1336
irqmask=0xMMMM[IA-32] Set a bit mask of IRQs allowed to be
1337
assigned automatically to PCI devices. You can
1338
make the kernel exclude IRQs of your ISA cards
1340
pirqaddr=0xAAAAA [IA-32] Specify the physical address
1341
of the PIRQ table (normally generated
1342
by the BIOS) if it is outside the
1343
F0000h-100000h range.
1344
lastbus=N [IA-32] Scan all buses thru bus #N. Can be
1345
useful if the kernel is unable to find your
1346
secondary buses and you want to tell it
1347
explicitly which ones they are.
1348
assign-busses [IA-32] Always assign all PCI bus
1349
numbers ourselves, overriding
1350
whatever the firmware may have done.
1351
usepirqmask [IA-32] Honor the possible IRQ mask stored
1352
in the BIOS $PIR table. This is needed on
1353
some systems with broken BIOSes, notably
1354
some HP Pavilion N5400 and Omnibook XE3
1355
notebooks. This will have no effect if ACPI
1356
IRQ routing is enabled.
1357
noacpi [IA-32] Do not use ACPI for IRQ routing
1358
or for PCI scanning.
1359
routeirq Do IRQ routing for all PCI devices.
1360
This is normally done in pci_enable_device(),
1361
so this option is a temporary workaround
1362
for broken drivers that don't call it.
1363
firmware [ARM] Do not re-enumerate the bus but instead
1364
just use the configuration from the
1365
bootloader. This is currently used on
1366
IXP2000 systems where the bus has to be
1367
configured a certain way for adjunct CPUs.
1368
noearly [X86] Don't do any early type 1 scanning.
1369
This might help on some broken boards which
1370
machine check when some devices' config space
1371
is read. But various workarounds are disabled
1372
and some IOMMU drivers will not work.
1373
bfsort Sort PCI devices into breadth-first order.
1374
This sorting is done to get a device
1375
order compatible with older (<= 2.4) kernels.
1376
nobfsort Don't sort PCI devices into breadth-first order.
1377
cbiosize=nn[KMG] The fixed amount of bus space which is
1378
reserved for the CardBus bridge's IO window.
1379
The default value is 256 bytes.
1380
cbmemsize=nn[KMG] The fixed amount of bus space which is
1381
reserved for the CardBus bridge's memory
1382
window. The default value is 64 megabytes.
这么将近100行的东东都是PCI这边儿可以设置的参数选项,与之相比,USB那里也忒单纯了。咱们地球上都在搞军备竞争,内核也去跟风搞什么代码竞争,看谁的代码长谁的代码复杂,现在连内核参数也搞竞争了,看谁的多。当然这种竞争与keso说的中国互联网行业的“抄袭盛行,低水平竞争,恶性竞争。”不一样。
这么多选项有的前边儿已经提到了,没有提到的俺也不打算在这里讲,日后碰到再说了。明白了内核参数,再看看模块参数。其实俺所说的模块参数就是你使用insmod或者modprobe加载模块时在命令行里指定的参数,它们在驱动里使用module_param这样的宏来声明。不过千万要明白一点的是,俺意思并不是说内核参数就不能使用module_param这样的宏去定义了,USB那边儿的nousb就是使用__module_param_call宏定义的。
另外还要明白一点的是,当模块被编译进内核的时候,它的那些模块参数就需要在grub文件的kernel行里来指定了,比如,
modprobe usbcore autosuspend=2
放到kernel行里就是
usbcore.autosuspend=2
当然在drivers/usb/usb.c里autosuspend的默认值就是2,所以这里再去指定为2纯粹就是春节申遗多此一举。
你如果很好奇想知道一个模块都有哪些参数可以使用,敲命令“
modinfo -p ${modulename}
”就可以了。对于已经加载到内核里的模块,它们的参数都会陈列在
/sys/module/${modulename}/parameters/
目录下面儿等着你去检阅,当然你也可以使用
"echo -n ${value} > /sys/module/${modulename}/parameters/${parm}"
这样的命令去修改它们。
现在看看内核是怎么进行参数解析的,这主要依靠一个名叫parse_args的函数,它的细节不用关心,只需要明白在内核启动时,或者加载模块结协模块命令行参数时它都会被调用就可以了。如果你是一个有心人,去扫了眼init/main.c文件里的start_kernel函数,你就会发现,内核启动时调用了一次parse_args还觉得不过瘾,又调用了第二次
parse_args的第一次调用就在parse_early_param函数里面,为什么会出现两次调用parse_args的情况?怪只怪内核参数又分成了两种,一种是普普通通的,一种是有特权,需要在普通选项之前处理的,就像银行的VIP客户,有专门开辟的窗口接待,不管咱们排队排的多长,他们一到就会优先服务,即使没有VIP,那个窗口儿就是空着也不会给你用,就让你在那儿等。既然这个世界上人有两种,有特权的和没特权的,那内核参数同样也就有两种,有特权的和没特权的。
特权谁都想有,但是有了特权又都怕人说自己有特权,就像哈医大二院的纪委书记在接受央视的采访“老人住院费550万元”时评价自己单位“我们就是一所人民医院……就是一所贫下中农的医院,从来不用特权去索取自己身外的任何利益……我们不但没有多收钱还少收了。”那样,人就是这么的复杂和奇怪。内核参数就不一样了,有特权也是光明正大的用,从不藏着掖着,直接使用early_param宏去声明,让你一眼就看出它是有特权的,而普通的内核参数则是使用module_param系列的宏声明的。使用early_param声明的那些参数就会首先由parse_early_param去解析,然后才轮得着其它的参数。这里引用一下《Understanding Linux Network Internals》里的图7-2
关于这张图,你只要注意到__setup_start、__setup_end、__start___param、__stop___param这几个东东就可以了,它们很明确的告诉我们特权参数和普通参数在内存里存放的位置是不一样的,特权参数放在__setup_start和__setup_end之间的节里,普通参数放在__start___param和__stop___param之间的节里。
PCI里边儿也出现了一个early_param,在drivers/pci/pci.c里面
early_param怎么定义的你可以去include/linux/init.h里面看,俺这里就不说了,反正这行的意思就是发现你grub文件的kernel行里有“pci=”这样的东东的时候,就会调用函数pci_setup进行处理,怎么处理?这都是以后的事儿了。
参数解析完之后,再经过一个漫长而曲折的过程,就会调用到各种各样的xxx_initcall入口函数,这些函数的调用不是随便的,而是按照一定顺序的,这个顺序就取决于__define_initcall宏。__define_initcall宏用来将指定的函数指针放到.initcall.init节里,这都是讲USB Core的时候说过的,再扼要重述一遍。
内核可执行文件由许多链接在一起的对象文件组成。对象文件有许多节,如文本、数据、init 数据、bass 等等。这些对象文件都是由一个称为链接器脚本的文件链接并装入的。这个链接器脚本的功能是将输入对象文件的各节映射到输出文件中;换句话说,它将所有输入对象文件都链接到单一的可执行文件中,将该可执行文件的各节装入到指定地址处。 vmlinux.lds是存在于 arch/<target>/ 目录中的内核链接器脚本,它负责链接内核的各个节并将它们装入内存中特定偏移量处。在vmlinux.lds文件里查找initcall.init就可以看到下面的内容
__inicall_start = .;
.initcall.init : AT(ADDR(.initcall.init) – 0xC0000000) {
*(.initcall1.init)
*(.initcall2.init)
*(.initcall3.init)
*(.initcall4.init)
*(.initcall5.init)
*(.initcall6.init)
*(.initcall7.init)
}
__initcall_end = .;
这就告诉我们.initcall.init节又分成了7个字节,而xxx_initcall入口函数指针具体放在哪一个子节里边儿是由xxx_initcall的定义中,__define_initcall宏的参数决定的,比如core_initcall将函数指针放在.initcall1.init子节,device_initcall将函数指针放在了.initcall6.init子节等等。各个子节的顺序是确定的,即先调用.initcall1.init中的函数指针再调用.initcall2.init中的函数指针,等等。不同的入口函数被放在不同的子节中,因此也就决定了它们的调用顺序。
关于内核里初始化有关的一些内存节,可以再参考下《Understanding Linux Network Internals》的图7-3
现在你再回头去看看前面那张描述PCI里边儿各种xxx_initcall入口的表,应该不会再觉得迷惘了,对整个PCI子系统的运作流程也大致有个谱了。