基于x86体系结构分析linux-2.6.26的启动过程

这篇文章是在校期间陈香兰老师《深入理解Linux内核》的课堂作业,大部分内容参考自《深入理解Linux内核》 第三版 陈莉君等译。

工作需要拿出来复习一下。

转载注明出处。

 

 

1      Linux启动过程概述

本文分析基于x86平台的Linux启动过程。系统启动是个复杂的过程,主要包括以下几个步骤a、系统加电复位阶段b、BIOS自举阶段c、引导装入程序阶段d、内核运行及初始化e、init进程运行。

2      结合linux-2.6.26源代码分析启动过程

2.1      史前时代:BIOS

系统加电或复位后,基于80x86的特性,中央处理器将内存中所有数据清零,并对内存进行校验。如果没有错误,则CS寄存器中全部置1,IP寄存器中全部置0,即CS=FFFF[0]H,而IP=0000[0]H。[CS]:[IP]指向的就是BIOS的入口,由此进入BIOS的启动过程。

进入BIOS后,执行以下4个操作:

a、对硬件进行一系列测试,检测现在都有什么设备及这些设备是否正常工作。此阶段称为上电自检,会显示一些信息,如BIOS版本。

b、初始化硬件设备。此阶段会显示系统中所安装的所有PCI设备的一个列表。

c、搜索一个操作系统来启动。根据BIOS的设置,可能要试图访问软盘,硬盘或光盘的第一个扇区(引导扇区)。

d、只要找到一个有效的设备,就把上一步引导扇区的内容复制到RAM中从0x00007c00开始的位置。然后跳转到这个位置,开始执行执行刚才装进来的代码。

2.2      远古时代:引导装入程序

从磁盘启动Linux内核需要一个引导装入程序,常见的是LILO。LILO被分为部分(否则太大无法装入整个扇区),BIOS将程序的第一部分(在引导扇区)装入从0x00007c00开始位置的RAM中,然后这段程序又把自己移到地质0x00096a00,建立实模式栈(0x00096000 ~ 0x000969ff),并把LILO的第部分装到从地址0x00096c00开始的RAM中。

第二部分从磁盘读取可用操作系统的映射表,并提供给用户一个提示符,供用户选择。然后根据用户选择引导程序就可以把相应分区的引导扇区拷贝到RAM中并执行它,或直接把内核映像拷贝到RAM中。

在加载内核之前先了解以下内核映像的结构:

(Documentation/i386/boot.txt)
        For a modern bzImage kernel with boot protocol version >= 2.02, a memory layout like the following is suggested:

~                        ~
        |  Protected-mode kernel |#这是上面说的保护模式代码,LILO(grub)会把它放到这里
100000  +------------------------+
        |  I/O memory hole         |
0A0000        +------------------------+
        |  Reserved for BIOS         |        Leave as much as possible unused
        ~                        ~
        |  Command line                 |        (Can also be below the X+10000 mark)
X+10000        +------------------------+
        |  Stack/heap                 |        For use by the kernel real-mode code.
X+08000        +------------------------+      
        |  Kernel setup                 |        The kernel real-mode code.
        |  Kernel boot sector         |        The kernel legacy boot sector.
        #上面这两个就是vmlinuz的前面两部分的代码和数据
X       +------------------------+
        |  Boot loader                 |        <- Boot sector entry point 0000:7C00
001000        +------------------------+
        |  Reserved for MBR/BIOS |
000800        +------------------------+
        |  Typically used by MBR |
000600        +------------------------+
        |  BIOS use only         |
000000        +------------------------+

... where the address X is as low as the design of the boot loader permits.

导入内核映像主要执行以下步骤:

a、调用一个BIOS过程显示“Loading”信息。

b、调用BIOS过程从磁盘装入内核映像的初始部分,即将内核映像的第一个512字节从地址0x00090000开始存入RAM中,而将setup()函数的代码从0x00090200开始存入RAM中。

header.S

...

 .code16
        .section ".bstext", "ax" #注意这个节的名称

        .global bootsect_start
bootsect_start:

 #这是bootsect代码,也就是vmlinuz第一个512字节的源代码,即内核映像的第一个512字节

       # Normalize the start address
        ljmp        $BOOTSEG, $start2

 

