2010年01月17日

reference to: http://hi.baidu.com/liu_bin0101/blog/item/8d3cfd2efb7725594ec22623.html


在学习块设备原理的时候,我最关系块设备的数据流程,从应用程序调用Read或者Write开始,数据在内核中到底是如何流通、处理的呢?然后又如何抵达具体的物理设备的呢?下面对一个带Cache功能的块设备数据流程进行分析。

1、 用户态程序通过open()打开指定的块设备,通过systemcall机制陷入内核,执行blkdev_open()函数,该函数注册到文件系统方法(file_operations)中的open上。在blkdev_open函数中调用bd_acquire()函数,bd_acquire函数完成文件系统inode到块设备bdev的转换,具体的转换方法通过hash查找实现。得到具体块设备的bdev之后,调用do_open()函数完成设备打开的操作。在do_open函数中会调用到块设备驱动注册的open方法,具体调用如下:gendisk->fops->open(bdev->bd_inode, file)

2、 用户程序通过readwrite函数对设备进行读写,文件系统会调用相应的方法,通常会调用如下两个函数:generic_file_readblkdev_file_write。在读写过程中采用了多种策略,首先分析读过程。

3、 用户态调用了read函数,内核执行generic_file_read,如果不是direct io方式,那么直接调用do_generic_file_read->do_generic_mapping_read()函数,在do_generic_mapping_read(函数位于filemap.c)函数中,首先查找数据是否命中Cache,如果命中,那么直接将数据返回给用户态;否则通过address_space->a_ops->readpage函数发起一个真实的读请求。readpage函数中,构造一个buffer_head,设置bh回调函数end_buffer_async_read,然后调用submit_bh发起请求。在submit_bh函数中,根据buffer_head构造bio,设置bio的回调函数end_bio_bh_io_sync,最后通过submit_biobio请求发送给指定的快设备。

4、 如果用户态调用了一个write函数,内核执行blkdev_file_write函数,如果不是direct io操作方式,那么执行buffered write操作过程,直接调用generic_file_buffered_write函数。Buffered write操作方法会将数据直接写入Cache,并进行Cache的替换操作,在替换操作过程中需要对实际的快设备进行操作,address_space->a_ops提供了块设备操作的方法。当数据被写入到Cache之后,write函数就可以返回了,后继异步写入的任务绝大部分交给了pdflush daemon(有一部分在替换的时候做了)

5、 数据流操作到这一步,我们已经很清楚用户的数据是如何到内核了。与用户最接近的方法是file_operations,每种设备类型都定义了这一方法(由于Linux将所有设备都看成是文件,所以为每类设备都定义了文件操作方法,例如,字符设备的操作方法为def_chr_fops,块设备为def_blk_fops,网络设备为bad_sock_fops)。每种设备类型底层操作方法是不一样的,但是通过file_operations方法将设备类型的差异化屏蔽了,这就是Linux能够将所有设备都理解为文件的缘由。到这里,又提出一个问题:既然这样,那设备的差异化又该如何体现呢?在文件系统层定义了文件系统访问设备的方法,该方法就是address_space_operations,文件系统通过该方法可以访问具体的设备。对于字符设备而言,没有实现address_space_operations方法,也没有必要,因为字符设备的接口与文件系统的接口是一样的,在字符设备open操作的过程中,将inode所指向的file_operations替换成cdev所指向的file_operations就可以了。这样用户层读写字符设备可以直接调用cdevfile_operations方法了。

6、 截至到步骤(4),读操作在没有命中Cache的情况下通过address_space_operations方法中的readpage函数发起块设备读请求;写操作在替换Cache或者Pdflush唤醒时发起块设备请求。发起块设备请求的过程都一样,首先根据需求构建bio结构,bio结构中包含了读写地址、长度、目的设备、回调函数等信息。构造完bio之后,通过简单的submit_bio函数将请求转发给具体的块设备。从这里可以看出,块设备接口很简单,接口方法为submit_bio(更底层函数为generic_make_request),数据结构为struct bio

7、 submit_bio函数通过generic_make_request转发biogeneric_make_request是一个循环,其通过每个块设备下注册的q->make_request_fn函数与块设备进行交互。如果访问的块设备是一个有queue的设备,那么会将系统的__make_request函数注册到q->make_request_fn中;否则块设备会注册一个私有的方法。在私有的方法中,由于不存在queue队列,所以不会处理具体的请求,而是通过修改bio中的方法实现bio的转发,在私有make_request方法中,往往会返回1,告诉generic_make_request继续转发比bioGeneric_make_request的执行上下文可能有两种,一种是用户上下文,另一种为pdflush所在的内核线程上下文。

8、 通过generic_make_request的不断转发,最后请求一定会到一个存在queue队列的块设备上,假设最终的那个块设备是某个scsi disk/dev/sda)。generic_make_request将请求转发给sda时,调用__make_request,该函数是Linux提供的块设备请求处理函数。在该函数中实现了极其重要的操作,通常所说的IO Schedule就在该函数中实现。在该函数中试图将转发过来的bio merge到一个已经存在的request中,如果可以合并,那么将新的bio请求挂载到一个已经存在request中。如果不能合并,那么分配一个新的request,然后将bio添加到其中。这一切搞定之后,说明通过generic_make_request转发的bio已经抵达了内核的一个站点——request,找到了一个临时归宿。此时,还没有真正启动物理设备的操作。在__make_request退出之前,会判断一个bio中的sync标记,如果该标记有效,说明请求的bio是一个是实时性很强的操作,不能在内核中停留,因此调用了__generic_unplug_device函数,该函数将触发下一阶段的操作;如果该标记无效的话,那么该请求就需要在queue队列中停留一段时间,等到queue队列触发闹钟响了之后,再触发下一阶段的操作。__make_request函数返回0,告诉generic_make_request无需再转发bio了,bio转发结束。

9、 到目前为止,文件系统(pdflush或者address_space_operations)发下来的bio已经mergerequest queue中,如果为sync bio,那么直接调用__generic_unplug_device,否则需要在unplug timer的软中断上下文中执行q->unplug_fn。后继request的处理方法应该和具体的物理设备相关,但是在标准的块设备上如何体现不同物理设备的差异性呢?这种差异性就体现在queue队列的方法上,不同的物理设备,queue队列的方法是不一样的。举例中的sda是一个scsi设备,在scsi middle levelscsi_request_fn函数注册到了queue队列的request_fn方法上。在q->unplug_fn(具体方法为:generic_unplug_device)函数中会调用request队列的具体处理函数q->request_fnOk,到这一步实际上已经将块设备层与scsi总线驱动层联系在了一起,他们的接口方法为request_fn(具体函数为scsi_request_fn)。

10、明白了第(9)点之后,接下来的过程实际上和具体的scsi总线操作相关了。在scsi_request_fn函数中会扫描request队列,通过elv_next_request函数从队列中获取一个request。在elv_next_request函数中通过scsi总线层注册的q->prep_rq_fnscsi层注册为scsi_prep_fn)函数将具体的request转换成scsi驱动所能认识的scsi command。获取一个request之后,scsi_request_fn函数直接调用scsi_dispatch_cmd函数将scsi command发送给一个具体的scsi host。到这一步,有一个问题:scsi command具体转发给那个scsi host呢?秘密就在于q->queuedata中,在为sda设备分配queue队列时,已经指定了sda块设备与底层的scsi设备(scsi device)之间的关系,他们的关系是通过request queue维护的。

11、 scsi_dispatch_cmd函数中,通过scsi host的接口方法queuecommandscsi command发送给scsi host。通常scsi hostqueuecommand方法会将接收到的scsi command挂到自己维护的队列中,然后再启动DMA过程将scsi command中的数据发送给具体的磁盘。DMA完毕之后,DMA控制器中断CPU,告诉CPU DMA过程结束,并且在中断上下文中设置DMA结束的中断下半部。DMA中断服务程序返回之后触发软中断,执行SCSI中断下半部。

