内核修炼之道

来源:互联网 发布:山东淘宝拍卖会 编辑:程序博客网 时间:2024/06/02 09:01

2011.8.25

1.内核的剪裁与配置:

      为了满足某些嵌入式应用的要求,需要把Linux内核裁剪得足够小,通过配置去除掉不需要的特性,或者部分特性编译成可加载的内核模块来缩减内核的体积。

2. static int __init usb_init(void) 

__init标记,它对内核来说是一种暗示,表明它修饰的函数,仅在初始化期间使用,当模块装载之后,它占用的资源就会被释放用做他用。__init的定义在include/linux/init.h文件里。

#define __init  __attribute__ ((__section__ (".init.text")))    __attribute__GNU C 扩展的其中一种功能,主要是用来声明一些特殊的属性,告诉并且指示编译器去进行特定方面的优化和更仔细的代码检查。GNU C支持十几个属性,section便是其中的一个。通常情况下,编译器将函数放在 .text节,变量放在 .data 和 .bss节,使用section属性,可以讲函数或者变量放在自己定义的节中,则__init的定义便表示将它修饰的代码放在.init.text节。链接器会为相同节的代码和数据安排在一起,比如__init修饰的所有代码都会被放在.init.text节里,初始化结束后就可以释放这部分内存。

3. subsys_initcall宏,它也在include/linux/init.h里定义

#define subsys_initcall(fn)             __define_initcall("4",fn,4)

这里又出现了一个宏__define_initcall,它用于将指定的函数指针fn放到initcall.init节里 而对于具体的subsys_initcall宏,则是把fn放到.initcall.init的子节.initcall4.init里。内核可执行文件由许多链接在一起的对象文件组成。对象文件有许多节,如文本、数据、init数据、bass等等。这些对象文件都是由一个称为链接器脚本的文件链接并装入的。这个链接器脚本的功能是将输入对象文件的各节映射到输出文件中;换句话说,它将所有输入对象文件都链接到单一的可执行文件中,将该可执行文件的各节装入到指定地址处。 vmlinux.lds是存在于arch/<target>/ 目录中的内核链接器脚本,它负责链接内核的各个节并将它们装入内存中特定偏移量处。

4.Linuxcmake-2.8.4 的编译和安装

如果还没有安装CMake,源码树中提供了一个 bootstrap 脚本:
#./bootstrap
#make
#make install 

一。源码安装的cmake 如何卸载(删除)。

二。如何添加永久环境变量使其 直接cmake 就可以 不用只想到cmake的安装目录。

三。安装cmake是 有个 configure 和 bootstrap 两个配置命令有什么区别?

1make uninstall ,不过不是所有的都支持

2export PATH=/cmake/bin:$PATH

3、我记得 bootstrap 是某个项目管理程序的控制脚本,用来生成编译环境的。configure 好像就归他生成,但一般软件项目发布后都会生成好足够用的环境,所以不一定需要这个东西。

2011.8.26

LKD,ULK等书确实经典,但是整日抱着它们用功地啃,最多只是说明你是一个很有上进心很应该得到表彰的好青年、好同志,不过也就仅此而已,毫不夸张地说,学习内核就是学习内核代码,内核代码本身就是最好的参考资料,其他任何经典或者非经典的书笔者都只是起到辅助作用,不能也不应该取代内核代码在我们学习过程中的主导地位。

浏览内核源代码:

1.内核一方面负责与计算机的硬件进行交互,实现对硬件的编程控制和接口操作,调度对硬件资源的访问,另一方面为用户应用程序提供了一个高级的执行环境和访问硬件的虚拟接口。

2.内核又分为体系相关部分(为体系结构和硬件特有)和体系无关部分(可移植)。通常在体系无关部分定义体系相关部分的接口,这样,内核在向新的体系结构移植过程中就变成确定这些接口的特性并且将它们加以实现的过程。

3.进程通信:信号,共享内存,消息队列,管道,信号量。

4.进程引入:

即“执行中的程序”,呈现出程序在内存中执行的动态特性。在多道程序执行过程中,需要共享系统资源,从而导致各个程序在执行过程中相互制约的关系(具有间断性),这种特新是在程序执行过程中才发生的,是动态的,而传统的程序是一组指令的集合,是静态的概念,再也无法其在内存中的执行情况(即不知道何时开始何时结束与其他程序之间的关系等等),从而程序这个静态概念再也无法如实反映出程序并发执行的动态特性,因为引入了进程。