# 0x07C0:0000 如果从这里开始执行,那么说明是被BIOS直接加载过来的,这是不允许的,因为现在linux需要一个bootloader,这也就是上面说的bootsect有点特殊的地方,就是说它并没打算用来执行。所以万一它被作为bootsect由BIOS直接执行,那么就直接提示reboot.

Header.s中定义了三个节.bstext,.bsdata,.header,这3个节共同构成了上vmlinuz的第一个512字节。

c、同样的方式装入剩余的内核映像,并把内核映像放入从低地址0x00010000(适用于make zImage编译的小内核映像)或者从高地址0x00100000(适用于 bzImage编译的大内核映像)开始的RAM中。这段代码是保护模式的代码。

d、跳转到setup()代码。

header.s

 jmp_far(__LOAD_DS__+0x20, 0);   /* Run the kernel */ 

0x20加在段上就是0x200个字节(实模式下逻辑地址到线性地址的计算方法seg<<4+offset),也就是跳到vmlinuz实模式代码setup执行了.

 

2.3      中世纪:setup()函数

setup()汇编语言函数的代码有链接程序放在内核映像文件的偏移量0x200处。作用是初始化计算机的硬件设备,并为内核程序执行建立环境。虽然BIOS已经初始化了大部分的硬件设备,但Linux并不依赖于BIOS,而是以自己的方式重新初始化设备以增强可移植性和健壮性。

Header.s  执行到了setup代码了,也就是vmlinuz第二个512字节处。也就是_start

        # offset 512, entry point

        .globl        _start
_start:
                # Explicitly enter this as bytes, or the assembler
                # tries to generate a 3-byte jump here, which causes
                # everything else to push off to the wrong offset.
                .byte        0xeb                # short (2-byte) jump
                .byte        start_of_setup-1f

        #第一条指令,这是一条短跳转,接下来到start_of_setup。执行设置堆栈清空setup的bss段等操作,然后跳转到boots/main.c中。

# Jump to C code (should not return)
        calll        main #到C代码去了
        

 #代码在boot/main.c

setup()函数和main()函数本质上执行以下操作:

a、调用BIOS例程,在RAM中建立系统物理内存布局表。

b、设置键盘重复延时和速率,初始化视频卡,初始化磁盘控制器并检测硬盘参数,检测IBM微通道总线,检查PS/2指针设备(总线鼠标),检查对高级电源(APM)BIOS的支持。

c、如果BIOS支持磁盘驱动服务,则调用相应的其过程在RAM中建立系统可用硬盘表。

d、将低装载的内核映像移动到高装载的物理地址(0x00010000)处。因为解压内核映像需要一些空闲空间作为临时缓冲区。

e、设置8042键盘控制器的A20引脚。

f、建立一个临时中断描述符表(IDT)和一个临时全局描述符表(GDT)。

g、设置浮点单元,重新编写可编程中断控制器(PIC)(以屏蔽所有终端),通过设置cr0状态寄存器中的PE位把CPU从实模式切换到保护模式。

h、跳转到startup_32()汇编语言函数。

main函数中的最后两个函数,实现从实模式到保护模式的切换
        /* Do the last things and invoke protected mode */
        go_to_protected_mode();进入保护模式。这个函数进而又调用       
        protected_mode_jump(boot_params.hdr.code32_start,(u32)&boot_params + (ds() << 4));
        这个函数接受两个参数,第一个参数是保护模式的第一条代码,在0x100000,后面这个就是给内核传递的参数,由于切换到保护模式,所以要给出参数的线性地址,而不是有效地址,ds()函数就是ds寄存器的值。

 

2.4      文艺复兴时期:startup_32()函数

有两个不同的startup_32()函数,此处指arch/i386/boot/compressed/head.s文件的那个。

该函数执行以下操作:

a、初始化段寄存器和一个临时堆栈。

b、清零eflags寄存器的所有位,用0填充_edata和_end符号标示的内核未初始化数据区。

c、调用decompress_kernel()解压内核映像。移动解压后的映像到物理地址0x00100000地址开始的最终位置。

d、跳转到物理地址0x00100000处。

第二个startup_32()函数为第一个Linux进程(进程0)建立执行环境。

该函数执行以下操作:

a、把段寄存器初始化为最终值,把内核的bss段填充为0.

 lgdt pa(boot_gdt_descr)
         movl $(__BOOT_DS),%eax
         movl %eax,%ds
         movl %eax,%es
         movl %eax,%fs
         movl %eax,%gs

 

 * Clear BSS first so that there are no surprises... */
 #清空BSS

         cld
         xorl %eax,%eax
         movl $pa(__bss_start),%edi
         movl $pa(__bss_stop),%ecx
         subl %edi,%ecx
         shrl $2,%ecx
         rep ; stosl

 