12、     SCSi中断下半部中,调用scsi command结束的回调函数,这个函数往往为scsi_done,在scsi_done函数调用blk_complete_request函数结束请求request,每个请求维护了一个bio链,所以在结束请求过程中回调每个请求中的bio回调函数,结束具体的bioBio又有文件系统的buffer head生成,所以在结束bio时,回调buffer_head的回调处理函数bio->bi_end_io(注册为end_bio_bh_io_sync)。自此,由中断引发的一系列回调过程结束,总结一下回调过程如下:scsi_done->end_request->end_bio->end_bufferhead

13、  回调结束之后,文件系统引发的读写操作过程结束。

块设备层分析(1)

R.wen

一、综述

1是块设备操作的一个分层实现图。当一个进程调用read读取一个文件时,内核执行如下一个过程:首先,它通过VFS层去读取要到的文件块有没有已经被cache了,这个cache由一个buffer_head结构读取。如果要读取的文件块还没有被cache,则就要从文件系统中去读取了,这就是文件系统的映射层,它通过一个address_space结构来引用,然后调用文件系统读函数(readpage)去读取一个页面大小的数据,这个读函数对于不同的文件系统来说,是不一样的。当它从磁盘中读出数据时,它会将数据页链入cache中,当下次再读取时,就不需要再次从磁盘出去读了。Readpage()函数并不是直接去操作磁盘,而只是将请求初始化成一个bio结构,并提交给通用块层(generic block layer)

1

       它就通过submit_bio()去完成的。通用块层再调用相应设备的IO调度器,通过这个调度器的调度算法,将这个bio或合并到已存在的request中,或创建一个新的request,并将这个新创建的request插入到设备的请求队列中去。这就完成了IO调度层的工作。最后就是块设备驱动所做的工作了。IO调度器传递给块驱动的是一个请求队列,块驱动就是要处理这个队列中的请求,直到这个队列为空为止。

二、通用块层(generic block layer)

通用块层操作的是一个bio结构,这个结构主要的数据域是,

unsigned short              bi_vcnt;

struct bio_vec        *bi_io_vec;     /* the actual vec list */

这个就是要读写的数据向量,且每个struct bio_vec       为一个segment

//这个函数主要是调用generic_make_request()去完成工作:

void submit_bio(int rw, struct bio *bio)

{

       ……

       generic_make_request(bio);

}

//这个函数的主要作用是将bio传递给驱动去处理

void generic_make_request(struct bio *bio)

{

       ……

       do {

              char b[BDEVNAME_SIZE];

              //取得块设备相应的队列,每个设备一个

              q = bdev_get_queue(bio->bi_bdev);

             

              /*

              * If this device has partitions, remap block n

              * of partition p to block n+start(p) of the disk.

              */

              blk_partition_remap(bio); //块设备分区信息转换,如将相对于一个分区的的偏移地址转换成相对于整个块设备的绝对偏移等等。

             

              old_sector = bio->bi_sector;

              old_dev = bio->bi_bdev->bd_dev;

              ……

              //这个是块设备队列的请求处理函数。由块设备创建请求队列时初始化。

              //对于IDE等设备,它是__make_request()。但对于ramdisk就不一样了。

              ret = q->make_request_fn(q, bio); // __make_request()

       } while (ret);

}

//这要函数的主要作用就是调用IO调度算法将bio合并,或插入到队列中合适的位置中去

static int __make_request(request_queue_t *q, struct bio *bio)

{

       struct request *req;

       int el_ret, nr_sectors, barrier, err;

       const unsigned short prio = bio_prio(bio);

       const int sync = bio_sync(bio);

       int rw_flags;

       nr_sectors = bio_sectors(bio);

       //用于处理高端内存

       blk_queue_bounce(q, &bio);

      

       spin_lock_irq(q->queue_lock);

       //测试是否能合并,本文忽略IO调度算法

       el_ret = elv_merge(q, &req, bio);

       switch (el_ret) {

              //前两种可以合并

              case ELEVATOR_BACK_MERGE:

                     ……

                     goto out;

              case ELEVATOR_FRONT_MERGE:

                     ……

                     goto out;

             

//不能合并,需要新创一个request

              /* ELV_NO_MERGE: elevator says don’t/can’t merge. */

              default:

                     ;

       }

get_rq:

      

       rw_flags = bio_data_dir(bio);

       if (sync)

              rw_flags |= REQ_RW_SYNC;

       //新创一个request

       req = get_request_wait(q, rw_flags, bio);

       //初始化这个request

       init_request_from_bio(req, bio);

       spin_lock_irq(q->queue_lock);

       if (elv_queue_empty(q)) //空队列的处理

              blk_plug_device(q);

       add_request(q, req); //将新请求加入队列中去

out:

       if (sync) //如果需要同步,立即处理请求

              __generic_unplug_device(q);

       spin_unlock_irq(q->queue_lock);

       return 0;

end_io:

       bio_endio(bio, nr_sectors << 9, err);

       return 0;

}

//触发块设备驱动进行真正的IO操作

void __generic_unplug_device(request_queue_t *q)

{

       if (unlikely(blk_queue_stopped(q)))

              return;

       if (!blk_remove_plug(q))

              return;

       q->request_fn(q); //设备的请求处理函数,属于驱动层

}    


2010年01月16日
在内核BSP中增加一块新的skyeye2410电路板

添加板相关代码

为新板子在arch/arm/mach-xxx添加Kconfig和Makefile项目:

Index: arch/arm/mach-s3c2410/Kconfig===================================================================--- arch/arm/mach-s3c2410/Kconfig       (revision 87)+++ arch/arm/mach-s3c2410/Kconfig       (working copy)@@ -139,4 +139,10 @@        help           Say Y here if you are using the Armzone QT2410 +config MACH_SKYEYE2410+       bool "skyeye 2410 board"+       select CPU_S3C2410+       help+          Say Y here if you are using the skyeye to simulate S3C2410+ endmenuIndex: arch/arm/mach-s3c2410/Makefile===================================================================--- arch/arm/mach-s3c2410/Makefile      (revision 87)+++ arch/arm/mach-s3c2410/Makefile      (working copy)@@ -29,6 +29,7 @@ obj-$(CONFIG_MACH_TCT_HAMMER)  += mach-tct_hammer.o obj-$(CONFIG_MACH_VR1000)      += mach-vr1000.o usb-simtec.o obj-$(CONFIG_MACH_QT2410)      += mach-qt2410.o+obj-$(CONFIG_MACH_SKYEYE2410)  += mach-skyeye2410.o  # Common bits of machine support

添加新的板文件arch/arm/mach-s3c2410/mach-skyeye2410.c:

/* linux/arch/arm/mach-s3c2410/mach-skyeye2410.c * * linux/arch/arm/mach-s3c2410/mach-skyeye2410.c * * Copyright (C) 2010 by Barry Song * All rights reserved. * * @Author: Barry Song * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, * MA 02111-1307 USA * * @History: * derived from linux/arch/arm/mach-s3c2410/mach-bast.c, written by * Ben Dooks <ben@simtec.co.uk> * ***********************************************************************/

#include <linux/kernel.h>#include <linux/types.h>#include <linux/interrupt.h>#include <linux/list.h>#include <linux/timer.h>#include <linux/init.h>#include <linux/serial_core.h>#include <linux/platform_device.h>#include <linux/io.h>

#include <asm/mach/arch.h>#include <asm/mach/map.h>#include <asm/mach/irq.h>

#include <mach/hardware.h>#include <asm/irq.h>#include <asm/mach-types.h>

#include <plat/regs-serial.h>#include <plat/iic.h>

#include <plat/devs.h>#include <plat/cpu.h>

#include <plat/common-smdk.h>

#include <mach/regs-lcd.h>#include <mach/fb.h>

static struct map_desc skyeye2410_iodesc[] __initdata = {        /* nothing here yet */        {                .virtual=  0xe0000000,                .pfn= __phys_to_pfn(0x19000000),                .length= SZ_1M,                .type= MT_DEVICE        },};