5.进程定义:

进程是一个具有一定对功能的程序关于某个数据集合的一次运行活动。它是操作系统动态执行的基本单元,在传统的操作系统中,它既是基本分配单元,也是基本的执行单元。

第一,进程是一个实体。每一个进程都有它自己的地址空间,一般情况下,包括文本区域(text region)、数据区域(data region)和堆栈(stack region)。文本区域存储处理器执行的代码;数据区域存储变量和进程执行期间使用的动态分配的内存;堆栈区域存储着活动过程调用的指令和本地变量。第二,进程是一个执行中的程序。程序是一个没有生命的实体,只有处理器赋予程序生命时,它才能成为一个活动的实体,我们称其为进程。 

6.进程特性:

动态性:动态产生,动态消亡

并发性:与其他进程并发执行

独立性:独立运行的基本单位,同时也是系统分配资源和调度的独立单位

异步性:由于进程间的相互制约,使进程具有执行的间断性,即进程按各自独立的、不 可预知的速度向前推进 

结构性:进程由程序、数据和进程控制块三部分组成

7.分时:

即时间片轮转,分时系统允许多个用户同时使用计算机。操作系统采用时间片轮转的方式处理每个用户的服务请求。

8.GCC扩展:

1)语句表达式

+++ include /linux/kernel.h

#define min(x,y)({ \

typeof(x) _x = (x); \  /*typeofx)表示x的类型*/

typeof(y) _y = (y); \

void (&_x == &_y);\  /*判断xy类型是否相同*/

_x < _y ?_x : _y  })

2)零长度数组

+++ include/linux/usb

struct usb_interface_cache{

unsigend num_altsetting;

struct kref ref; 

struct usb_host_interface altestting[0];

//也称可变长数组,不占用结构的空间,但它意味着这个结构的长度充满了变数,创建该结构对象时,可以根据实际需要制定这个可变长数组的长度,并且分配相应的空间。

3)可变参数宏

#define debug(format, ...)  fprintf ( stderr, format, __VA_ARGS__)

...”表示可变参数,实际调用时,它会代替宏体里面的__VA_ARGS__GCC支持更复杂的形式(上面是ISO C标准),可以给可变参数取名。

#define debug(format, args...)  fprintf ( stderr, format, args)

内核中例子:

+++ include/linux/kernel.h

244 #define pr_info(fmt, arg...) printk(KERN_INFO fmt, ##arg) ##主要是针对参数为空的情况。例:debug"a message";宏展开后字符串后面会多出一个,号,使用##可以避免。

4)标号元素

GCC中数组或结构变量的初始化可以指定索引或结构域名,允许初始化值以任意顺序出现(C标准中则不行,需顺序初始化),未出现在初始化中的元素,其初值为0

int a[k]={ [0 ... K-1] = 1}; 

const stuct file_operation ext2_file_operations = {

.llseek = generic_file_llseek ,

.read = do_sync_read ,

...

};

5)特殊属性(__attribute__

GCC允许声明函数、变量和类型的特殊属性,以便指示编译器进行特定方面的优化和更仔细的代码检查。使用方式在声明后加上:

__attribute__((ATTRIBUTE))

ATTRIBUTE是属性的说明,GCC支持十几种属性:

1)noreturn:用于函数,表示该函数从不返回,它让编译器生成更为优化的代码。

2)format( archtype, string-index, first-to-check): 让编译器检查格式化字符串是否匹配

3)unused:用于函数和变量,表示该函数或者变量可能并不使用,这个属性能够避免编译器产生警告信息

4)section (略)

5)aligned:用于变量、结构体或联合,设定一个指定大小的对齐格式,以字节为单位

例如:aligned256)若在一个结构体声明后面,则表示结构体里面的各个变量都是以256个字节对齐,又例如aligned2)在一个结构体(有四个字节的int变量,一个字节的char变量,两个字节的short变量)后,则各个变量都是以四个字节对齐,那么siziof(结构体)= 4+2+2=8byte

6)packed:使用最小可能的对齐,使用最小内存。不要添加填充位

7)内联函数:很多事标准的C库函数的内建版本,比如mencpy;还有内建函数名字通常以__builtin开始,例如__builtin_expect(long exp, long c)用于处理预测、优化程序。这个内建函数意思即是exp的预期值是c,编译器可以根据这个信息适当地安排条件语句块的顺序,将符合这个条件的分支放在合适的地方。