b、初始化0号进程的页表。把页全局目录的地址存放在cr3寄存器中,并通过设置cr0寄存器的PG位启用分页。

#pg0:临时的页表项。映射前面4M 内存空间大小
         movl $pa(pg0), %edi
         movl $pa(swapper_pg_dir), %edx
         movl $PTE_ATTR, %eax
10:
         #edi中存放了pg0的地址。PDE_ATTR(%edi):会成生一个PDE项
         leal PDE_ATTR(%edi),%ecx                   /* Create PDE entry */
       
         #将生成的PDE设为PGD的第0项
         movl %ecx,(%edx)                          /* Store identity PDE entry */
       
         #将生成的PDE设为PGD的page_pde_offset项即0x300
         movl %ecx,page_pde_offset(%edx)                  /* Store kernel PDE entry */
 
         #即edx 指向pgd的第二项
         addl $4,%edx       
         #接下来就是设置pg0的值了.       
         movl $1024, %ecx #设置循环次数
11:

         #将eax-> edi . edi存放的是pg0的地址
         stosl
         addl $0x1000,%eax                #eax = eax +0x1000 (0x1000 = 4K)
         loop 11b
         #经过上面的循环之后,pg0中的内容依次被设置为:0x007, 0x1007,0x2007...0x3FF007
         #这次从线性地址0开始的第一个PGD项和从__PAGE_OFFSET开始的第一个PGD都可以对前4M 进行寻址了

 

c、建立进程0的内核态堆栈,并再一次清零eflags寄存器的所有位。

//开启分页
         movl $pa(swapper_pg_dir),%eax
         movl %eax,%cr3           /* set the page table pointer.. */
         movl %cr0,%eax

         orl  $X86_CR0_PG,%eax
         movl %eax,%cr0           /* ..and set paging (PG) bit */

         ljmp $__BOOT_CS,$1f        /* Clear prefetch and normalize %eip */
1:
         /* Set up the stack pointer */
         //建立内核态堆栈
         lss stack_start,%esp

 

d、中断向量表初始化。

e、从BIOS中获取系统参数放到第一个页框中,识别处理器型号,用GDT和IDT表(由setup()函数初始化)的地址来填充gdtr和idtr寄存器。

f、跳转到start_kernel()函数。 
    

jmp start_kernel   

   

2.5      现代:start_kernel()函数

start_kernel()函数完成Linux内核的初始化工作,几乎所有内核都是由这个函数进行初始化的。执行后会显示“Linux version ...”信息,除此之外,在init程序和内核线程执行的最后阶段还会显示很多其他的信息。最后在控制台上初现熟悉的登录提示符,通知用户Linux内核已经启动,现在正在运行。

现在只提及初始化中的少部分:

a、调用sched_init()函数来初始化调度程序。

b、调用build_all_zonelists()函数来初始化内存管理区。

c、调用page_alloc_init()函数初始化伙伴系统分配程序。

d、调用trap_init()函数和init_IRQ()函数以完成IDT的初始化。

e、调用time_init()函数来初始化系统日期和时间。

f、调用kernel_thread()函数为进程1创建内核线程。

3      Linux启动过程分析总结

以下图来表示Linux启动的过程:

4      结合Linux启动过程时的内核输出信息分析启动过程

本例使用的是Ubuntu 8.04

sw2@sw2-desktop:~$ dmesg 执行查看命令

[    0.000000] Linux version 2.6.24-16-generic (buildd@palmer) (gcc version 4.2.3 (Ubuntu 4.2.3-2ubuntu7)) #1 SMP Thu Apr 10 13:23:42 UTC 2008 (Ubuntu 2.6.24-16.30-generic)

#Start_kernel()开始执行后的现实的信息--内核版本,gcc版本,发行版本的发行日期

 

[    0.000000] BIOS-provided physical RAM map:

[    0.000000]  BIOS-e820: 0000000000000000 - 000000000009f800 (usable) 可用

[    0.000000]  BIOS-e820: 000000000009f800 - 00000000000a0000 (reserved) 保留

[    0.000000]  BIOS-e820: 00000000000ca000 - 00000000000cc000 (reserved)