#define LCD_WIDTH 320#define LCD_HEIGHT 240#define LCD_PIXCLOCK 40000#define LCD_RIGHT_MARGIN 67#define LCD_LEFT_MARGIN 40#define LCD_HSYNC_LEN 31#define LCD_UPPER_MARGIN 25#define LCD_LOWER_MARGIN 5#define LCD_VSYNC_LEN 

static struct s3c2410fb_display skyeye2410_lcd_cfg __initdata = {        .lcdcon5        = S3C2410_LCDCON5_FRM565 |                                          S3C2410_LCDCON5_INVVLINE |                              S3C2410_LCDCON5_INVVFRAME |                              S3C2410_LCDCON5_PWREN,        .width          = LCD_WIDTH,        .height         = LCD_HEIGHT,        .xres           = LCD_WIDTH,        .yres           = LCD_HEIGHT,        .bpp            = 16,        .left_margin    = LCD_LEFT_MARGIN + 1,        .right_margin   = LCD_RIGHT_MARGIN + 1,        .hsync_len      = LCD_HSYNC_LEN + 1,        .upper_margin   = LCD_UPPER_MARGIN + 1,        .lower_margin   = LCD_LOWER_MARGIN + 1,        .vsync_len      = LCD_VSYNC_LEN + 1,        .pixclock       = LCD_PIXCLOCK,        .type           = S3C2410_LCDCON1_TFT,};

static struct s3c2410fb_mach_info s3c2410_lcd_info __initdata = {        .displays       = &skyeye2410_lcd_cfg,        .num_displays   = 1,        .default_display = 0,        .gpdcon =       0xaa95aaa1,        .gpdcon_mask =  0xffc0fff0,        .gpdup =        0x0000faff,        .gpdup_mask =   0xffffffff,        .lpcsel         = 0xf82,};

static void __init skyeye2410_lcd_init(void){    s3c24xx_fb_set_platdata(&s3c2410_lcd_info);}

#define UCON S3C2410_UCON_DEFAULT#define ULCON S3C2410_LCON_CS8 | S3C2410_LCON_PNONE | S3C2410_LCON_STOPB#define UFCON S3C2410_UFCON_RXTRIG8 | S3C2410_UFCON_FIFOMODE

static struct s3c2410_uartcfg skyeye2410_uartcfgs[] __initdata = {        [0] = {                .hwport      = 0,                .flags       = 0,                .ucon        = UCON,                .ulcon       = ULCON,                .ufcon       = UFCON,        },        [1] = {                .hwport      = 1,                .flags       = 0,                .ucon        = UCON,                .ulcon       = ULCON,                .ufcon       = UFCON,        },        [2] = {                .hwport      = 2,                .flags       = 0,                .ucon        = UCON,                .ulcon       = ULCON,                .ufcon       = UFCON,        }};

static struct platform_device *skyeye2410_devices[] __initdata = {        /*&s3c_device_usb,*/        &s3c_device_lcd,        &s3c_device_wdt,        /*&s3c_device_i2c0,*/        /*&s3c_device_iis,*/};

static void __init skyeye2410_map_io(void){        s3c24xx_init_io(skyeye2410_iodesc, ARRAY_SIZE(skyeye2410_iodesc));        s3c24xx_init_clocks(0);        s3c24xx_init_uarts(skyeye2410_uartcfgs, ARRAY_SIZE(skyeye2410_uartcfgs));}

static void __init skyeye2410_init(void){        s3c_i2c0_set_platdata(NULL);        platform_add_devices(skyeye2410_devices, ARRAY_SIZE(skyeye2410_devices));        skyeye2410_lcd_init();        smdk_machine_init();}

MACHINE_START(SMDK2410, "skyeye2410") /* @TODO: request a new identifier and switch                                    * to skyeye2410 */        /* Maintainer: Barry Song */        .phys_io        = S3C2410_PA_UART,        .io_pg_offst    = (((u32)S3C24XX_VA_UART) >> 18) & 0xfffc,        .boot_params    = S3C2410_SDRAM_PA + 0x100,        .map_io         = skyeye2410_map_io,        .init_irq       = s3c24xx_init_irq,        .init_machine   = skyeye2410_init,        .timer          = &s3c24xx_timer,MACHINE_END

配置内核并编译

配置内核,选择skyeye2410板:

System Type  --->         S3C2410 Machines  --->                  [*] skyeye 2410 board

编译内核。得到的vmlinux可以直接在skyeye运行:

bhsong@bhsong-laptop:~/develop/training/skyeye/training-simulation-new-board$ sudo skyeye -e vmlinux -c skyeye-standalone.conf big_endian is false.arch: armcpu info: armv4, arm920t, 41009200, ff00fff0, 2 mach info: name s3c2410x, mach_init addr 0x805f030ethmod num=1, mac addr=0:4:3:2:1:f, hostip=10.0.0.1lcd_mod:1uart_mod:0, desc_in:, desc_out:, converter:SKYEYE: use arm920t mmu opsLoaded RAM   ./initrd.imgstart addr is set to 0xc0008000 by exec file.Linux version 2.6.31.6-svn79 (bhsong@bhsong-laptop) (gcc version 4.2.2) #34 Sat Jan 16 18:38:55 CST 2010CPU: ARM920T [41009200] revision 0 (ARMvundefined/unknown), cr=00003177CPU: VIVT data cache, VIVT instruction cacheMachine: skyeye2410...

另外,我们也可以为skyeye2410电路板分配一个新的machine ID,这样就不再需要在MACHINE_START(SMDK2410, "skyeye2410")等处使用smdk2410的ID了。

2010年初对思科实施嵌入式Linux开发培训,感谢思科员工的支持。



第一天

_________________________________________________________________

课程一: 动手构建一个嵌入式Linux系统

_________________________________________________________________

  1. 进入嵌入式Linux的精彩世界

1.1 Linux的特点、版本演进与发展方向

1.2 嵌入式Linux系统组成

1.3 开发环境构建

1.3.1 安装设置ARM工具链

1.3.2 安装设置NFS文件系统

1.3.3 安装设置SSH服务

1.3.3 安装设置TFTP服务

1.4 GNU工具链和GDB调试

1.4.1 常用GNU工具的使用

1.4.2 使用gdb调试器

  1. 构建Linux Bootloader

2.1 编译和运行U-Boot

2.2 U-Boot移植

  1. 内核编译与配置

3.1 内核配置KconfigMakefile

3.2 内核编译

3.2 在内核中新增和修改程序

  1. 构建文件系统

4.1 文件系统组成

4.2 Busybox编译与配置

4.3 加入C

4.4 加入应用程序

4.4.1 编写一个应用程序

4.4.2 使用NFS服务

4.4.3 使用TFTP服务

第二天

5章 让系统映像更小

5.1 内核裁剪

5.2 文件系统裁剪

5.2.1 C库裁剪

5.2.2 应用程序裁剪

_________________________________________________________________

课程二: Linux内核和内核开发技术(驱动开发必备的内核知识)

_________________________________________________________________

6章 成为一个Linux内核开发者

6.1 patch和邮件列表

6.2官方源代码演进

6.3 商业发行版版本维护

6.4 源代码与项目管理工具

6.5 内核代码检查和check-in

7Linux内核工作机理

7.1 内核总体结构

7.2 进程管理

7.3 内存管理

7.4 文件系统

7.5 系统调用

8Linux内核编程

8.1 内核编程主要API

8.1.1 同步与并发

8.1.2 等待队列

8.1.3 内核延时

8.1.4 内存申请和释放

8.2 定时器

8.3 中断顶底半部

8.3.1 软中断

8.3.2 tasklet

8.3.3 工作队列

8.3.4 threaded_irq

8.4 Linux内核线程

8.5 IO与内存访问

8.6 DMAcache一致性

9Linux内核模块开发

9.1 内核模块组成

9.2 编写一个内核模块

9.3 加载和卸载内核模块

9.4 内核模块进入文件系统

第三天

10Linux内核调试方法

10.1 printk

10.2 oopspanic

10.3 内核debug选项

10.4 gdbkgdbkdb