+++ include/linux/compiler.h

#define likely(x) __builtin_expect(!!(x),1) 

#define unlikely(x) __builtin_expect(!!(x),0) 

if(unlikely(x))

{。。。}

告诉编译器条件x发生可能性不大,编译的时候会把这个条件块里的语句的目标码可能就会放在一个比较远的位置,以保证经常执行的目标码更紧凑。

内核中的链表:include/linux/list.h

struct list_head{

struct list_head *next, *prev;

};

内核链表与通常数据结构上学的单链表不同,前者没有在链表结构中包含数据,而是在描述数据结构中包含链表。

插入:

struct  inline  __list_add( struct list_head *new , struct list_head *prev, struct list_head *next )

{

next->prev = new;

new->next = next; 

new->prev = prev;

prev->next = new; 

}

static inline void list_add(struct list_head *new , struct list_head *head)

{

__list_add( new, head, head->next);//插到head之后

}

static inline void list_add_tail(struct list_head *new , struct list_head *head)

{

__list_add( new, head->prev, head);//插到head->prev之后

}

删除:

static inline void __list_del(struct list_head * prev, struct list_head * next)

{

next->prev = prev;

prev->next = next;

}

static inline void list_del(struct list_head *entry)

{

__list_del(entry->prev, entry->next);//删除entry

entry->next = LIST_POISON1;

entry->prev = LIST_POISON2;

}

static inline void list_replace(struct list_head *old, struct list_head *new)

{

new->next = old ->next;

new->next->prev = new;

new->prev = old->prev;

new->prev->next = new;

}

static inline void list_replace_init(struct list_head *old, struct list_head *new)

{

list_replace(old, new);//从链表里删除一个元素,并且将其初始化

INIT_LIST_HEAD(old);//初始化生成的链表头的两个指针nextprev都指向自己

}

遍历:

#define list_entry(ptr, type, member) \

container_of(ptr, type, member)

例如:

struct list_head *tmp;       struct usb_hub *hub;      tmp = hub_evevt_list.next;

hub = list_entry(tmp, struct usb_hub, event_list); 从全局链表hub_evevt_list中取出一个tmp,通过tmp,获得它所对应的struct_usb_hub

2011.8.29

Kconfig结构

1)config定义一个新的菜单项,菜单项属性有类型(bool(选中或不选中),tristatebool基础上增加了M模块)),依赖关系,选择提示,帮助信息等。

2)菜单组织结构(树状结构)。通过关键字“menu”显示声明为菜单,或者依赖关系。

利用KconfigMakefile寻找目标代码

linux/drivers/usb/Kconfig 内容

# USB device configuration

menuconfig USB_SUPPORT  //显示声明为菜单,组织成树状结构

bool "USB support"     //类型,即可选可不选

depends on HAS_IOMEM //依赖关系

default y //默认

---help--- //帮助信息

  This option adds core support for Universal Serial Bus (USB).

  You will also need drivers from the following menu to make use of it.

if USB_SUPPORT

# Host-side USB depends on having a host controller

# NOTE:  dummy_hcd is always an option, but it's ignored here ...

# NOTE:  SL-811 option should be board-specific ...

config USB_ARCH_HAS_HCD //主机控制器的驱动程序(HCD)。它位于USB主机控制器与USB系统软件之间。 

boolean  //布尔值,即可选

default y if USB_ARCH_HAS_OHCI

default y if USB_ARCH_HAS_EHCI

default y if PCMCIA && !M32R # sl811_cs

default y if ARM # SL-811

default y if SUPERH # r8a66597-hcd

default PCI

# many non-PCI SOC chips embed OHCI

config USB_ARCH_HAS_OHCI

boolean

# ARM://这些体系结构都默认选上该菜单

default y if SA1111

default y if ARCH_OMAP

default y if ARCH_LH7A404

default y if ARCH_S3C2410

default y if PXA27x

default y if PXA3xx

default y if ARCH_EP93XX

default y if ARCH_AT91

default y if ARCH_PNX4008 && I2C

default y if MFD_TC6393XB

# PPC:

default y if STB03xxx

default y if PPC_MPC52xx

# MIPS:

default y if SOC_AU1X00

# SH:

default y if CPU_SUBTYPE_SH7720

default y if CPU_SUBTYPE_SH7721

default y if CPU_SUBTYPE_SH7763

default y if CPU_SUBTYPE_SH7786

# more:

default PCI

# some non-PCI hcds implement EHCI