[    0.000000]  BIOS-e820: 00000000000dc000 - 00000000000e0000 (reserved)

[    0.000000]  BIOS-e820: 00000000000e4000 - 0000000000100000 (reserved)

[    0.000000]  BIOS-e820: 0000000000100000 - 000000000fef0000 (usable)

[    0.000000]  BIOS-e820: 000000000fef0000 - 000000000feff000 (ACPI data) ACPI数据

[    0.000000]  BIOS-e820: 000000000feff000 - 000000000ff00000 (ACPI NVS)ACPI NVS

[    0.000000]  BIOS-e820: 000000000ff00000 - 0000000010000000 (usable)

[    0.000000]  BIOS-e820: 00000000fec00000 - 00000000fec10000 (reserved)

[    0.000000]  BIOS-e820: 00000000fee00000 - 00000000fee01000 (reserved)

[    0.000000]  BIOS-e820: 00000000fffe0000 - 0000000100000000 (reserved)

使用e820技术显示物理内存表--从0x00000000~0x00010000

 

[    0.000000] 0MB HIGHMEM available. 0M高速缓存可用

[    0.000000] 256MB LOWMEM available. 160M低速缓存可用

[    0.000000] found SMP MP-table at 000f6aa0 找到SMP MP-table地址

[    0.000000] Entering add_active_range(0, 0, 65536) 0 entries of 256 used

[    0.000000] Zone PFN ranges: 物理页编号范围

[    0.000000]   DMA             0 ->     4096

[    0.000000]   Normal       4096 ->    65536

[    0.000000]   HighMem     65536 ->    65536

页面信息

.....

.....

Kernel command line: ro root=LABEL=/
#内核命令行:根只读

Initializing CPU#0
#初始化CPU#0

Detected 2793.547 MHz processor.
#检测cpu主频为2793.547

Console: colour VGA+ 80x25
#console类型

Calibrating delay loop... 5570.56 BogoMIPS
#校正回环延时,后面的数值好像是每秒运算多少百万条指令

Memory: 156648k/163840k available (1347k kernel code, 5460k reserved, 999k data, 132k init, 0k highmem)
#可用物理内存,kernel占用1347k;5460k保留;999k数据;132k初始化;0k

高速缓存

Dentry cache hash table entries: 32768 (order: 6, 262144 bytes)
Inode cache hash table entries: 16384 (order: 5, 131072 bytes)
Mount cache hash table entries: 512 (order: 0, 4096 bytes)
Buffer-cache hash table entries: 8192 (order: 3, 32768 bytes)
Page-cache hash table entries: 65536 (order: 6, 262144 bytes)
#各种缓存hash值

CPU: Trace cache: 12K uops<6>CPU: L2 cache: 256K
#cpu的L1和L2

Intel machine check architecture supported.
Intel machine check reporting enabled on CPU#0.
#硬件体系结构

CPU:     After generic, caps: 0febfbff 00000000 00000000 00000000
CPU:             Common caps: 0febfbff 00000000 00000000 00000000
CPU: Intel(R) Celeron(R) CPU 2.80GHz stepping 08
#CPU检测信息

Enabling fast FPU save and restore... done.
#启用快速FPU读写

Enabling unmasked SIMD FPU exception support... done.
#启用无屏蔽的单指令流FPU异常支持

Checking 'hlt' instruction... OK.
#检测hlt指令

POSIX conformance testing by UNIFIX
#POSIX一致性测试

mtrr: v1.40 (20010327) Richard Gooch (rgooch@atnf.csiro.au)
mtrr: detected mtrr type: Intel
#内核的MTRR支持,可提升PCI/AGP速率

PCI: PCI BIOS revision 2.10 entry at 0xfd9a0, last bus=1
PCI: Using configuration type 1
PCI: Probing PCI hardware
PCI: Using IRQ router PIIX [8086/7110] at 00:07.0
PCI: Cannot allocate resource region 4 of device 00:07.1
#PCI扩展插槽检测

Limiting direct PCI/PCI transfers.
#限制直连的PCI传输

isapnp: Scanning for PnP cards...
isapnp: No Plug & Play device found
#ISA扩展插槽检测

Linux NET4.0 for Linux 2.4
#2.4内核支持ipv4

Based upon Swansea University Computer Society NET3.039
#基于SUCS的NET3.039