10.5 使用仿真器

_________________________________________________________________

课程三: Linux设备驱动开发(驱动原理、软件架构与设备实例)

_________________________________________________________________

11Linux字符设备驱动结构

11.1 Linux字符设备驱动结构

11.2 globalmem虚拟设备实例描述

11.3 globalmem设备驱动

11.3.1加载与卸载设备驱动

11.3.2打开与释放函数

11.3.3读写函数

11.3.4 seek函数

11.3.5 ioctl函数

11.4 globalmem驱动的编译与加载

11.5 globalmem驱动在用户空间的验证

12Linux设备驱动中的并发控制

12.1并发控制的概念

12.2何时需要并发控制

12.3自旋锁

12.3.1自旋锁的概念

12.3.2自旋锁相关数据结构与函数

12.4信号量

12.4.1信号量的概念

12.4.2信号量与同步

12.4.3信号量与互斥

12.4.4信号量相关数据结构与函数

12.5增加并发控制后的globalmem驱动

12.5.1globalmem驱动中增加并发控制

12.5.2在用户空间验证globalmem设备的并发控制

13Linux设备驱动中的阻塞与非阻塞I/O

13.1阻塞与非阻塞I/O

13.2 poll操作的概念

13.3何时需要poll操作

13.4等待队列waitqueues

13.5 Linux poll操作相关数据结构与函数

13.5.1应用程序中的poll相关数据结构与函数

13.5.2设备驱动中的poll相关数据结构与函数

13.6支持poll操作的globalmem驱动

13.6.1globalmem驱动中增加poll操作

13.6.2在用户空间验证globalmem设备的poll

14Linux设备驱动中的异步通知

14.1异步通知的概念

14.2何时需要异步通知

14.3 Linux异步通知相关数据结构与函数

14.4支持异步通知的globalmem驱动

14.4.1globalmem驱动中增加异步通知

14.4.2在用户空间验证globalmem的异步通知

15章 硬件和综合因素:中断、IO访问、驱动分层结构

15.1 以触摸屏驱动为例剖析IO访问

15.2 以触摸屏驱动为例剖析中断的使用

15.3 以音频驱动为例剖析剖析DMA的使用

15.4 范举多个驱动子系统剖析Linux驱动的框架结构

第四天

16Linux块设备驱动

16.1块设备的I/O操作特点

16.2 Linux块设备驱动结构

16.3块设备驱动注册与注销

16.4块设备的打开与释放

16.5块设备驱动的ioctl函数

16.6块设备I/O请求队列

16.6.1请求队列的概念

16.6.2请求队列的相关数据结构与函数

16.6.3请求队列的保护机制

16.7块设备的读写流程

16.8实例:硬盘设备驱动

17Linux Ethernet设备驱动

17.1 Linux网络设备驱动结构

17.2网络设备驱动注册与注销

17.3网络设备初始化

17.4网络设备打开与释放

17.5数据发送流程

17.6数据接收流程

17.7网络连接状态

17.8参数设置和统计数据

17.9实例:CS8900网卡设备驱动

18Linux PCI设备驱动

18.1 PCII/O空间、存储空间和配置空间

18.2 Linux PCI设备驱动架构

18.3 PCI设备驱动注册与注销

18.4 PCI设备初始化

18.5 PCI设备的probe

18.6 PCI设备打开与释放

18.7 PCI设备数据读写与控制

18.8 PCI设备驱动中断处理

18.8实例:NE2000 PCI网卡驱动

_________________________________________________________________

课程四: Linux内核移植(BSP构建)

_________________________________________________________________

19Linux BSP构建

19.1 BSP的组成部分

19.2 plat/mach各组件的实现

19.2.1 内核节拍

19.2.2中断管理

19.2.3 时钟

19.2.4 GPIO

19.2.5 DMA

19.2.6 IO内存映射

19.3设备与资源

19.3.1 platform deviceresourceplarform data

19.3.2 uart/spi/i2c等设备板级resource

四方公司系我国电力行业和北京市中关村科技园区的知名企业,2009年10月23-25日对该企业进行了18课时的企业培训,感谢四方公司工作同志的支持。
培训大纲:

第一天

1. Linux 系统开发的总述
1.1 Linux 开发环境及Linux 系统的组成
1.2 从VxWorks 过渡到Linux
1.3 MontaVista Linux 的主要工作
2. 构建嵌入式Linux 系统
2.1 工具链
2.2 内核编译与裁减
2.3 根文件系统
2.4 应用和库裁减
3. Linux 内核各组成部分介绍及内核编程基础
3.1 内核组成及各部分关系
3.2 内核编程API
3.3 内核模块编程

第二天

4. Linux 进程调度与实时性
4.1 Linux 进程调度机理
4.2 Linux 内核线程
4.3 Linux 线程模型
4.4 实时任务
4.5 硬实时Linux
5. Linux 中断和定时器
5.1 中断之悖论
5.2 顶半部与底半部
5.3 中断线程化
5.4 Linux 内核定时器
6. Linux 设备驱动结构
6.1 Linux 字符设备驱动
6.2 Linux 块设备驱动
6.3 Linux TTY 子系统与串口驱动
6.4 VxWorks 与Linux 驱动对比分析
7. Linux 网络设备驱动
7.1 Linux 网络设备驱动结构
7.2 Linux 网络设备数据流程
7.3 Linux 网络协议栈的结构

第三天

8. Linux 启动过程
8.1 u-boot
8.2 Linux 启动过程流程分析
8.3 Linux 快速启动
9. Linux 内核的移植
9.1 嵌入式设备上的BSP 开发
9.2 驱动移植和目录组织
9.3 内核程序的可移植性
10. Linux 应用编程核心
10.1 Linux 系统调用
10.2 Linux 多进程编程与进程间通信
10.3 Linux 多线程编程与线程间通信
11. Linux 调试技巧
11.1 gdb
11.2 内核与驱动调试
11.3 应用调试
11.4 内核和应用崩溃转储

BSP的组成部分
plat/mach各组件的实现
内核节拍
中断管理
时钟
GPIO
DMA
IO内存映射
设备与资源
platform device、resource和plarform data
uart/spi/i2c等设备板级resource
下载地址:
http://img1.51cto.com/attachment/200911/109393_1258256558.pdf
Linux网络设备驱动架构
Linux网络设备驱动数据流程

NON-NAPI模式数据接收流程
NAPI模式数据接收流程
数据发送流程
Linux网络协议栈的实现
TCP/UDP/IP/MAC各层数据传递
网络系统调用与socket
下载地址:
http://img1.51cto.com/attachment/200911/109393_1258256315.pdf

2009年11月14日上海讲座-Linux触摸屏驱动PPT

1.触摸屏的硬件原理
阻性触摸屏
容性触摸屏
多点触摸
2.Linux触摸屏驱动
Linux Input层
input event报告
Linux多点触摸
3.触摸屏的用户空间编程
input层接口
坐标校正
下载地址:
http://img1.51cto.com/attachment/200911/109393_1258255867.pdf
 

配置和编译内核

在linux-2.6.31下运行make menuconfig,选中内核的如下栏目:

Kernel hacking  --->         [*] Compile the kernel with debug info 

make vmlinux得到新的包含符号信息的内核。

启动skyeye为debug模式运行vmlinux

bhsong@bhsong-laptop:~/develop/training/skyeye/training-simulation-with-debconf udo skyeye -d -e vmlinux -c skyeye-standalone.cbig_endian is false.arch: armcpu info: armv4, arm920t, 41009200, ff00fff0, 2 mach info: name s3c2410x, mach_init addr 0x805f030ethmod num=1, mac addr=0:4:3:2:1:f, hostip=10.0.0.1lcd_mod:1uart_mod:0, desc_in:, desc_out:, converter:SKYEYE: use arm920t mmu opsLoaded RAM   ./initrd.imgstart addr is set to 0xc0008000 by exec file.debugmode= 1, filename = skyeye-standalone.conf, server TCP port is 12345

通过arm-linux-gdb连接localhost:12345