config USB_ARCH_HAS_EHCI

boolean

default y if PPC_83xx

default y if SOC_AU1200

default y if ARCH_IXP4XX

default PCI

# ARM SA1111 chips have a non-PCI based "OHCI-compatible" USB host interface.

config USB

tristate "Support for Host-side USB" //可编译成为模块

depends on USB_ARCH_HAS_HCD

---help---

  Universal Serial Bus (USB) is a specification for a serial bus

  subsystem which offers higher speeds and more features than the

  traditional PC serial port.  The bus supplies power to peripherals

  and allows for hot swapping.  Up to 127 USB peripherals can be

  connected to a single USB host in a tree structure.

  

  The USB host is the root of the tree, the peripherals are the

  leaves and the inner nodes are special USB devices called hubs.

  Most PCs now have USB host ports, used to connect peripherals

  such as scanners, keyboards, mice, modems, cameras, disks,

  flash memory, network links, and printers to the PC.

  Say Y here if your computer has a host-side USB port and you want

  to use USB devices.  You then need to say Y to at least one of the

  Host Controller Driver (HCD) options below.  Choose a USB 1.1

  controller, such as "UHCI HCD support" or "OHCI HCD support",

  and "EHCI HCD (USB 2.0) support" except for older systems that

  do not have USB 2.0 support.  It doesn't normally hurt to select

  them all if you are not certain.

  If your system has a device-side USB port, used in the peripheral

  side of the USB protocol, see the "USB Gadget" framework instead.

  After choosing your HCD, then select drivers for the USB peripherals

  you'll be using.  You may want to check out the information provided

  in <file:Documentation/usb/> and especially the links given in

  <file:Documentation/usb/usb-help.txt>.

  To compile this driver as a module, choose M here: the

  module will be called usbcore.

source "drivers/usb/core/Kconfig"    //结束一个菜单项

source "drivers/usb/mon/Kconfig"

source "drivers/usb/wusbcore/Kconfig"

source "drivers/usb/host/Kconfig"

source "drivers/usb/musb/Kconfig"

source "drivers/usb/class/Kconfig"

source "drivers/usb/storage/Kconfig"

source "drivers/usb/image/Kconfig"

comment "USB port drivers"  定义菜单项

depends on USB

config USB_USS720

tristate "USS720 parport driver"

depends on USB && PARPORT

select PARPORT_NOT_PC

---help---

  This driver is for USB parallel port adapters that use the Lucent

  Technologies USS-720 chip. These cables are plugged into your USB

  port and provide USB compatibility to peripherals designed with

  parallel port interfaces.

  The chip has two modes: automatic mode and manual mode. In automatic

  mode, it looks to the computer like a standard USB printer. Only

  printers may be connected to the USS-720 in this mode. The generic

  USB printer driver ("USB Printer support", above) may be used in

  that mode, and you can say N here if you want to use the chip only

  in this mode.

  Manual mode is not limited to printers, any parallel port

  device should work. This driver utilizes manual mode.

  Note however that some operations are three orders of magnitude

  slower than on a PCI/ISA Parallel Port, so timing critical

  applications might not work.

  Say Y here if you own an USS-720 USB->Parport cable and intend to

  connect anything other than a printer to it.

  To compile this driver as a module, choose M here: the

  module will be called uss720.

source "drivers/usb/serial/Kconfig"

source "drivers/usb/misc/Kconfig"

source "drivers/usb/atm/Kconfig"

source "drivers/usb/gadget/Kconfig"

source "drivers/usb/otg/Kconfig"

endif # USB_SUPPORT

linux/drivers/usb/gedget目录存放usb gadget的驱动程序,控制外围设备如何作为一个usb设备和主机通信,比如嵌入式开发板通常会支持SD卡,使用usb连接线将开发板连接到PC机时,通过usb gadget架构的驱动,可以将该SD卡模拟成U盘使用。

Linux/drivers/usb/core/usb.c

...

...

subsys_initcall(usb_init); //内核使用subsys_initcall宏来指定初始化函数,这里代码较核心

module_exit(usb_exit);

MODULE_LICENSE("GPL");

//核心usb_init初始化函数 ,在/usb/core/usb.c

static int __init usb_init(void)

{

....

}

__init 修饰的代码放在.init.text节中,初始化结束候便可以释放这部分内存。

init/main.c 里面(do_initcalls)实际调用这里的__initcall_start  __initcall_end

原创粉丝点击