Initializing RT netlink socket
#初始化网络接收接口

apm: BIOS version 1.2 Flags 0x03 (Driver version 1.16)
#高级电源管理

Starting kswapd
#启动kswpd进程,kswapd是核心调页线程,挺重要的一个核心线程

VFS: Disk quotas vdquot_6.5.1
#虚拟文件系统

pty: 2048 Unix98 ptys configured
#伪终端支持

Serial driver version 5.05c (2001-07-08) with MANY_PORTS MULTIPORT SHARE_IRQ SERIAL_PCI ISAPNP enabled
#串行设备的内核支持

ttyS0 at 0x03f8 (irq = 4) is a 16550A
ttyS1 at 0x02f8 (irq = 3) is a 16550A
#初始化tty

Real Time Clock Driver v1.10e
#始终程序

Floppy drive(s): fd0 is 1.44M
FDC 0 is a post-1991 82077
#软驱驱动

NET4: Frame Diverter 0.46
#网络的帧分离器版本

RAMDISK driver initialized: 16 RAM disks of 4096K size 1024 blocksize
#随机存储器支持

Uniform Multi-Platform E-IDE driver Revision: 7.00beta-2.4
#一致的多平台E-IDE驱动版本

ide: Assuming 33MHz system bus speed for PIO modes; override with idebus=xx
#IDE的工作频率

PIIX4: IDE controller at PCI slot 00:07.1
PIIX4: chipset revision 1
PIIX4: not 100% native mode: will probe irqs later
    ide1: BM-DMA at 0x1478-0x147f, BIOS settings: hdc:DMA, hdd:pio
#IDE驱动信息

hdc: VMware Virtual IDE CDROM Drive, ATAPI CD/DVD-ROM drive
#ATAPI CD/DVD-ROM drive

ide1 at 0x170-0x177,0x376 on irq 15
#IDE1位置和IRQ

ide-floppy driver 0.99.newide
ide-floppy driver 0.99.newide
#IDE驱动

 

md: md driver 0.90.0 MAX_MD_DEVS=256, MD_SB_DISKS=27
md: Autodetecting RAID arrays.
md: autorun ...
md: ... autorun DONE.
#多线程调试

NET4: Linux TCP/IP 1.0 for NET4.0
IP Protocols: ICMP, UDP, TCP, IGMP
IP: routing cache hash table of 1024 buckets, 8Kbytes
TCP: Hash tables configured (established 16384 bind 32768)
Linux IP multicast router 0.06 plus PIM-SM
#TCP/IP信息

NET4: Unix domain sockets 1.0/SMP for Linux NET4.0.
RAMDISK: Compressed image found at block 0
Freeing initrd memory: 247k freed
#空闲的初始化缓存

VFS: Mounted root (ext2 filesystem).
#挂接根文件系统

SCSI subsystem driver Revision: 1.00
#SCSI支持

PCI: Found IRQ 11 for device 00:10.0
#PCI IRQ支持

scsi: ***** BusLogic SCSI Driver Version 2.1.15 of 17 August 1998 *****
scsi: Copyright 1995-1998 by Leonard N. Zubkoff <lnz@dandelion.com>
scsi0: Configuring BusLogic Model BT-958 PCI Wide Ultra SCSI Host Adapter
scsi0:   Firmware Version: 5.07B, I/O Address: 0x1440, IRQ Channel: 11/Level
scsi0:   PCI Bus: 0, Device: 16, Address: 0xEC800000, Host Adapter SCSI ID: 7
scsi0:   Parity Checking: Enabled, Extended Translation: Enabled
scsi0:   Synchronous Negotiation: Ultra, Wide Negotiation: Enabled
scsi0:   Disconnect/Reconnect: Enabled, Tagged Queuing: Enabled
scsi0:   Scatter/Gather Limit: 128 of 8192 segments, Mailboxes: 211
scsi0:   Driver Queue Depth: 211, Host Adapter Queue Depth: 192
scsi0:   Tagged Queue Depth: Automatic, Untagged Queue Depth: 3
scsi0:   Error Recovery Strategy: Default, SCSI Bus Reset: Enabled
scsi0: *** BusLogic BT-958 Initialized Successfully ***
scsi0 : BusLogic BT-958
Vendor: VMware,   Model: VMware Virtual S Rev: 1.0
Type:   Direct-Access                      ANSI SCSI revision: 02