root@bhsong-laptop:~/develop/svn/ldd6410/linux-2.6.31# arm-linux-gdb vmlinuxGNU gdb 6.6Copyright (C) 2006 Free Software Foundation, Inc.GDB is free software, covered by the GNU General Public License, and you arewelcome to change it and/or distribute copies of it under certain conditions.Type "show copying" to see the conditions.There is absolutely no warranty for GDB.  Type "show warranty" for details.This GDB was configured as "--host=/usr/local/arm/4.2.2-eabi/usr/bin/ --target=arm-linux"...(gdb) target remote localhost:12345Remote debugging using localhost:12345warning: shared library handler failed to enable breakpointstext () at arch/arm/kernel/head.S:7979              msr     cpsr_c, #PSR_F_BIT | PSR_I_BIT | SVC_MODE @ ensure svc modeCurrent language:  auto; currently asm

使用gdb进行源代码级调试:

设置断点在start_kernel(gdb) b start_kernelBreakpoint 1 at 0xc0008914: file init/main.c, line 560.

继续运行:(gdb) cContinuing.

Program received signal SIGHUP, Hangup.start_kernel () at init/main.c:560560             smp_setup_processor_id();Current language:  auto; currently c

查看变量:(gdb) p/x jiffies$1 = 0xffff15a0