scsi0: Target 0: Queue Depth 28, Asynchronous
scsi0: Target 1: Queue Depth 3, Asynchronous
scsi0: Target 2: Queue Depth 3, Asynchronous
scsi0: Target 3: Queue Depth 3, Asynchronous
scsi0: Target 4: Queue Depth 3, Asynchronous
scsi0: Target 5: Queue Depth 3, Asynchronous
scsi0: Target 6: Queue Depth 3, Asynchronous
scsi0: Target 7: Queue Depth 3, Asynchronous
scsi0: Target 8: Queue Depth 3, Asynchronous
scsi0: Target 9: Queue Depth 3, Asynchronous
scsi0: Target 10: Queue Depth 3, Asynchronous
scsi0: Target 11: Queue Depth 3, Asynchronous
scsi0: Target 12: Queue Depth 3, Asynchronous
scsi0: Target 13: Queue Depth 3, Asynchronous
scsi0: Target 14: Queue Depth 3, Asynchronous
scsi0: Target 15: Queue Depth 3, Asynchronous
Attached scsi disk sda at scsi0, channel 0, id 0, lun 0
SCSI device sda: 8388608 512-byte hdwr sectors (4295 MB)
#检测和调试SCSI驱动器过程

Partition check:
sda: sda1 sda2 sda3 sda4 < sda5 >
#分区检测

Journalled Block Device driver loaded
#驱动器载入

EXT3-fs: INFO: recovery required on readonly filesystem.
EXT3-fs: write access will be enabled during recovery.
scsi0: Tagged Queuing now active for Target 0
kjournald starting. Commit interval 5 seconds
EXT3-fs: recovery complete.
EXT3-fs: mounted filesystem with ordered data mode.
#文件系统检测、挂接、设置

Freeing unused kernel memory: 132k freed
#空闲的可用核心内存

usb.c: registered new driver usbdevfs
usb.c: registered new driver hub
usb-uhci.c: $Revision: 1.275 $ time 17:59:01 Mar 13 2003
usb-uhci.c: High bandwidth mode enabled
#USB驱动支持和检测

PCI: Found IRQ 9 for device 00:07.2
PCI: Sharing IRQ 9 with 00:12.0
#PCI设备检测

 

usb-uhci.c: USB UHCI at I/O 0x1060, IRQ 9
usb-uhci.c: Detected 2 ports
usb.c: new USB bus registered, assigned bus number 1
hub.c: USB hub found
hub.c: 2 ports detected
usb-uhci.c: v1.275:USB Universal Host Controller Interface driver
usb.c: registered new driver hiddev
usb.c: registered new driver hid
#usb设备检测

hid-core.c: v1.8.1 Andreas Gal, Vojtech Pavlik <vojtech@suse.cz>
hid-core.c: USB HID support drivers
mice: PS/2 mouse device common for all mice
EXT3 FS 2.4-0.9.19, 19 August 2002 on sd(8,2), internal journal
Adding Swap: 305192k swap-space (priority -1)
kjournald starting. Commit interval 5 seconds
EXT3 FS 2.4-0.9.19, 19 August 2002 on sd(8,1), internal journal
EXT3-fs: mounted filesystem with ordered data mode.
kjournald starting. Commit interval 5 seconds
EXT3 FS 2.4-0.9.19, 19 August 2002 on sd(8,3), internal journal
EXT3-fs: mounted filesystem with ordered data mode.
parport0: PC-style at 0x378 [PCSPP,TRISTATE]
#文件系统挂接

ip_tables: (C) 2000-2002 Netfilter core team
#ip_tables核心线程信息

pcnet32.c:v1.27b 01.10.2002 tsbogend@alpha.franken.de
PCI: Found IRQ 10 for device 00:11.0
pcnet32: PCnet/PCI II 79C970A at 0x1080, 00 0c 29 e5 f4 4d assigned IRQ 10.
divert: allocating divert_blk for eth0
eth0: registered as PCnet/PCI II 79C970A
pcnet32: 1 cards_found.
#网卡

ip_tables: (C) 2000-2002 Netfilter core team
ip_tables: (C) 2000-2002 Netfilter core team
ip_tables: (C) 2000-2002 Netfilter core team

 

5      参考资料

[1]深入理解Linux内核 第三版 陈莉君等译

posted @ 2013-05-09 14:20  悉路  阅读(1817)  评论(0编辑  收藏  举报