查看上下文源码:(gdb) l551             pgtable_cache_init();552             vmalloc_init();553     }554     555     asmlinkage void __init start_kernel(void)556     {557             char * command_line;558             extern struct kernel_param __start___param[], __stop___param[];559     560             smp_setup_processor_id();(gdb) 

调试内核模块

在目标机上加载globalfifo模块并创建设备结点:

# modprobe globalfifo globalfifo_major=252# mknod /dev/globalfifo c 252 0# 

在 目标机上获得globalfifo运行时地址信息: # cd /sys/module/globalfifo/sections/ # cat .bss 0xc1c07160 # cat .data 0xc1c07024 # cat .text 0xc1c06000 在主机调试器上添加globalfifo的符号,添加符号时所用section地址皆来源于目标机:

(gdb) add-symbol-file drivers/char/driver_examples/globalfifo.ko 0xc1c06000 -s .bss 0xc1c07160 -s .data 0xc1c06000add symbol table from file "drivers/char/driver_examples/globalfifo.ko" at        .text_addr = 0xc1c06000        .bss_addr = 0xc1c07160        .data_addr = 0xc1c06000(y or n) yReading symbols from /home/bhsong/develop/svn/ldd6410/linux-2.6.31/drivers/char/driver_examples/globalfifo.ko...done.(gdb) 

调试模块: 在主机调试器上设置在globalfifo_read处断点:

(gdb) b globalfifo_readBreakpoint 1 at 0xc1c06444: file drivers/char/driver_examples/globalfifo.c, line 104.

目标机读取/dev/globalfifo:

# cat /dev/globalfifo

调试主机上进入了断点:

Program received signal SIGHUP, Hangup.globalfifo_read (filp=0xc1c2f2a0, buf=0xbea5bac8 "", count=4096, ppos=0xc182bf88)    at drivers/char/driver_examples/globalfifo.c:104104       struct globalfifo_dev *dev = filp->private_data; Current language:  auto; currently c(gdb) 

进行调试:

(gdb) l99      /* globalfifo read */100     static ssize_t globalfifo_read(struct file *filp, char __user *buf, size_t count,101       loff_t *ppos)102     {103       int ret;104       struct globalfifo_dev *dev = filp->private_data; 105       DECLARE_WAITQUEUE(wait, current); 106     107       down(&dev->sem); 108       add_wait_queue(&dev->r_wait, &wait); (gdb) n

Program received signal SIGHUP, Hangup.globalfifo_read (filp=0xc1c2f2a0, buf=0xbea5bac8 "", count=4096, ppos=0xc182bf88)    at drivers/char/driver_examples/globalfifo.c:105105       DECLARE_WAITQUEUE(wait, current); (gdb) nCan't send signals to this remote system.  SIGHUP not sent.

Program received signal SIGHUP, Hangup.globalfifo_read (filp=0xc1c2f2a0, buf=0xbea5bac8 "", count=4096, ppos=0xc182bf88)    at /home/bhsong/develop/svn/ldd6410/linux-2.6.31/arch/arm/include/asm/thread_info.h:9797              return (struct thread_info *)(sp & ~(THREAD_SIZE - 1));(gdb) p *dev$1 = {cdev = {kobj = {name = 0x0, entry = {next = 0xc0618004, prev = 0xc0618004}, parent = 0x0, kset = 0x0,       ktype = 0xc029f5ec, sd = 0x0, kref = {refcount = {counter = 2}}, state_initialized = 1, state_in_sysfs = 0,       state_add_uevent_sent = 0, state_remove_uevent_sent = 0, uevent_suppress = 0}, owner = 0xc1c07028, ops = 0xc1c065f0,     list = {next = 0xc1c7532c, prev = 0xc1c7532c}, dev = 264241152, count = 1}, current_len = 0,   mem = '\0' <repeats 4095 times>, sem = {lock = {raw_lock = {<No data fields>}}, count = 1, wait_list = {next = 0xc0619044,       prev = 0xc0619044}}, r_wait = {lock = {raw_lock = {<No data fields>}}, task_list = {next = 0xc061904c,       prev = 0xc061904c}}, w_wait = {lock = {raw_lock = {<No data fields>}}, task_list = {next = 0xc0619054,       prev = 0xc0619054}}}(gdb) 

本文中所有相关内核及实例源代码可以通过svn获取:

svn checkout http://ldd6410.googlecode.com/svn/trunk/ ldd6410-read-only

已经做好的,可直接进行内核和内核模块调试实验的包位于: http://ldd6410.googlecode.com/files/ldd6410-skyeye-pack-with-debug.tar.gz

添加代码、Kconfig和Makefile

在linux-2.6.31/drivers/char目录下建立子目录:

bhsong@bhsong-laptop:~/develop/svn/ldd6410/linux-2.6.31/drivers/char$ mkdir driver_examples

将三个驱动hello.c、globalmem.c、globalfifo.c拷入driver_examples目录:

cd driver_examples/cp ../../../../training/kernel/drivers/hello/hello.c ./cp ../../../../training/kernel/drivers/globalmem/globalmem.c ./cp ../../../../training/kernel/drivers/globalmem/globalfifo.c ./

修改drivers/char下面的Kconfig和Makefile导入driver_examples目录:

这将形成如下菜单:

 driver examples in 'Explain Linux Device Drivers in detail' ─────────────────────────────┐    Arrow keys navigate the menu.  <Enter> selects submenus --->.  Highlighted letters are hotkeys.  Pressing <Y>              includes, <N> excludes, <M> modularizes features.  Press <Esc><Esc> to exit, <?> for Help, </> for Search.  Legend:        [*] built-in  [ ] excluded  <M> module  < > module capable                                                                                                                                                                                           ┌───────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐                            --- driver examples in 'Explain Linux Device Drivers in detail'                                                          < >   Hello World (NEW)                                                                                                  < >   globalmem (NEW)                                                                                                    < >   globalfifo (NEW)                                                                                                                                                                                                                                                                                                                                                                                       

相关的Kconfig和Makefile patch如下:

Index: drivers/char/Kconfig===================================================================--- drivers/char/Kconfig        (revision 87)+++ drivers/char/Kconfig        (working copy)Index: drivers/char/driver_examples/Makefile===================================================================--- drivers/char/driver_examples/Makefile       (revision 0)+++ drivers/char/driver_examples/Makefile       (revision 0)@@ -0,0 +1,3 @@+obj-$(CONFIG_HELLO_WORLD)              += hello.o+obj-$(CONFIG_GLOBALMEM)                += globalmem.o+obj-$(CONFIG_GLOBALFIFO)               += globalfifo.o@@ -1110,5 +1110,7 @@  source "drivers/s390/char/Kconfig" +source "drivers/char/driver_examples/Kconfig"+ endmenu Index: drivers/char/Makefile===================================================================--- drivers/char/Makefile       (revision 87)+++ drivers/char/Makefile       (working copy)@@ -111,6 +111,8 @@ obj-$(CONFIG_JS_RTC)           += js-rtc.o js-rtc-y = rtc.+obj-$(CONFIG_DRIVER_EXAMPLE)           += driver_examples/+ # Files generated that shall be removed upon make clean clean-files := consolemap_deftbl.c defkeymap.Index: drivers/char/driver_examples/Kconfig===================================================================--- drivers/char/driver_examples/Kconfig        (revision 0)+++ drivers/char/driver_examples/Kconfig        (revision 0)@@ -0,0 +1,31 @@+#+# driver examples configuration+#++menuconfig DRIVER_EXAMPLE+       tristate "driver examples in 'Explain Linux Device Drivers in detail'"+       ---help---+         say Yes to build-in hello world, globalmem, globalfifo, say M to get+         those kernel modules++if DRIVER_EXAMPLE++config HELLO_WORLD+       tristate "Hello World"+       ---help---+         To compile this driver as a module, choose M here; the module will be+         called hello.++config GLOBALMEM+       tristate "globalmem"+       ---help---+         To  compile this driver as a module, choose M here; the module will be+         called globalmem.++config GLOBALFIFO+       tristate "globalfifo"+       ---help---+         To  compile this driver as a module, choose M here; the module will be+         called globalfifo.++endif # DRIVER_EXAMPLE+Index: drivers/char/Kconfig===================================================================--- drivers/char/Kconfig        (revision 87)+++ drivers/char/Kconfig        (working copy)@@ -1110,5 +1110,7 @@  source "drivers/s390/char/Kconfig" +source "drivers/char/driver_examples/Kconfig"+ endmenu Index: drivers/char/Makefile===================================================================--- drivers/char/Makefile       (revision 87)+++ drivers/char/Makefile       (working copy)@@ -111,6 +111,8 @@ obj-$(CONFIG_JS_RTC)           += js-rtc.o js-rtc-y = rtc.+obj-$(CONFIG_DRIVER_EXAMPLE)           += driver_examples/+ # Files generated that shall be removed upon make clean clean-files := consolemap_deftbl.c defkeymap.Index: drivers/char/driver_examples/Kconfig===================================================================--- drivers/char/driver_examples/Kconfig        (revision 0)+++ drivers/char/driver_examples/Kconfig        (revision 0)@@ -0,0 +1,31 @@+#+# driver examples configuration+#++menuconfig DRIVER_EXAMPLE+       tristate "driver examples in 'Explain Linux Device Drivers in detail'"+       ---help---+         say Yes to build-in hello world, globalmem, globalfifo, say M to get+         those kernel modules++if DRIVER_EXAMPLE++config HELLO_WORLD+       tristate "Hello World"+       ---help---+         To compile this driver as a module, choose M here; the module will be+         called hello.++config GLOBALMEM+       tristate "globalmem"+       ---help---+         To  compile this driver as a module, choose M here; the module will be+         called globalmem.++config GLOBALFIFO+       tristate "globalfifo"+       ---help---+         To  compile this driver as a module, choose M here; the module will be+         called globalfifo.++endif # DRIVER_EXAMPLEIndex: drivers/char/driver_examples/Makefile===================================================================--- drivers/char/driver_examples/Makefile       (revision 0)+++ drivers/char/driver_examples/Makefile       (revision 0)@@ -0,0 +1,3 @@+obj-$(CONFIG_HELLO_WORLD)              += hello.o+obj-$(CONFIG_GLOBALMEM)                += globalmem.o+obj-$(CONFIG_GLOBALFIFO)               += globalfifo.o

编译和安装

选中三个模块:

─── driver examples in 'Explain Linux Device Drivers in detail' ─────────────────────────────┐    Arrow keys navigate the menu.  <Enter> selects submenus --->.  Highlighted letters are hotkeys.  Pressing <Y>              includes, <N> excludes, <M> modularizes features.  Press <Esc><Esc> to exit, <?> for Help, </> for Search.  Legend:        [*] built-in  [ ] excluded  <M> module  < > module capable                                                                                                                                                                                           ┌───────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐                            --- driver examples in 'Explain Linux Device Drivers in detail'                                                          <M>   Hello World                                                                                                        <M>   globalmem                                                                                                          <M>   globalfifo                                                                                                                                                                                                        

在linux-2.6.31下运行:

bhsong@bhsong-laptop:~/develop/svn/ldd6410/linux-2.6.31$ make modules

以loopback方式mount根文件系统,譬如对于skyeye的initrd.img运行如下命令:

...skyeye/training-simulation$ sudo mount -o loop initrd.img initrd

将所有模块安装到根文件系统:

make INSTALL_MOD_PATH=/home/bhsong/develop/training/skyeye/training-simulation/initrd modules_install

请根据你的路径,正确的设置INSTALL_MOD_PATH。安装之后,所有模块以及模块间依赖关系进入根文件系统:

initrd/lib/modules$ tree.`-- 2.6.31.6-svn79    |-- build -> /home/bhsong/develop/svn/ldd6410/linux-2.6.31    |-- kernel    |   |-- drivers    |   |   |-- cdrom    |   |   |   `-- cdrom.ko    |   |   |-- char    |   |   |   `-- driver_examples    |   |   |       |-- globalfifo.ko    |   |   |       |-- globalmem.ko    |   |   |       `-- hello.ko    |   |   |-- input    |   |   |   `-- gameport    |   |   |       `-- gameport.ko    |   |   `-- scsi    |   |       |-- ch.ko    |   |       |-- scsi_wait_scan.ko    |   |       |-- sr_mod.ko    |   |       `-- st.ko    |   |-- lib    |   |   |-- ts_bm.ko    |   |   |-- ts_fsm.ko    |   |   `-- ts_kmp.ko    |   `-- net    |       |-- ipv4    |       |   |-- netfilter    |       |   |   |-- arp_tables.ko    |       |   |   |-- arpt_mangle.ko    |       |   |   |-- arptable_filter.ko    |       |   |   |-- ip_queue.ko    |       |   |   |-- ip_tables.ko    |       |   |   |-- ipt_ECN.ko    |       |   |   |-- ipt_LOG.ko    |       |   |   |-- ipt_MASQUERADE.ko    |       |   |   |-- ipt_NETMAP.ko    |       |   |   |-- ipt_REDIRECT.ko    |       |   |   |-- ipt_REJECT.ko    |       |   |   |-- ipt_ULOG.ko    |       |   |   |-- ipt_addrtype.ko    |       |   |   |-- ipt_ah.ko    |       |   |   |-- ipt_ecn.ko    |       |   |   |-- iptable_filter.ko    |       |   |   |-- iptable_mangle.ko    |       |   |   |-- iptable_nat.ko    |       |   |   |-- iptable_raw.ko    |       |   |   |-- nf_conntrack_ipv4.ko    |       |   |   |-- nf_defrag_ipv4.ko    |       |   |   |-- nf_nat.ko    |       |   |   |-- nf_nat_amanda.ko    |       |   |   |-- nf_nat_ftp.ko    |       |   |   |-- nf_nat_h323.ko    |       |   |   |-- nf_nat_irc.ko    |       |   |   |-- nf_nat_pptp.ko    |       |   |   |-- nf_nat_proto_gre.ko    |       |   |   |-- nf_nat_proto_udplite.ko    |       |   |   |-- nf_nat_sip.ko    |       |   |   |-- nf_nat_snmp_basic.ko    |       |   |   `-- nf_nat_tftp.ko    |       |   |-- tcp_bic.ko    |       |   |-- tcp_htcp.ko    |       |   `-- tcp_westwood.ko    |       `-- netfilter    |           |-- ipvs    |           |   `-- ip_vs.ko    |           |-- nf_conntrack.ko    |           |-- nf_conntrack_amanda.ko    |           |-- nf_conntrack_ftp.ko    |           |-- nf_conntrack_h323.ko    |           |-- nf_conntrack_irc.ko    |           |-- nf_conntrack_netbios_ns.ko    |           |-- nf_conntrack_netlink.ko    |           |-- nf_conntrack_pptp.ko    |           |-- nf_conntrack_proto_gre.ko    |           |-- nf_conntrack_proto_udplite.ko    |           |-- nf_conntrack_sip.ko    |           |-- nf_conntrack_tftp.ko    |           |-- nfnetlink.ko    |           |-- nfnetlink_log.ko    |           |-- nfnetlink_queue.ko    |           |-- x_tables.ko    |           |-- xt_CLASSIFY.ko    |           |-- xt_CONNMARK.ko    |           |-- xt_HL.ko    |           |-- xt_MARK.ko    |           |-- xt_NFLOG.ko    |           |-- xt_NFQUEUE.ko    |           |-- xt_RATEEST.ko    |           |-- xt_TCPMSS.ko    |           |-- xt_cluster.ko    |           |-- xt_comment.ko    |           |-- xt_connbytes.ko    |           |-- xt_connlimit.ko    |           |-- xt_connmark.ko    |           |-- xt_conntrack.ko    |           |-- xt_dccp.ko    |           |-- xt_dscp.ko    |           |-- xt_esp.ko    |           |-- xt_hashlimit.ko    |           |-- xt_helper.ko    |           |-- xt_hl.ko    |           |-- xt_iprange.ko    |           |-- xt_length.ko    |           |-- xt_limit.ko    |           |-- xt_mac.ko    |           |-- xt_mark.ko    |           |-- xt_multiport.ko    |           |-- xt_owner.ko    |           |-- xt_pkttype.ko    |           |-- xt_policy.ko    |           |-- xt_quota.ko    |           |-- xt_rateest.ko    |           |-- xt_realm.ko    |           |-- xt_recent.ko    |           |-- xt_state.ko    |           |-- xt_statistic.ko    |           |-- xt_string.ko    |           |-- xt_tcpmss.ko    |           |-- xt_tcpudp.ko    |           |-- xt_time.ko    |           `-- xt_u32.ko    |-- modules.alias    |-- modules.ccwmap    |-- modules.dep    |-- modules.ieee1394map    |-- modules.inputmap    |-- modules.isapnpmap    |-- modules.ofmap    |-- modules.order    |-- modules.pcimap    |-- modules.seriomap    |-- modules.symbols    |-- modules.usbmap    `-- source -> /home/bhsong/develop/svn/ldd6410/linux-2.6.31

17 directories, 116 files

运行

启动Linux,对于skyeye为:

sudo skyeye -e vmlinux -c skyeye-standalone.conf...Welcome to     _      _____      __   __    _      _    / \    /  __ \    /  \_/  \  | |    |_|                    / _ \   | |  | |  / /\   /\ \ | |     _ ____  _   _  _  _   / /_\ \  | |__| | / /  \_/  \ \| |    | |  _ \| | | |\ \/ / / /___\ \ | |__\ \ | |       | || |___ | | |_| | |_| |/    \/_/     \_\| |   \_\|_|       |_||_____||_|_| |_|\____|\_/\_/           ARMLinux for Lihacker LDD6410

For further information please check:http://www.lihacker.com/http://www.linuxdriver.cn/For technical support, please subscribe mail list linuxdriver@googlegroups.com and post there.The url is http://groups.google.com/group/linuxdriver.# 

加载模块:

modprobe hellomodprobe globalmem  globalmem_major=250modprobe globalfifo globalfifo_major=251

skyeye对insmod的支持有些问题,加载模块时会出现:

# modprobe helloUnable to handle kernel paging request at virtual address bf000000pgd = c05f0000[bf000000] *pgd=00000000Internal error: Oops: 807 [#1]Modules linked in:CPU: 0    Not tainted  (2.6.31.6-svn79 #21)PC is at __memzero+0x24/0x80LR is at 0x0pc : [<c0107404>]    lr : [<00000000>]    psr: 20000013sp : c182bf0c  ip : 00000000  fp : 00000005r10: c2807230  r9 : c2807000  r8 : c2807388r7 : 00000004  r6 : c2807168  r5 : 00000012  r4 : bf000000r3 : 00000000  r2 : 00000000  r1 : 00000464  r0 : bf000000Flags: nzCv  IRQs on  FIQs on  Mode SVC_32  ISA ARM  Segment userControl: 0000317f  Table: c05f0000  DAC: 00000015Process modprobe (pid: 795, stack limit = 0xc182a270)Stack: (0xc182bf0c to 0xc182c000)bf00:                            c005fba0 00000979 00000000 c2807518 c2807608 bf20: c28072dc c1139e60 c28078a0 00000010 000000c5 c2807388 c182a000 c2807630 bf40: 00000000 c008c178 00000171 00000000 0015ec79 00000287 00000001 00000008 bf60: 00000000 00000979 0015e2f0 00000000 0015e300 c002bf48 c182a000 00000000 bf80: 00000000 c0060878 c1c1a2c0 c1c2f3a0 00000003 00000979 0015e2f0 0015dfa8 bfa0: 00000080 c002bda0 00000979 0015e2f0 0015e300 00000979 0015e2f0 00000000 bfc0: 00000979 0015e2f0 0015dfa8 00000080 0015e2a0 0015dfbc 00000001 00000000 bfe0: bea65958 bea65948 000177f8 00009350 20000010 0015e300 ab081000 11000006 [<c0107404>] (__memzero+0x24/0x80) from [<00000000>] (0x0)Code: e52de004 e1a0c002 e1a0e002 e2511040 (a8a0500c) ---[ end trace 1e19dcbb1e19dcba ]---Segmentation fault

详见bug: http://sourceforge.net/tracker/?func=detail&aid=2897905&group_id=85554&atid=576533 我们使用strace去寻找死在哪个系统调用:

# strace modprobe helloexecve("/sbin/modprobe", ["modprobe", "hello"], [/* 7 vars */]) = 0uname({sys="Linux", node="lihacker", ...}) = 0brk(0)                                  = 0x15d000brk(0x15dd02)                           = 0x15dd02set_tls(0x15d4a0, 0x1573c0, 0, 0x1, 0x15d4a0) = 0brk(0x17ed02)                           = 0x17ed02brk(0x17f000)                           = 0x17f000getuid32()                              = 0chdir("/lib/modules")                   = 0uname({sys="Linux", node="lihacker", ...}) = 0chdir("2.6.31.6-svn79")                 = 0open("/proc/modules", O_RDONLY)         = 3fstat64(3, {st_mode=S_IFREG|0444, st_size=0, ...}) = 0mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x40000000read(3, ""..., 1024)                    = 0close(3)                                = 0munmap(0x40000000, 4096)                = 0lstat64("/etc/modprobe.conf", 0xbe846880) = -1 ENOENT (No such file or directory)lstat64("/etc/modprobe.d", 0xbe846880)  = -1 ENOENT (No such file or directory)open("modules.dep", O_RDONLY)           = 3fstat64(3, {st_mode=S_IFREG|0644, st_size=17141, ...}) = 0mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x40001000read(3, "/lib/modules/2.6.31.6-svn79/kerne"..., 1024) = 1024read(3, "nel/net/netfilter/x_tables.ko\n/li"..., 1024) = 1024read(3, "/lib/modules/2.6.31.6-svn79/kerne"..., 1024) = 1024read(3, "/net/netfilter/xt_esp.ko: /lib/mo"..., 1024) = 1024read(3, "ables.ko /lib/modules/2.6.31.6-sv"..., 1024) = 1024read(3, "es/2.6.31.6-svn79/kernel/net/netf"..., 1024) = 1024read(3, ".6-svn79/kernel/net/netfilter/nf_"..., 1024) = 1024read(3, "b/modules/2.6.31.6-svn79/kernel/n"..., 1024) = 1024read(3, "etfilter/nf_conntrack_ipv4.ko /li"..., 1024) = 1024read(3, ".6.31.6-svn79/kernel/net/netfilte"..., 1024) = 1024read(3, "defrag_ipv4.ko\n/lib/modules/2.6.3"..., 1024) = 1024read(3, "tfilter/nf_conntrack_ipv4.ko /lib"..., 1024) = 1024read(3, ".6-svn79/kernel/net/ipv4/netfilte"..., 1024) = 1024read(3, "1.6-svn79/kernel/net/netfilter/x_"..., 1024) = 1024read(3, "ib/modules/2.6.31.6-svn79/kernel/"..., 1024) = 1024read(3, "ib/modules/2.6.31.6-svn79/kernel/"..., 1024) = 1024read(3, "o:\n/lib/modules/2.6.31.6-svn79/ke"..., 1024) = 757close(3)                                = 0munmap(0x40001000, 4096)                = 0open("/lib/modules/2.6.31.6-svn79/kernel/drivers/char/driver_examples/hello.ko", O_RDONLY) = 3fstat64(3, {st_mode=S_IFREG|0644, st_size=2425, ...}) = 0read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\1\0(\0\1\0\0\0\0\0\0\0\0\0\0\0\210"..., 3072) = 2425read(3, ""..., 647)                     = 0close(3)                                = 0init_module(0x15e300, 2425, ""Unable to handle kernel paging request at virtual address bf000000pgd = c0620000[bf000000] *pgd=00000000Internal error: Oops: 807 [#1]Modules linked in:CPU: 0    Not tainted  (2.6.31.6-svn79 #21)PC is at __memzero+0x24/0x80LR is at 0x0pc : [<c0107404>]    lr : [<00000000>]    psr: 20000013sp : c069ff0c  ip : 00000000  fp : 00000005r10: c2807230  r9 : c2807000  r8 : c2807388r7 : 00000004  r6 : c2807168  r5 : 00000012  r4 : bf000000r3 : 00000000  r2 : 00000000  r1 : 00000464  r0 : bf000000Flags: nzCv  IRQs on  FIQs on  Mode SVC_32  ISA ARM  Segment userControl: 0000317f  Table: c0620000  DAC: 00000015Process modprobe (pid: 796, stack limit = 0xc069e270)Stack: (0xc069ff0c to 0xc06a0000)ff00:                            c005fba0 00000000 00000000 c2807518 c2807608 ff20: c28072dc c1139240 c28078a0 00000010 00000000 c2807388 00000000 c2807630 ff40: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 ff60: 00000000 00000979 0015e2f0 00000000 0015e300 c002bf48 c069e000 00000000 ff80: 00000000 c0060878 be846958 c002dbe0 00000000 00000979 0015e2f0 0015dfa8 ffa0: 00000080 c002bf0c 00000979 0015e2f0 0015e300 00000979 0015e2f0 00000000 ffc0: 00000979 0015e2f0 0015dfa8 00000080 0015e2a0 0015dfbc 00000001 00000000 ffe0: be846958 be846948 000177f8 00009350 20000010 0015e300 ebff1fc9 e3500000 [<c0107404>] (__memzero+0x24/0x80) from [<00000000>] (0x0)Code: e52de004 e1a0c002 e1a0e002 e2511040 (a8a0500c) ---[ end trace 1e19dcbb1e19dcba ]--- <unfinished ...>+++ killed by SIGSEGV +++

很明显,死在内核的linux-2.6.31/kernel/module.c的init_module()函数中,加上几个printk:

2453 SYSCALL_DEFINE3(init_module, void __user *, umod,2454         unsigned long, len, const char __user *, uargs)2455 {2456     struct module *mod;2457     int ret = 0;2458 2459     printk("%s %d\n", __func__, __LINE__);2460 2461     /* Must have permission */2462     if (!capable(CAP_SYS_MODULE) || modules_disabled)2463         return -EPERM;2464 2465     +printk("%s %d\n", __func__, __LINE__);2466     2467     /* Only one module load at a time, please */2468     if (mutex_lock_interruptible(&module_mutex) != 0)2469         return -EINTR;2470 2471     +printk("%s %d\n", __func__, __LINE__);2472     2473     /* Do all the hard work */2474     mod = load_module(umod, len, uargs);2475     if (IS_ERR(mod)) {2476         mutex_unlock(&module_mutex);2477         return PTR_ERR(mod);2478     }2479 2480     +printk("%s %d\n", __func__, __LINE__);2481     

再次运行:

# modprobe hellosys_init_module 2459sys_init_module 2465sys_init_module 2471Unable to handle kernel paging request at virtual address bf000000pgd = c05f0000[bf000000] *pgd=00000000Internal error: Oops: 807 [#1]Modules linked in:

从打印信息可以看出,panic发生在load_module函数,既然"sys_init_module 2471"被打印而"sys_init_module 2480"未打印。 继续跟进load_module(),直到最后发现内核崩溃在memset(ptr, 0, mod->core_size);

static noinline struct module *load_module(void __user *umod,                   unsigned long len,                   const char __user *uargs){...     ptr = module_alloc_update_bounds(mod->core_size);...     kmemleak_not_leak(ptr);...     memset(ptr, 0, mod->core_size);

一路跟踪源代码,发现ARM的Kernel会将模块的内存申请在3G-16M到3G的区域,而skyeye在内核空间处理3G以下地址的时候,会发生错误:

"Unable to handle kernel paging request at virtual address bf000000"

因此,workaround掉这个bug:

Index: arch/arm/kernel/module.c===================================================================--- arch/arm/kernel/module.c    (revision 87)+++ arch/arm/kernel/module.c    (working copy)@@ -38,7 +38,7 @@ #ifdef CONFIG_MMU void *module_alloc(unsigned long size) {-       struct vm_struct *area;+       /*struct vm_struct *area;         size = PAGE_ALIGN(size);        if (!size)@@ -49,6 +49,9 @@                return NULL;         return __vmalloc_area(area, GFP_KERNEL, PAGE_KERNEL_EXEC);+*/++       return size == 0 ? NULL : kmalloc(size, GFP_KERNEL); } #else /* CONFIG_MMU */ void *module_alloc(unsigned long size)@@ -59,7 +62,7 @@  void module_free(struct module *module, void *region) {-       vfree(region);+       kfree(region); }  int module_frob_arch_sections(Elf_Ehdr *hdr,

由 于此workaround使用了kmalloc,如果内核模块很大,kmalloc申请的内存不够存放,则可以修改: include/linux/slab.h中的KMALLOC_SHIFT_HIGH宏定义。另外,此workaround成立的条件是mem=32M或 小于32M。 现在我们可以自由地加载模块了,看看结果:

加载模块:# modprobe helloHello World enter# modprobe globalmem  globalmem_major=250# modprobe globalfifo globalfifo_major=251

查看加载的模块:# lsmod    Not taintedglobalmem 3356 0 - Live 0xc0f66000hello 1188 0 - Live 0xc1128000globalfifo 4292 0 - Live 0xc182a000

查看设备:# cat /proc/devices Character devices:  1 mem  2 pty  3 ttyp  4 /dev/vc/0  4 tty  4 ttyS  5 /dev/tty  5 /dev/console  5 /dev/ptmx  7 vcs 10 misc 13 input 21 sg 29 fb128 ptm136 pts204 s3c2410_serial250 globalmem251 globalfifo

建立结点:# mknod /dev/globalmem c 250 0# mknod /dev/globalfifo c 251 0# 读写设备文件:# echo "hello, cisco" > /dev/globalmemwritten 13 bytes(s) from 0# cat /dev/globalmem read 4096 bytes(s) from 0hello, cisco

# cat /dev/globalfifo &# echo "hello,linux, I love you" > /dev/globalfifo written 24 bytes(s),current_len:24# read 24 bytes(s),current_len:0hello,linux, I love you

本文用到的所有代码可通过svn获取:

svn checkout http://ldd6410.googlecode.com/svn/trunk/ ldd6410-read-only

本文实验已经做好的包位于: http://ldd6410.googlecode.com/files/ldd6410-skyeye-pack-with-module.tar.gz