青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

socketref,再見!高德

https://github.com/adoggie

  C++博客 :: 首頁(yè) :: 聯(lián)系 :: 聚合  :: 管理
  246 Posts :: 4 Stories :: 312 Comments :: 0 Trackbacks

常用鏈接

留言簿(54)

我參與的團(tuán)隊(duì)

搜索

  •  

最新評(píng)論

閱讀排行榜

評(píng)論排行榜


bh處理

1.三種舊式的bottom half 處理類型

IMMEDIATE_BH:  driver注冊(cè)入tq_immediate隊(duì)列,等待調(diào)度

TQUEUE_BH:       執(zhí)行tq_timer,到系統(tǒng)tick產(chǎn)生時(shí)觸發(fā)

TIMER_BH:    直接綁定到do_timer()處理函數(shù)

 

2.mark_bh()

       將激活32個(gè)BH中的某一個(gè),觸發(fā)軟中斷來進(jìn)入被調(diào)度狀態(tài)

       此種軟中斷優(yōu)先級(jí)為 HI_SOFTIRQ

      

3.tasklet

       此方法將觸發(fā)軟中斷,使tasklet t被進(jìn)入調(diào)度狀態(tài)

       此方式觸發(fā)的軟中斷優(yōu)先級(jí)為TASKLET_SOFTIRQ

       使用方式:

              1. 聲明tasklet:

                     DECLARE_TASKLET

              2. 執(zhí)行tasklet

                     tasklet_schedule(t)

 

 

 

       init_bh(TIMER_BH, timer_bh);     定時(shí)器中斷將執(zhí)行 timer_bh任務(wù)隊(duì)列

       init_bh(TQUEUE_BH, tqueue_bh);

       init_bh(IMMEDIATE_BH, immediate_bh);

BH后半部處理類型

interrupts.h

 

enum {

       TIMER_BH = 0, /*時(shí)鐘*/ 

       TQUEUE_BH,   定時(shí)器隊(duì)列

       DIGI_BH,

       SERIAL_BH,

       RISCOM8_BH,

       SPECIALIX_BH,

       AURORA_BH,

       ESP_BH,

       SCSI_BH,

       IMMEDIATE_BH,

       CYCLADES_BH,

       CM206_BH,

       JS_BH,

       MACSERIAL_BH,

       ISICOM_BH

};

 

bonfs

 

1.bonfs 是一種簡(jiǎn)易的只讀文件系統(tǒng),在2440項(xiàng)目中,bonfs是一個(gè)可選的塊設(shè)備。

 bonfs是一種設(shè)備,他位于mtd管理層的上端,所以其可以 register_mtd_user()注冊(cè)一個(gè)mtd_user.

此刻如果已經(jīng)存在mtd設(shè)備或者mtd分區(qū)設(shè)備,在register_mtd_user()時(shí),每個(gè)mtd設(shè)備都會(huì)通過 notifier_add()通知bonfs,然后bonfs便可以進(jìn)行初始化操作。

 

vivi在進(jìn)行分區(qū)時(shí)可以將分區(qū)格式化為若干bon分區(qū)( read_partition_info() )

bonnotifier_add()處理中,調(diào)用read_partition_info()讀取分區(qū)開頭8字節(jié)是否是bon分區(qū)的MAGIC標(biāo)志

 

**bon.c代碼,發(fā)現(xiàn)bon是將整個(gè)mtd設(shè)備(也就是整個(gè)nand flash)作為操作對(duì)象,而并不是一個(gè)mtd分區(qū)一個(gè)bon分區(qū)。

       似乎有點(diǎn)好理解了,(bon設(shè)備不依賴mtd分區(qū)),vivi 在用bon分區(qū)時(shí),將分區(qū)信息寫入nand flash 頭部的512字節(jié)區(qū)域內(nèi),bon視整個(gè)flash是一個(gè)mtd設(shè)備,

       然后通過read_partition_info()bon分區(qū)信息讀出,在內(nèi)存中構(gòu)建分區(qū)信息,所以在mtdblock.c代碼中沒有使用到mtd設(shè)備的分區(qū)信息,所以也就是不能用 root =/dev/mtdblock/0方式加載.

 

 

**vivi的代碼中已經(jīng)寫死了整個(gè)nand flash的分區(qū)使用規(guī)則,如果不使用bonfs的話,就沒有必要進(jìn)行bon分區(qū),分區(qū)只是搽除數(shù)據(jù)和寫入標(biāo)志,還有其他一些信息

 

 

       bon設(shè)備支持 add_mtd_device(s3c2440_mtd) 方式添加的mtd設(shè)備

 

如何讀取和校驗(yàn)bon分區(qū)的:

 

       read_partition_info(){

              mtd->read_oob(( mtd->size - mtd->erasesize),8) erasesize開始讀8字節(jié)oob數(shù)據(jù)

              if( oob[5] == 0xff){

                     buf = mtd->read( ( mtd->size - mtd->erasesize),512)  512字節(jié),bon分區(qū)信息存儲(chǔ)在這512字節(jié)區(qū)間內(nèi),比如分區(qū)偏移,大小,標(biāo)志等等

                     if( buf[0~7]!=BON_MAGIC){

                            非法bon分區(qū)

                     }

              }

              devfs_mk_dir("bon")                   創(chuàng)建 /dev/bon

              mtd(nand flash)中讀取bon分區(qū)表信息,構(gòu)建分區(qū)數(shù)組

              devfs_register()            注冊(cè)子設(shè)備文件 /dev/bon/0,1,2,3 (子設(shè)備號(hào)根據(jù)vivi對(duì)nand flash的分區(qū))

              統(tǒng)計(jì)bad_block信息

       }

      

      

       bon_sizes[]     分區(qū)大小  ,k為計(jì)算單位

       bon_blksizes[] 分區(qū)塊大小

      

      

      

.bon分區(qū)作為啟動(dòng)root分區(qū)

       cmd : char linux_cmd[] = "noinitrd root=/dev/bon/2 init=/linuxrc console=ttyS0";

       察看 main.c root_dev_names[]列表中并不存在bon的設(shè)備類型,原因在于bon是注冊(cè)進(jìn)入devfs,fs加載之前便已經(jīng)初始化完畢了,

       所以傳遞以上內(nèi)核參數(shù)是可以找到對(duì)應(yīng)的設(shè)備的.

       root_dev_names[]定義的是固定的設(shè)備名稱和設(shè)備編號(hào),此時(shí)就需要實(shí)現(xiàn)此設(shè)備的驅(qū)動(dòng)程序必須與設(shè)備編號(hào)進(jìn)行匹配對(duì)應(yīng)了,也就是靠devfs_register(MAJOR)注冊(cè)的設(shè)備主編號(hào)

      

      

.bon分區(qū)啟動(dòng)

       start_kernel(){

              1.parse_command() 分析命令行參數(shù) root=/dev/bon/2

              2.查找啟動(dòng)設(shè)備的主設(shè)備編號(hào)

              初始化所有設(shè)備驅(qū)動(dòng)__initcall

              在設(shè)備驅(qū)動(dòng)中,bon注冊(cè)一個(gè)devfs的塊設(shè)備名稱 /dev/bon,然后注冊(cè)為mtd user ,register_mtd_user()

              當(dāng)mtd設(shè)備驅(qū)動(dòng)被初始話時(shí),啟動(dòng)nand_chip驅(qū)動(dòng)去搜索flash設(shè)備,通過一系列的寄存器的交互,發(fā)現(xiàn)了mtd設(shè)備,

              如果當(dāng)前沒有把分區(qū)開關(guān)打開的話則將整個(gè)flash當(dāng)作一個(gè)mtd設(shè)備加入mtd_table[],同時(shí)通知mtd user,也就是告知

              bon,當(dāng)前已經(jīng)有mtd設(shè)備掛入,bon得到控制權(quán)了,他就讀取mtd設(shè)備的開頭512字節(jié)的數(shù)據(jù),構(gòu)造分區(qū)表信息,和校驗(yàn)分區(qū)是否合法,

              bon將每個(gè)分區(qū)注冊(cè)進(jìn)入devfs作為子設(shè)備(/dev/bon/0)

              3.回到步驟2,由于bon沒有存在root_dev_names[]靜態(tài)表中,所以內(nèi)核不知道其major編號(hào),所以在跳過了nfs,floppy啟動(dòng)判別之后,

              內(nèi)核拿著bon名字到devfs進(jìn)行查詢此設(shè)備的主次設(shè)備編號(hào),如果找到了則讀取超級(jí)塊,然后進(jìn)行文件系統(tǒng)的識(shí)別

             

       }

devfs

devfs 文件系統(tǒng)在內(nèi)存中建立,而不是存儲(chǔ)在塊設(shè)備或者字符設(shè)備之上。

但是為了符合vfs文件系統(tǒng)的接口規(guī)范,以 devfs_entry來包裝 dentry,以devfs_inode來包裝 inode。

 

devfs文件系統(tǒng)中的devfs_entry存放在一個(gè)叫做 fs_info的結(jié)構(gòu)變量中,以inode->ino節(jié)點(diǎn)編號(hào)作為索引可以查詢fs_info中的devfs_entry

 

數(shù)據(jù)結(jié)構(gòu)

===============

1. devfs_entry : 描述devfs中的目錄項(xiàng)

       typedef struct devfs_entry * devfs_handle_t;

       struct fcb_type  : 此結(jié)構(gòu)類型概括了 字符設(shè)備/塊設(shè)備/常規(guī)文件的信息,包括了設(shè)備的特性,安全管理和最重要的驅(qū)動(dòng)函數(shù)入口

       devfs_entry存放在fs_info->table[]

 

2.devfsd

       此為一服務(wù)進(jìn)程,觀察devfs 目錄項(xiàng)條目的運(yùn)行狀態(tài),比如加入新的entry, devfs_register()會(huì)觸發(fā)DEVFSD_NOTIFY_REGISTERED,devfsd便可以進(jìn)行一些新增設(shè)備的處理工作

      

 

函數(shù)

==============

1.get_devfs_entry_from_vfs_inode()   

       根據(jù)inode->ino,在fs_info->table中搜尋devfs_entry對(duì)象

 

 

+++++++++++

1.訪問devfs塊設(shè)備文件

命令: cat /dev/mtdblock/0

              mtdblock/0 的主設(shè)備號(hào)31,次設(shè)備號(hào) 12 ,之前是由mtd驅(qū)動(dòng)調(diào)用 devfs_register()注冊(cè)進(jìn)入內(nèi)核,并在devfsfs_info->table[]中占有一席之地。

              用戶在打開設(shè)備文件進(jìn)入內(nèi)核空間,在內(nèi)核空間中,fs層通過path_walk()等函數(shù)找到/dev/mtdblock/0對(duì)應(yīng)的dentry信息,

              然后構(gòu)建出file結(jié)構(gòu),將dentry->inode file作為參數(shù),傳入devfs文件系統(tǒng)的 dev_open()函數(shù)。

              devfs文件系統(tǒng)根據(jù)inode->ino編號(hào)在fs_info->table[]中找出mtd設(shè)備的devfs_entry對(duì)象,在devfs_entry->fcb->ops中存放著

              設(shè)備驅(qū)動(dòng)入口函數(shù),驅(qū)動(dòng)入口函數(shù)原型可以是file_operations或者block_device_operations. 然后將 devfs_entry->fcb->ops傳給inode->i_bdev->bd_op作為塊設(shè)備驅(qū)動(dòng)入口。

              devfs_open()中,如果當(dāng)前打開的是塊設(shè)備,就把缺省的塊設(shè)備訪問接口(def_blk_fops)付給file->f_op,然后調(diào)用file->f_open->open(inode,file)打開塊設(shè)備.

              **所以對(duì)比字符設(shè)備和塊設(shè)備,塊設(shè)備的訪問比字符設(shè)備多了一層 block_device_operation. 字符設(shè)備可以將驅(qū)動(dòng)寫在file_operations中,但塊設(shè)備必須寫在block_device_operation, 但要訪問設(shè)備的話都是通過 file_operation訪問,塊設(shè)備利用def_blk_fops進(jìn)行了file_operationblock_device_operation的轉(zhuǎn)換。

             

2.塊設(shè)備的讀寫

       ll_rw_block                 

      

      

devfs_register

===================

embedded_arch

1. bootloader :  vivi/u-boot

2. kernel_image

3. root_fs

 

bootloader通過jtag燒寫到flash rom

bootloader通過rs232kernel_image寫入flash,或者bootloaderrs232/ethernet動(dòng)態(tài)加載kernel_image

kernel_image加載fs,可以采取nfs/mtd方式加載

kernel支持mtd/nand文件系統(tǒng)驅(qū)動(dòng)

 

bootloader通過參數(shù)決定從何處掛載root-fs

 

 

fs

1. 新的文件系統(tǒng)注冊(cè)

       register_filesystem()

       find_filesystem()

       kern_mount()

數(shù)據(jù)結(jié)構(gòu):

       1.file_system_type

              struct file_system_type {

              const char *name;

              int fs_flags;

              struct super_block *(*read_super) (struct super_block *, void *, int);

              struct module *owner;/*動(dòng)態(tài)加載的模塊,編譯進(jìn)內(nèi)核則為0*/

              struct vfsmount *kern_mnt; /* For kernel mount, if it's FS_SINGLE fs */

              struct file_system_type * next;

              };

       2.file_systems(super.c)定義了文件系統(tǒng)類型的鏈表

 

IO訪問

1.外設(shè)訪問的方式

       *IO端口訪問 提供特殊的端口訪問指令,比如inb,outb

       *端口映射:  將外設(shè)register映射到cpu內(nèi)存空間,用普通操作指令便可訪問外設(shè)register 。非常方便,cpu開銷比較小,指令簡(jiǎn)單

2.防止優(yōu)化

       對(duì)于register映射到內(nèi)存的方式,將碰到兩種麻煩的可能。其一,硬件采用cache的方式來加快對(duì)內(nèi)存地址的訪問;其二,編譯器會(huì)優(yōu)化

       操作指令,比如寫入內(nèi)存數(shù)據(jù)可能會(huì)被放入寄存器。以上兩種情況都將導(dǎo)致訪問外射IO內(nèi)存地址空間失敗。

       所以為了避免這種情況,linux初始化時(shí)禁用訪問io地址空間時(shí)硬件cache功能;對(duì)于編譯器的解決方法就是采用barrier()

      

3.io端口地址的請(qǐng)求和釋放

       release_region

       request_region

4. io讀寫

       inb(),insb(),outb(),outsb()

       inb_p 暫停讀

 

io資源分配

1. request_region()

       請(qǐng)求io映射端口內(nèi)存地址范圍.內(nèi)存區(qū)域用resource數(shù)據(jù)結(jié)構(gòu)進(jìn)行表示。

       resource表現(xiàn)一段內(nèi)存地址,以樹型展示不同的區(qū)域。也就是簡(jiǎn)單的理解為一段內(nèi)存地址可以又分隔為許多內(nèi)存地址,所以resource具有了parent,sibling,child屬性

 

ipc

source/ipc目錄內(nèi)的ipc機(jī)制都是system V 新增的通信機(jī)制,包括 共享內(nèi)存,消息隊(duì)列和信號(hào)量

 

sys_pipe的實(shí)現(xiàn)在arch/i386/kernel中,似乎跟具體的平臺(tái)有某種關(guān)系,這是何故呢?

*pipe的實(shí)現(xiàn)比較簡(jiǎn)單,但是具有所有文件具有的特征,區(qū)別在于pipeinode節(jié)點(diǎn)中存在一個(gè) pipe_operation的操作跳轉(zhuǎn),兩個(gè)進(jìn)程一頭寫一頭讀,數(shù)據(jù)靠?jī)?nèi)核中申請(qǐng)的一頁(yè)內(nèi)存進(jìn)行中轉(zhuǎn)。

夫進(jìn)程在創(chuàng)建pipe之后,可以將pipe文件句柄傳遞到兩個(gè)子進(jìn)程去,使其子進(jìn)程之間可以進(jìn)行單向的數(shù)據(jù)通信。

 

**fifo 命名管道

      支持在無進(jìn)程關(guān)系的不同進(jìn)程之間通過打開命名管道名稱進(jìn)行通信

==管道都是基于流,效率和使用方式都不是很好,比如不能提供有效的消息分割,導(dǎo)致要到應(yīng)用層進(jìn)行數(shù)據(jù)的解析,類似于tcp流的處理.

 

system-V ipc sys_ipc()函數(shù)內(nèi)分流處理,類型有 semophera/message/share-memory

 

?sys_ipc()存在于 /arch/i386/kernel,又不知何故難道不可移植的嗎?

kernel運(yùn)行參數(shù)加載

include/asm-arm/mach/arch.h

arch/arm/mach-s3c2440/smdk.c

       定義平臺(tái)信息結(jié)構(gòu)

 

MACHINE_START(SMDK2440, "Samsung-SMDK2440")

       BOOT_MEM(0x30000000, 0x48000000, 0xe8000000)

       BOOT_PARAMS(0x30000100)     //定義linux啟示參數(shù)存放區(qū)域

       FIXUP(fixup_smdk)

       MAPIO(smdk_map_io)

       INITIRQ(s3c2440_init_irq)

MACHINE_END

 

start_kernel(){

       setup_arch(){

              setup_machine() //獲取平臺(tái)配置信息結(jié)構(gòu) struct machine_desc

              {

                     檢索__arch_info_begin開始的位置的平臺(tái)信息是否匹配平臺(tái)類型

                     vmlinux-armv.lds.in 包含__arch_info_begin 定義*(.arch.info)

                    

              }

       }

      

}

Linux啟動(dòng)過程中硬件模塊的加載

設(shè)備驅(qū)動(dòng)程序都用module_init(xx)進(jìn)行登記

module_init()函數(shù)就是將 xxx函數(shù)地址負(fù)值到 initcall(void*) 的函數(shù)指針變量,并將其連接如.initcall.init節(jié)中. __attribute__ ((unused,__section__ (".initcall.init")))

       kernel啟動(dòng)時(shí),在函數(shù)do_initcalls() initcall.init節(jié)中的所有函數(shù)地址都執(zhí)行一便,這樣就完成了靜態(tài)連接到kernel的驅(qū)動(dòng)的初始化調(diào)用

mtd_nand

1.nand支持結(jié)構(gòu)層次

 

       fs (user mode)

       mtd

       nand(driver)

 

** mtd/0              字符設(shè)備       driver/mtd/mtdchar.c

** mtdblock/0 塊設(shè)備  driver/mtd/mtdblock.c

      

3層結(jié)構(gòu),nand驅(qū)動(dòng)提供訪問flash 的底層接口函數(shù),mtd抽象訪問接口,最上層就是文件系統(tǒng)層,目前可以使用的是jiffs,yaffs2等等

 

2. 基本數(shù)據(jù)結(jié)構(gòu)

       struct mtd_info

              定義一堆的訪問nand驅(qū)動(dòng)的接口和數(shù)據(jù)

       add_mtd_device()   將一個(gè)nand 設(shè)備注冊(cè)為一個(gè)mtd設(shè)備

       add_mtd_partitions()      將分區(qū)注冊(cè)為mtd設(shè)備 (這種方式不能用于bon塊設(shè)備,因?yàn)檫@樣bon無法讀取正確的設(shè)備分區(qū)信息,bon是面向整個(gè)flash設(shè)備的)

       struct mtd_part 代表一個(gè)nand分區(qū) , mtd_part::mtd指向 mtd_info

      

       struct mtd_notifier {

        void (*add)(struct mtd_info *mtd);  注冊(cè)時(shí)回調(diào)fs的函數(shù)

        void (*remove)(struct mtd_info *mtd);

        struct mtd_notifier *next;

    };

    user-mode fs要提供mtd_notifier,當(dāng)nand設(shè)備插拔時(shí)將進(jìn)行通知(remove)fs

      

       register_mtd_user (struct mtd_notifier *new);

       unregister_mtd_user (struct mtd_notifier *old);

       以上這兩個(gè)方法供具體的fs調(diào)用

      

       CONFIG_MTD_SMC_S3C2440_SMDK_PARTITION 決定了是否將一個(gè)mtd分區(qū)注冊(cè)為一個(gè)mtd設(shè)備

      

1. s3c2440 nand驅(qū)動(dòng)

       driver\mtd\nand\smc_sc2440.c

       smc_insert()登記 nand partition信息

       smc_scan()    掃描nand類型,并安裝nand處理函數(shù)到 mtd_info中去

       add_mtd_partitions() 注冊(cè)mtd_info,支持partition

       add_mtd_device(){

              把每一個(gè)分區(qū)注冊(cè)為一個(gè)mtd設(shè)備

              把每個(gè)mtd分區(qū)注冊(cè)進(jìn)每一個(gè)mtd_notifier中去

       }

      

2.

       mtd_partition  定義mtd設(shè)備分區(qū)表

       mtd_part        定義分區(qū)信息結(jié)構(gòu)

                                   每個(gè)mtd_part分區(qū)都視為mtd設(shè)備,所以都具有 mtd_info數(shù)據(jù)結(jié)構(gòu)。并將mtd_part通過add_mtd_device()注冊(cè)進(jìn)入mtd_table[]數(shù)組

      

       mtd_part 擁有mtd_info的理由:

              mtd_info定義了訪問mtd設(shè)備的接口,比如 mtd.read().  mtd_part作為mtd設(shè)備的一個(gè)區(qū)間,但也被看作是一個(gè)mtd設(shè)備,所以讀取mtd的起始位置與直接訪問mtd主設(shè)備不一致,多了一個(gè)偏移量。mtd_part.mtd.read()函數(shù)中計(jì)算出mtd主設(shè)備中自己的絕對(duì)位置,然后調(diào)用主mtd設(shè)備的read()接口函數(shù),所以在訪問帶有分區(qū)的mtd設(shè)備時(shí)調(diào)用繞了一個(gè)小圈子。

              mtd_info中的諸多函數(shù)在主設(shè)備中定義為  nand_xxx(),而對(duì)應(yīng)著在mtd_part.mtd中也對(duì)應(yīng)定義了 part_xxx(),但調(diào)用還是最終要進(jìn)入 nand_xxx(). part_xxx()接口只是在為真正調(diào)用nand_xxx()而計(jì)算自己在主設(shè)備中的偏移量而已 。

       所以對(duì)mtd來講,一個(gè)分區(qū)就是一個(gè)mtd設(shè)備,如果沒有劃分分區(qū)則此設(shè)備就是一個(gè)mtd設(shè)備

      

       smc_insert

mtdblock

1. 初始化

 

       init_mtdblock(){

              devfs_register_blkdev()  注冊(cè)mtd塊設(shè)備到devfs文件系統(tǒng)

              register_mtd_user()              注冊(cè)mtd設(shè)備插拔事件接收者

              blk_init_queue()            初始化數(shù)據(jù)隊(duì)列

              mtdblock_thread()         啟動(dòng)工作線程

       }

      

/drivers/mtd/mtdcore.c 代碼中通過add_mtd_device()mtd/nand/smc_s3c2440.c中定義的分區(qū)mtd_part作為mtd_info設(shè)備注冊(cè)進(jìn)系統(tǒng)mtd設(shè)備鏈表

       修改分區(qū)信息:

              smc_s3c2440.c::smc_partitions[],

              開放 宏開關(guān) CONFIG_MTD_SMC_S3C2440_SMDK_PARTITION ,系統(tǒng)將所有定義的分區(qū)作為mtd設(shè)備登記入mtd_table[]數(shù)組.(通過/dev/mtdblock/n便穿越mtdblock驅(qū)動(dòng)到達(dá)mtd驅(qū)動(dòng)層獲取mtd分區(qū)信息)

 

64M K9F1208U0M Flash

15bit 32k pages = 16M

nand flash = 16M*4Plane = 64M

 

column Address 用于選擇 A,BC區(qū)地址

 

出廠時(shí)如果是壞塊,在第一或者第二頁(yè)的C區(qū)的第6字節(jié)不為0xff

 

Flash由于在使用時(shí)會(huì)產(chǎn)生新的壞塊,所以必須采用replacement策略,即在寫入檢測(cè)為壞塊則跳躍到下一個(gè)塊進(jìn)行處理。

 

//擦出操作時(shí)產(chǎn)生失敗,則有必要將此塊標(biāo)示為壞塊

       檢測(cè)到操作失敗

       定位到頁(yè)所在塊的第一個(gè)頁(yè)位置

       寫入C區(qū)517位置的值為非0xff

 

在讀寫之前判別好/壞塊的方法:

       從塊編號(hào)換算到頁(yè)編號(hào)

       NF_CMD(CMD_READ2);           // 0x50

       NF_ADDR(VALIDADDR)            //column 6個(gè)字節(jié),517位置,      #define VALIDADDR     0x05

       NF_ADDR()          //PAGE raws

       NF_RDDATA()  如果是0xff,表示為好塊,否則為壞塊

      

ECC校驗(yàn):

       Samsung 2440nandflash controller在讀寫flash之后將自動(dòng)產(chǎn)生ecc校驗(yàn)碼存放在ECC寄存器,所以在執(zhí)行寫頁(yè)操作之后,必須把ECC校驗(yàn)碼寫入到512~527這個(gè)C區(qū)間內(nèi),一般都放置在512+8的位置;在讀操作時(shí),將存儲(chǔ)的校驗(yàn)碼讀出并與ECC寄存器的值進(jìn)行比對(duì)即可

Flash 讀寫代碼:

 

BOOL FMD_WriteSector(SECTOR_ADDR startSectorAddr, LPBYTE pSectorBuff, PSectorInfo pSectorInfoBuff, DWORD dwNumSectors)

{

    BYTE Status;

    ULONG SectorAddr = (ULONG)startSectorAddr;

    ULONG MECC;             

    if (!pSectorBuff && !pSectorInfoBuff)

    {

        RETAILMSG(1,(TEXT("FMD_WriteSector: Failed sector write 01. \r\n")));

        return(FALSE);

    }

   

    NF_RSTECC();                            // Initialize ECC.

    NF_nFCE_L();                             // Select the flash chip.

    NF_CMD(CMD_RESET);        // Send reset command.

    NF_WAITRB();            // Wait for flash to complete command.

 

    while (dwNumSectors--)

    {

        ULONG blockPage = (((SectorAddr / NAND_PAGE_CNT) * NAND_PAGE_CNT) | (SectorAddr % NAND_PAGE_CNT));

 

        if (!pSectorBuff) //如果只是準(zhǔn)備寫入page信息(512~517)

        {

            // If we are asked just to write the SectorInfo, we will do that separately

            NF_CMD(CMD_READ2);                 // Send read command. 讀第C區(qū)命令

            NF_CMD(CMD_WRITE);                    // Send write command.

            NF_ADDR(0);                            // Column = 0.  第一個(gè)字節(jié)開始(512)

            NF_ADDR(blockPage         & 0xff);    // Page address. 頁(yè)行地址,15位寬,所以共 2^15=32K pages = 16M

            NF_ADDR((blockPage >>  8) & 0xff);

            NF_ADDR((blockPage >> 16) & 0xff); 

            WrPageInfo((PBYTE)pSectorInfoBuff);      //寫入信息

            pSectorInfoBuff++;

        }

        else

        {

          NF_RSTECC();

          NF_MECC_UnLock();

         

            NF_CMD(CMD_READ);                     // Send read command. 0x00

            NF_CMD(CMD_WRITE);                    // Send write command.

            NF_ADDR(0);                            // Column = 0.

            NF_ADDR(blockPage         & 0xff);    // Page address.

            NF_ADDR((blockPage >>  8) & 0xff);

            NF_ADDR((blockPage >> 16) & 0xff); 

 

            //  Special case to handle un-aligned buffer pointer.

            if( ((DWORD) pSectorBuff) & 0x3)

            {

                WrPage512Unalign (pSectorBuff);

            }

            else

            {

                WrPage512(pSectorBuff);                // Write page/sector data.

            }

                     NF_MECC_Lock();

                    

            // Write the SectorInfo data to the media.

            //

            if(pSectorInfoBuff)

            {

                WrPageInfo((PBYTE)pSectorInfoBuff);  //寫入尾部信息 512~519

                pSectorInfoBuff++;

 

            }

            else    // Make sure we advance the Flash's write pointer (even though we aren't writing the SectorInfo data)

            {

                BYTE TempInfo[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};

                WrPageInfo(TempInfo);

            }

            pSectorBuff += NAND_PAGE_SIZE;

 

            MECC = NF_RDMECC0();

            NF_WRDATA((MECC      ) & 0xff);

            NF_WRDATA((MECC >>  8) & 0xff);

            NF_WRDATA((MECC >> 16) & 0xff);

            NF_WRDATA((MECC >> 24) & 0xff);

 

        }

           

        //NF_CLEAR_RB();

        NF_CMD(CMD_WRITE2);                    // Send write confirm command.

        //NF_DETECT_RB();

        NF_WAITRB();                        // Wait for command to complete.

 

        NF_CMD(CMD_STATUS);

        Status = NF_RDDATA();

        if (Status & 0x1)

        {

            SetKMode (bLastMode);

            // EdbgOutputDebugString("ERROR: FMD_WriteSector: failed sector write.\r\n");

            RETAILMSG(1,(TEXT("FMD_WriteSector: Failed sector write 01. \r\n")));

            return(FALSE);

        }

 

        ++SectorAddr;

    }

 

    NF_nFCE_H();                            // Deselect the flash chip.

 

    SetKMode (bLastMode);

    return(TRUE);

}

 

 

 

BOOL FMD_ReadSector(SECTOR_ADDR startSectorAddr, LPBYTE pSectorBuff, PSectorInfo pSectorInfoBuff, DWORD dwNumSectors)

{

    ULONG SectorAddr = (ULONG)startSectorAddr;

    ULONG MECC,SECC,Count;

 

    BOOL bLastMode = SetKMode(TRUE);

 

    NF_RSTECC();                            // Initialize ECC.

    NF_nFCE_L();                            // Select the flash chip.

    NF_CMD(CMD_RESET);        // Send reset command.

    NF_WAITRB();            // Wait for flash to complete command.

   

    while (dwNumSectors--)

    {

        ULONG blockPage = (((SectorAddr / NAND_PAGE_CNT) * NAND_PAGE_CNT) | (SectorAddr % NAND_PAGE_CNT));

 

        NF_WAITRB();                            // Wait for flash to complete command.

           NF_RSTECC();

 

        if (pSectorBuff)

        {

            NF_CMD(CMD_READ);                    // Send read command.

            NF_ADDR(0);                            // Column = 0.

            NF_ADDR(blockPage         & 0xff);    // Page address.

            NF_ADDR((blockPage >>  8) & 0xff);

            NF_ADDR((blockPage >> 16) & 0xff); 

            NF_WAITRB();                        // Wait for command to complete.

 

            //  Handle unaligned buffer pointer

            NF_MECC_UnLock();

            if( ((DWORD) pSectorBuff) & 0x3)

            {

                RdPage512Unalign (pSectorBuff);

            }

            else

            {

                RdPage512(pSectorBuff);                // Read page/sector data.

            }

          NF_MECC_Lock();

 

              for (Count=0; Count<8; Count++)

                            NF_RDDATA(); 這里是要跳開8個(gè)字節(jié)的 sectorInfo信息,之后的4字節(jié)就是ECC

                     MECC  = NF_RDDATA() << 0;

                     MECC |= NF_RDDATA() << 8;

                     MECC |= NF_RDDATA() << 16;

                     MECC |= NF_RDDATA() << 24;

                    

                     NF_WRMECCD0( ((MECC&0xff00)<<8)|(MECC&0xff) );

                     NF_WRMECCD1( ((MECC&0xff000000)>>8)|((MECC&0xff0000)>>16) );

                    

                     if (NF_RDESTST0 & 0x3)

                     {

                         RETAILMSG(1,(TEXT("ecc error %x %x \r\n"),NF_RDMECC0(),MECC));

                    

                         NF_nFCE_H();                             // Deselect the flash chip.

                   SetKMode (bLastMode);

                   return FALSE; 

                     }           

 

        }

 

        if (pSectorInfoBuff)

        {

            NF_CMD(CMD_READ2);                    // Send read confirm command.

            NF_ADDR(0);                            // Column = 0.

            NF_ADDR(blockPage         & 0xff);    // Page address.

            NF_ADDR((blockPage >>  8) & 0xff);

            NF_ADDR((blockPage >> 16) & 0xff); 

            NF_WAITRB();                        // Wait for command to complete.

 

            RdPageInfo((PBYTE)pSectorInfoBuff);    // Read page/sector information.

 

            NF_RDDATA();                        // Read/clear status.

            NF_RDDATA();                        //

 

            pSectorInfoBuff++;

        }

 

        ++SectorAddr;

        pSectorBuff += NAND_PAGE_SIZE;

    }

 

    NF_nFCE_H();                             // Deselect the flash chip.

 

    SetKMode (bLastMode);

    return(TRUE);

}

 

tasklet

1.數(shù)據(jù)結(jié)構(gòu)

       struct tasklet_struct/*bh tasklet*/

       {

              struct tasklet_struct *next;

              unsigned long state;

              atomic_t count;

              void (*func)(unsigned long); /*服務(wù)程序*/

              unsigned long data;

       };

 

2.常用的操作方法  (linux/interrupt.h kernel/softirq.c)

       DECLARE_TASKLET(name, func, data)

       DECLARE_TASKLET_DISABLED(name, func, data)

       tasklet_schedule()

       tasklet_disable()  tasklet::count1

       tasklet_enable()     

       tasklet_kill()   從調(diào)度隊(duì)列中刪除

       mark_bh()

 

**tasklet 的引用計(jì)數(shù) 0才能被調(diào)度執(zhí)行

 

系統(tǒng)的軟中斷似乎都使用了tasklet方式,包括傳統(tǒng)的BH方式

       **TIMER_BH/TQUEUE_BH在時(shí)鐘中斷產(chǎn)生時(shí)將這兩個(gè)處理隊(duì)列放入軟中斷調(diào)度任務(wù)隊(duì)列

 

 

中斷在某個(gè)cpu上產(chǎn)生,在中斷處理函數(shù)中調(diào)度tasklet_schedule(),tasklet掛入此cputasklet處理隊(duì)列中,

tasklet_action()被軟中斷調(diào)用,然后將當(dāng)前cputasklet隊(duì)列取出,循環(huán)執(zhí)行一遍, (tasklet執(zhí)行一次)

 

**在一個(gè)cpu產(chǎn)生的tasklet則必定在這個(gè)cpu上被執(zhí)行

 

 

tasklet實(shí)現(xiàn)方式:

       DECLARE_TASKLET(xxxx)

       tasklet_schedule(xxx)

tqueue

tqueue也是Bh的實(shí)現(xiàn)方式,但在2.5版本已經(jīng)廢棄

 

tqueue.h定義了基本的數(shù)據(jù)類型

       queue_task()

       task_queue tq_timer, tq_immediate, tq_disk;

 

**tq_timer 在時(shí)鐘中斷產(chǎn)生時(shí)被mark_bh進(jìn)入調(diào)度隊(duì)列的

**tq_immediate加入隊(duì)列時(shí)候必須手動(dòng)調(diào)用mark_bh讓系統(tǒng)進(jìn)行調(diào)度

 

timer.c 定義了處理隊(duì)列

DECLARE_TASK_QUEUE(tq_timer);

DECLARE_TASK_QUEUE(tq_immediate);

 

 

void tqueue_bh(void){

       run_task_queue(&tq_timer);    執(zhí)行隊(duì)列任務(wù)

}

void immediate_bh(void){

       run_task_queue(&tq_immediate)

}

以上兩個(gè)標(biāo)準(zhǔn)的BH處理函數(shù)對(duì)應(yīng)TQUEUE_BHIMMEDIATE_BH,這兩個(gè)函數(shù)也被包裝成標(biāo)準(zhǔn)的tasklet供軟中斷調(diào)度執(zhí)行

 

采用tqueueBH方式的實(shí)現(xiàn):

       queue_task(&short_task, &tq_immediate);   加入一個(gè)task

    mark_bh(IMMEDIATE_BH);                             調(diào)度運(yùn)行

隊(duì)列BH方式,在被調(diào)度執(zhí)行完畢后tq_struct都將被刪除,下次使用必須再次將自己加入(queue_task)

Uart

 

driver/serial/serial_core.c 包含了串口操作的核心代碼

 

當(dāng)uart設(shè)備掛入devfs文件系統(tǒng)時(shí),設(shè)備名稱命名為ttyS%d

 

 

struct uart_driver

struct uart_ops              定義操作接口函數(shù)列表

struct uart_port      定義設(shè)備端口信息(包括一系列硬件配置信息)

struct console              控制臺(tái)設(shè)備

 

 

 

static struct console s3c2440_cons = {

       name:             "ttyS",

       write:             s3c2440_console_write,

       device:           s3c2440_console_device,

       wait_key: s3c2440_console_wait_key,

       setup:             s3c2440_console_setup,

       flags:              CON_PRINTBUFFER,

       index:             -1,

};

 

void __init s3c2440_console_init(void)

{

       register_console(&s3c2440_cons);

}

創(chuàng)建jffs2文件系統(tǒng)

kernel編譯支持jffs2

cramfs  mount /mnt

tar cf fs.tar mnt

cd /tmp

tar xf fs.tar

mkfs.jffs2 -r mnt -o root.jffs2 -l

 

配置網(wǎng)卡 ifconfig eth0 192.18.14.5 up

wget http://192.168.14.3/imagewrite

wget http://192.168.14.3/root.jffs2

 

#./imagewrite /dev/mtd/0 root_qtopia_2440.cramfs:2m

 

1.dependence tools

       wget

       imagewrite

       apache

       mkfs.jffs2

      

2.configuration

       啟動(dòng)httpd ,默認(rèn)/var/www/html為主目錄.

       ln -s /home/scott/kernel /var/www/html/kernel

 

wget http://192.168.103.55/kernel/zImage

      

 

磁盤緩沖

 

1. buffer cache

       磁盤io操作依賴buffer cache ,一個(gè)buffer對(duì)應(yīng)一個(gè)磁盤塊

       buffer cache 用于提高磁盤接收和存儲(chǔ)數(shù)據(jù)的性能.

       * data structure

              a set of buffer head

              a hash table

       buffer_head 包含數(shù)據(jù)緩沖區(qū),但內(nèi)核也保留一些不含數(shù)據(jù)緩沖區(qū)的buffer_head,避免分配和釋放的開銷

      

      

2. page cache

       一個(gè)page對(duì)應(yīng)多個(gè)磁盤塊 (x86中的PAGE_SIZE=4kb,一般的磁盤塊大小為512k/1024k)

       內(nèi)核在IO操作之前必須檢查待獲取的數(shù)據(jù)是否存在于page cache

       * page結(jié)構(gòu)中表示緩沖數(shù)據(jù)用 address_space數(shù)據(jù)結(jié)構(gòu)來描述(比如在文件中的偏移量)

       * struct address_space

              表示的頁(yè)類型:

                     常規(guī)文件或者目錄信息

                     內(nèi)存映射文件信息(mmap)

                     原始的塊設(shè)備數(shù)據(jù)u

                     用戶態(tài)進(jìn)程被交換到交換空間的數(shù)據(jù)

                     進(jìn)程間通信的共享內(nèi)存區(qū)域數(shù)據(jù)

              kernel 根據(jù)頁(yè)類型執(zhí)行適當(dāng)?shù)牟僮骱瘮?shù)來完成數(shù)據(jù)的讀取(regular file,block device,swap area ...)

       * address_space_operations 定義如何處理頁(yè)的操作表

             

pm.c 實(shí)現(xiàn)apm電源管理框架

mizi實(shí)現(xiàn)的代碼在 arm\kernel\apm2.c

mizi實(shí)現(xiàn)了APM_LCD_ON/OFF,APM_MZ_SLEEP,APM_IOC_SUSPEND

 

2440體系中,不同的設(shè)備調(diào)用pm_register()接口注冊(cè)電源回調(diào)函數(shù),在內(nèi)核中的pm_list存放電源管理設(shè)備鏈表。

在用戶層通過訪問/dev/apm_bios通過ioctl發(fā)送APM_LCD_ON等等控制命令,這些命令將被傳遞到具體的設(shè)備驅(qū)動(dòng)之電源回調(diào)函數(shù)之中

 

調(diào)度初始化

start_kernel{

       sched_init{

              init_bh(TIMER_BH, timer_bh);

              init_bh(TQUEUE_BH, tqueue_bh);

              init_bh(IMMEDIATE_BH, immediate_bh);

       }

}

進(jìn)程和調(diào)度

 

1.kernel_thread()

       用于創(chuàng)建內(nèi)核線程,其內(nèi)部匯編實(shí)現(xiàn)調(diào)用系統(tǒng)功能調(diào)用0x80,sys_clone實(shí)現(xiàn)

       觀看一下,有點(diǎn)好玩

       int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags)

       {

       long retval, d0;

       __asm__ __volatile__(

              "movl %%esp,%%esi\n\t"

              "int $0x80\n\t"        /* Linux/i386 system call */

              "cmpl %%esp,%%esi\n\t"     /* child or parent?  比較堆棧指針,如果變了就認(rèn)為是子進(jìn)程*/

              "je 1f\n\t"        /* parent - jump */

              /* Load the argument into eax, and push it.  That way, it does

               * not matter whether the called function is compiled with

               * -mregparm or not.  */

              "movl %4,%%eax\n\t"

              "pushl %%eax\n\t"         /*堆棧傳遞參數(shù)*/

              "call *%5\n\t"         /* call fn 如果在子進(jìn)程空間內(nèi)就直接調(diào)用線程函數(shù)*/

              "movl %3,%0\n\t"  /* exit */

              "int $0x80\n"

              "1:\t"

              :"=&a" (retval), "=&S" (d0)               /* EAX= __NR_clone*/

              :"0" (__NR_clone), "i" (__NR_exit),

               "r" (arg), "r" (fn),

               "b" (flags | CLONE_VM)

              : "memory");

              return retval;

       }

塊設(shè)備驅(qū)動(dòng)

1.request_queue

 

request_queue 包含buffer_head隊(duì)列頭,對(duì)列尾

buffer_head 緩沖數(shù)據(jù)頭信息,buffer_head維持內(nèi)存數(shù)據(jù)區(qū)域

 

request_queue { head,tail}

       buffer_head

              buffers in ram

      

 

塊大小是sector的整數(shù)倍但不能超過 PAGE_SIZE,而且必須是2的冪

 

 

2.最多255個(gè)設(shè)備,由全局變量blkdevs維持

 

 

全局變量

       bon_blksizes   (int**) 存放指定設(shè)備的次設(shè)備塊大小的數(shù)組

              bon_blksizes[i] = 1024;

              blksize_size[BON_MAJOR] = bon_blksizes;

             

             

.塊設(shè)備的讀寫

       塊設(shè)備通過塊隊(duì)列進(jìn)行緩沖讀寫,由專門的工作線程完成工作

       編寫塊設(shè)備驅(qū)動(dòng)必須提供一個(gè)塊隊(duì)列和一個(gè)塊處理接口函數(shù)(request_fn_proc)

       blk_init_queue(){    初始化塊隊(duì)列

             

       }

 

內(nèi)存管理

內(nèi)存管理硬件限制:

       DMA設(shè)備只能訪問 16M地址空間

       線性地址空間太小,不能訪問所有的物理內(nèi)存(比如32位不能過4GB)

linux將物理內(nèi)存劃分為3部分:ZONE_DMA ,ZONE_NORMAL ,ZONE_HIGHMEM

 

1.Page

       描述物理頁(yè)面的數(shù)據(jù)結(jié)構(gòu)

      

2. zone

       描述相同屬性的page的集合,3zone

       ZONE_DMA

       ZONE_NORMAL    is all physical memory from 16MB to 896MB. On other (more fortunate) architectures, ZONE_NORMAL is all available memory

       ZONE_HIGHMEM   all memory above the physical 896MB mark,cannot be directly accessed by kernel, but not be used on 64-bit architectures,not existed

      

       struct zone_struct 描述內(nèi)存區(qū)結(jié)構(gòu)

3. node

       將相同屬性的區(qū)組織成節(jié)

 

4.內(nèi)存分配

       allocate_page()       分配頁(yè)

       __get_free_pages() 分配頁(yè),返回頁(yè)的虛擬地址

       kmalloc()        分配指定字節(jié)大小的內(nèi)存空間

5.內(nèi)存分配標(biāo)志

       The flags are broken up into three categories: action modifiers, zone modifiers, and types. Action modifiers specify how the kernel is supposed to allocate the requested memory. In certain situations, only certain methods can be employed to allocate memory. For example, interrupt handlers must instruct the kernel not to sleep (because interrupt handlers cannot reschedule) in the course of allocating memory. Zone modifiers specify from where to allocate memory. As you saw earlier in this chapter, the kernel divides physical memory into multiple zones, each of which serves a different purpose. Zone modifiers specify from which of these zones to allocate. Type flags specify a combination of action and zone modifiers as needed by a certain type of memory allocation. Type flags simplify specifying numerous modifiers; instead, you generally specify just one type flag. The GFP_KERNEL is a type flag, which is used for code in process context inside the kernel. Let's look at the flags.

      

NUMA

=====   

       某些系統(tǒng)內(nèi)存是非勻質(zhì)的,也就是訪問不同的內(nèi)存地址時(shí)所花費(fèi)的開銷是不同的

       所以系統(tǒng)將內(nèi)存劃分為多個(gè)結(jié)( memory is partitioned in several nodes),在同一個(gè)結(jié)中cpu訪問時(shí)間是一致的

       IBM compatible PC is UMA,thus NUMA support is not really required

 

buddy system algorithm

========================

軟中斷

1. 在硬中斷中觸發(fā)軟中斷,并轉(zhuǎn)入BH部分,等待調(diào)度線程調(diào)用do_softirq()來執(zhí)行軟中斷入口函數(shù)

       2.4內(nèi)核4個(gè)軟中斷向量

       2.6則有6個(gè)

       中斷向量依次0~n,有限級(jí)依次降低

      

       最近一個(gè)中斷返回的時(shí)候看起來就是執(zhí)行do_softirq()的最佳時(shí)機(jī)。因?yàn)?/span>TASKLET_SOFTIRQHI_SOFTIRQ已經(jīng)被觸發(fā)了

      

2. 軟中斷負(fù)荷:(軟中斷內(nèi)核處理線程)

       內(nèi)核實(shí)際選中的方案是不立即處理重新觸發(fā)的軟中斷。而作為改進(jìn),當(dāng)大量軟中斷出現(xiàn)的時(shí)候,

       內(nèi)核會(huì)喚醒一組內(nèi)核線程來處理這些負(fù)載。這些線程在最低的優(yōu)先級(jí)上運(yùn)行(nice值是19),

       這能避免它們跟其它重要的任務(wù)搶奪資源。但它們最終肯定會(huì)被執(zhí)行,所以,這個(gè)折中方案能夠保證在軟中斷

       負(fù)擔(dān)很重的時(shí)候用戶程序不會(huì)因?yàn)榈貌坏教幚頃r(shí)間而處于饑餓狀態(tài)  

       只要do_softirq()函數(shù)發(fā)現(xiàn)已經(jīng)正在執(zhí)行過的內(nèi)核線程重新觸發(fā)了它自己,軟中斷內(nèi)核線程就會(huì)被喚醒。

       #此內(nèi)核線程方式2.4中似乎不存在

3. 工作隊(duì)列 workqueue_struct   2.4中不存在      

       在工作隊(duì)列和軟中斷/tasklet中作出選擇非常容易。

       如果推后執(zhí)行的任務(wù)需要睡眠,那么就選擇工作隊(duì)列。如果推后執(zhí)行的任務(wù)不需要休睡眠,那么就選擇軟中斷或tasklet

       工作隊(duì)列中的對(duì)象具有延遲執(zhí)行的定時(shí)器

       當(dāng)前的2.6版內(nèi)核中,有三種可能的選擇:軟中斷、tasklets和工作隊(duì)列

      

4. 軟中斷與驅(qū)動(dòng)程序:

       編寫驅(qū)動(dòng)程序調(diào)用request_irq()注冊(cè)中斷入口

       調(diào)用open_softirq()注冊(cè)bh的軟中斷處理入口

       中斷產(chǎn)生,執(zhí)行top half, 調(diào)用 tasklet_scehedule()加入工作函數(shù),調(diào)用 cpu_raise_softirq()觸發(fā)軟中斷

       在系統(tǒng)的schedule()中執(zhí)行do_softirq(),檢測(cè)軟中斷向量,依次執(zhí)行每一個(gè)tasklet入口函數(shù)

 

5. 軟中斷初始化

       start_kernel(){

              softirq_init(){

                     初始化32個(gè)tasklet,因?yàn)榇嬖?/span>32個(gè)BH處理函數(shù)

                     注冊(cè)軟中斷的中斷函數(shù)TASKLET_SOFTIRQ(tasklet_action),HI_SOFTIRQ(tasklet_hi_action)

              }

       }

時(shí)鐘中斷

1. SA_INTERRUPT:     中斷期間不允許中斷再次產(chǎn)生

 

 

top_half:

中斷觸發(fā) >> xxx_BH >> MarkBH >> Task_Schedule

bottom_half:

Schedule >> do_softirq >> check_task_vec(HI_SOFTIRQ/TASKLET_SOFTIRQ) >> exec_tasklet

 

sample:

 時(shí)鐘中斷0,觸發(fā)時(shí)鐘計(jì)時(shí)(jiffies++),調(diào)用MarkBhbh函數(shù)推入TaskLet隊(duì)列等待調(diào)度執(zhí)行;在調(diào)度時(shí),檢測(cè)軟中斷觸發(fā)標(biāo)志,循環(huán)執(zhí)行完中斷對(duì)應(yīng)的tasklet隊(duì)列。

  tasklet這種軟中斷處理方式跟irq_desc硬中斷處理有相似但也有不同的地方。兩種處理方式都允許一個(gè)中斷向量綁定若干個(gè)中斷處理函數(shù)

 

 

pc的時(shí)鐘設(shè)置為100MHZ , 10us

過高設(shè)置時(shí)鐘頻率導(dǎo)致調(diào)度overhead,可能引起用戶空間無法響應(yīng)的情況 

 

 

2.時(shí)鐘中斷初始化

       start_kernel{

              time_init{

                     setup_irq(0,irq0); //irq0 -- irq_action 時(shí)鐘中斷

              }

       }

 

3.時(shí)鐘中斷的處理函數(shù) timer_interrupt() (time.c) ,此種中斷類型SA_INTERRUPT,irq0

       所以不能被其他共享(也許為了提高響應(yīng)速度防止時(shí)鐘扭曲,在這里進(jìn)行了request_irq(0,SA_SHIRQ)的嘗試,一直失敗,看了代碼才明白)

 

4.中斷處理過程

       timer_interrupt(){

              basic do something

              do_timer_interrupt(){

                     do_timer(){

                            jiffies++

                            mark_bh(TIMER_BH){ 觸發(fā)后半部執(zhí)行                                            

                                   tasklet_hi_schedule(bh_task_vec+nr) bh部分加入調(diào)度隊(duì)列                                         

                                   如果tq_timer隊(duì)列非空,則把定時(shí)器隊(duì)列掛入調(diào)度隊(duì)列,mark_bh(TQUEUE_BH)

                            }

                     }

              }

       }

      

       # void (*bh_base[32])(void);  標(biāo)準(zhǔn)的32個(gè)BH回調(diào)函數(shù)

       # struct tasklet_struct bh_task_vec[32]; bh_base包裝成32個(gè)tasklet

       # bh_task_vec softirq_init()時(shí)被初始化32個(gè)tasklet,統(tǒng)一設(shè)置為bh_action()處理函數(shù)

       # 在時(shí)鐘中斷產(chǎn)生時(shí)將 bh_task_vec[TIMER_BH]推入 軟中斷調(diào)度隊(duì)列,調(diào)度器將循環(huán)執(zhí)行tasklet整個(gè)鏈表.

              所以在這里我為了將自己的函數(shù)掛入TIMER_BH隊(duì)列并隨時(shí)鐘中斷實(shí)時(shí)的被系統(tǒng)調(diào)度,我認(rèn)為只要將自己的處理

              函數(shù)包裝成一個(gè)tasklet,然后掛到bh_task_vec[TIMER_BH]之后就可以,這樣就能實(shí)現(xiàn)簡(jiǎn)單的時(shí)鐘中斷響應(yīng)

 

5.時(shí)鐘定時(shí)器

       TIMER_BH的實(shí)現(xiàn)函數(shù)timer_bh()  [kernel/timer.c]中進(jìn)行時(shí)鐘的更新和系統(tǒng)定時(shí)器的執(zhí)行

      

       timer_bh(){

              update_times();

              run_timer_list(); 執(zhí)行所有定時(shí)任務(wù)         

       }

      

       定時(shí)任務(wù)添加: add_timer()

數(shù)據(jù)同步管理

 

 

1.原子操作

       atomic_read

       atomic_sub_and_test(int i, atomic_t *v); 該函數(shù)從原子類型的變量v中減去i,并判斷結(jié)果是否為0,如果為0,返回真,否則返回假。

2.semaphore

       DECLARE_MUTEX

       DECLARE_MUTEX_LOCKED

       init_MUTEX

       sema_init

       down

       down_interruptible

       down_trylock

       up

3.waitqueue

       init_waitqueue_head

      

       wait_event_interruptible 等待事件喚醒,調(diào)用將被阻塞

       wake_up

      

       struct wait_queue_head_t  等待隊(duì)列數(shù)據(jù)結(jié)構(gòu)

              在此數(shù)據(jù)結(jié)構(gòu)中維持1把訪問鎖和一個(gè)進(jìn)程鏈表,等待處理進(jìn)程都將掛入此隊(duì)列

             

4.spinlock

       掛起cpu,只能同時(shí)由一人操作,可用于中斷處理. 一般都利用lock  鎖住總線的方式來實(shí)現(xiàn). spinlock只有在SMP情況下使用,cpu時(shí)spinlock是空代碼

       local_irq_disable

       local_irq_enable

       local_bh_disable

       bh local_bh_enable

       local_irq_save

       local_irq_restore

       wait_event_interruptible(w,f) 將當(dāng)前task加入等待隊(duì)列,內(nèi)部循環(huán)檢測(cè)f,f如果為true,則馬上退出阻塞狀態(tài);否則scadule()進(jìn)行一次調(diào)度切換

       wake_up_interruptible

      

      

      

== 中斷處理top half過程,如果是單處理器,此刻中斷是關(guān)閉的,所以不用考慮數(shù)據(jù)保護(hù)問題

       但在多處理器的環(huán)境中,不同的中斷會(huì)同時(shí)在不同cpu上產(chǎn)生,所以共享的數(shù)據(jù)必須進(jìn)行保護(hù)

      

== semaphore阻塞當(dāng)前進(jìn)程,將進(jìn)行推入等待隊(duì)列,以待調(diào)度程序進(jìn)行調(diào)度處理,所以不能在中斷處理函數(shù)中使用

      spin_lock  freeze system

 

文件系統(tǒng)

1. cramfs

       塊文件系統(tǒng),解壓到內(nèi)存讀,文件系統(tǒng)不可寫

       命令: mkfs.cramfs

2. tmpfs

       建立在vfs上,文件系統(tǒng)使用虛擬地址空間,所以掉電丟失數(shù)據(jù),優(yōu)點(diǎn)在于訪問,讀寫快速,可變大小

       動(dòng)態(tài)增加fs大小,可能將耗盡vm空間和物理內(nèi)存或者swap空間。

       命令: mount tmpfs /mnt/tmpfs -t tmpfs

       創(chuàng)建一個(gè)新的最大 32 MB tmpfs 文件系統(tǒng)

              mount tmpfs /dev/shm -t tmpfs -o size=32m

       **添加到 /etc/fstab,應(yīng)該是這樣:

       tmpfs      /dev/shm tmpfs      size=32m 0     0

 

 

       編譯: 啟用了Virtual memory file system support

3.ramfs

cramfs: Compressed ROM File System,是只讀文件系統(tǒng),其容量上限只有 256MB.

romfs: 非常小的只讀文件系統(tǒng).

jffs: 日志式快閃(Flash) 文件系統(tǒng).

tmpfs: 可以用來將數(shù)據(jù)暫時(shí)保存在RAM,而且容量可以隨著保存數(shù)據(jù)的量變 .

ramfs: 也可以用來將數(shù)據(jù)暫時(shí)保存在RAM,tmpfs類似.

WOLF Linux在嵌入式設(shè)備環(huán)境下使用,支持flash,同時(shí)又要支持多平臺(tái)下的存儲(chǔ)卡,所以,在選擇文件系統(tǒng)時(shí)要考慮到實(shí)際應(yīng)用.

WOLF Linux對(duì)文件系統(tǒng)的要求是:

要用盡可能少的內(nèi)存

速度要快

臨時(shí)文件操作不能在flash上進(jìn)行,因?yàn)?/span>flash的擦寫是有壽命的.

內(nèi)核運(yùn)行需要的文件和數(shù)據(jù)存放在flash,而一些臨時(shí)文件則用內(nèi)存作為存儲(chǔ)介質(zhì).所以內(nèi)核運(yùn)行采用兩種文件系統(tǒng):jffs tmpfs.

Jffs 文件系統(tǒng)是在flash上操作,內(nèi)部有專門對(duì)flash的優(yōu)化.采用jffs的優(yōu)點(diǎn)是內(nèi)存占用少,速度快,體積小.現(xiàn)在很多嵌入式linux采用的是基于 EXT2Ramdisk.Ramdiskflash上要占用2MB以上,啟動(dòng)時(shí)還要解壓縮到內(nèi)存,需要占用內(nèi)存10MB以上,缺點(diǎn)是浪費(fèi)內(nèi)存,啟動(dòng)速度慢.內(nèi)存需要10MBRamdisk中的內(nèi)容采用jffs只需占flash 2.1MB左右.

tmpfs由于體積隨內(nèi)容可增可減,所以它沒有浪費(fèi)內(nèi)存,一些臨時(shí)文件可以采用這種格式.

而書籍等數(shù)據(jù)文件是存放在存儲(chǔ)卡上的,這些存儲(chǔ)卡可能在不同的平臺(tái)上使用,比如,出版社用NTFS格式化了存儲(chǔ)卡,然后在上面加密拷貝了一些書籍.這時(shí)要求 WOLF Linux能識(shí)別該卡,并且完成閱讀軟件要求的讀寫操作,所以,支持存儲(chǔ)卡的文件系統(tǒng)更加多樣: FAT,VFAT (Windows-95),NTFS,EXT.

 

文件系統(tǒng)加載過程

 

 

start_kernel(){

       init(){

              do_basic_setup(){

                     硬件初始化

                     sock_init()

                     do_initcalls();  //調(diào)用所有靜態(tài)連接到內(nèi)核的驅(qū)動(dòng)初始化代碼,包括文件系統(tǒng)

                     filesystem_setup(){

                            init_devfs_fs(){

                                   register_filesystem( devfs)

                                   kern_mount(devfs) 掛上devfs文件系統(tǒng)(devfs_read_super)

                            }

                            init_nfs_fs()

                     }

                     mount_root() 掛根分區(qū)

                     mount_devfs_fs() root上掛devfs

                     如果是initrd啟動(dòng),執(zhí)行/linuxrc

              }

       }

       execv( /bin/init)

}

 

 

2. root文件系統(tǒng)加載

       比如root=/dev/hda1

       start_kernel(){

              parse_options(){

                     checksetup(){

                            一次調(diào)用命令行參數(shù)對(duì)應(yīng)的處理函數(shù),比如,__setup("root=", root_dev_setup);

                            root_dev_setup(){

                                   從命令行中拾取 root=之后的參數(shù)

                                   ROOT_DEV = name_to_kdev_t(){

                                          根據(jù)傳遞進(jìn)入的root fs名稱到 root_dev_names數(shù)組中找尋對(duì)應(yīng)的設(shè)備編號(hào)

                                          將此編號(hào)加上子設(shè)備號(hào) ,比如hda1 就加上1,hda2就加上2

                                          to_kdev_t()轉(zhuǎn)換成kdev_t作為root文件系統(tǒng)設(shè)備號(hào)返回

                                   }

                                   將設(shè)備名稱放入root_device_name數(shù)組中緩存,比如( root_device_name = hda1)

                                  

                            }

                     }

              }

       }

       內(nèi)核啟動(dòng)時(shí),分解根分區(qū)參數(shù),然后根據(jù)名稱(main.c:root_dev_names[])找到對(duì)應(yīng)的主設(shè)備編號(hào)(major),

       掛載devfs文件系統(tǒng)到內(nèi)存

       初始化所有的驅(qū)動(dòng)__initcall類型的函數(shù),這些初始化函數(shù)會(huì)將驅(qū)動(dòng)設(shè)備注冊(cè)進(jìn)入devfs

       執(zhí)行mount_root(){

              nfs()              啟動(dòng)

              floppy()   軟盤啟動(dòng)

              如果root=參數(shù)中的設(shè)備名稱未定義在root_dev_names[]列表之中,也就是說啟動(dòng)設(shè)備編號(hào)(major)無法獲得,

              則嘗試拿著設(shè)備名稱到devfs文件系統(tǒng)中去查找 devfs_find_handle(),如果找到存在這樣的條目,則獲取主,次設(shè)備編號(hào),

              然后調(diào)用這些設(shè)備驅(qū)動(dòng)讀取super_block.

              獲取super_block之后將其送入每一個(gè)文件系統(tǒng),看看這super_block是屬于誰的

       }

      

       devfs_find_handle(devfs_handle_t dir, const char *name,

                              unsigned int major, unsigned int minor,

                              char type, int traverse_symlinks)

        如果nameNULL,則根據(jù) majorminor進(jìn)行搜索,否則就根據(jù)名稱查找

      

3. __setup()

       安裝一些與內(nèi)核啟動(dòng)參數(shù)對(duì)應(yīng)的處理函數(shù),在parse_options()時(shí)遍歷到此參數(shù)時(shí)便調(diào)用對(duì)應(yīng)的處理函數(shù)

       這是一種內(nèi)核啟動(dòng)參數(shù)與內(nèi)核驅(qū)動(dòng)傳遞數(shù)據(jù)的一種方式

       使用方式: __setup("com90io=", com90io_setup);

       此種方式只適合靜態(tài)編鏈的內(nèi)核代碼,不適合模塊動(dòng)態(tài)加載方式.

       checksetup()執(zhí)行與內(nèi)核參數(shù)對(duì)應(yīng)的處理函數(shù)

      

      

4.    do_mount(){   加載文件系統(tǒng)

             

 

       }

      

       文件系統(tǒng)都構(gòu)件在塊設(shè)備上。設(shè)備文件 /dev/hda1 inode信息中包含設(shè)備編號(hào) kdev_t,根據(jù)設(shè)備編號(hào)可以找到此塊設(shè)備驅(qū)動(dòng)

 

中斷處理

1.共享中斷

       中斷信號(hào)n到來將依次調(diào)用中斷n的處理隊(duì)列中的處理函數(shù),中斷處理函數(shù)必須能識(shí)別devid(devid唯一)

       沒有一種可以檢測(cè)IRQ設(shè)備的方法

2.中斷檢測(cè)

       必須在空閑的中斷線路上進(jìn)行探測(cè)

 

3.中斷上半部和下半部共享使用的數(shù)據(jù)變量,在下半部處理時(shí)需要關(guān)閉中斷


Feedback

# re: 【Linux Kernel】 讀碼時(shí)寫的一些筆記,畢竟寫給自己看,有些亂(好好理解kernel,能更好寫出高效的驅(qū)動(dòng)和應(yīng)用代碼) 2008-07-04 09:56 呵呵
學(xué)習(xí)了  回復(fù)  更多評(píng)論
  

# re: 【Linux Kernel】 讀碼時(shí)寫的一些筆記,畢竟寫給自己看,有些亂(好好理解kernel,能更好寫出高效的驅(qū)動(dòng)和應(yīng)用代碼) 2008-07-10 23:24 夢(mèng)在天涯
關(guān)于liunx kerne有什么好書,推薦一個(gè),謝謝先??!  回復(fù)  更多評(píng)論
  

# re: 【Linux Kernel】 讀碼時(shí)寫的一些筆記,畢竟寫給自己看,有些亂(好好理解kernel,能更好寫出高效的驅(qū)動(dòng)和應(yīng)用代碼) 2008-07-16 02:58 放屁啊狗
@夢(mèng)在天涯
書太多,我可以提供ftp供你下載  回復(fù)  更多評(píng)論
  

青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            亚洲欧美日本另类| 欧美三级在线| 欧美在线一区二区三区| 精品二区久久| 国产亚洲aⅴaaaaaa毛片| 欧美日韩小视频| 欧美日韩国产123区| 欧美成人一区在线| 亚洲精品一区二区三区蜜桃久| 一区在线播放| 99精品国产高清一区二区| 欧美成人黑人xx视频免费观看| 久久精品国产成人| 久久久精品一品道一区| 久久国产精品99久久久久久老狼| 欧美一区二区三区在线观看视频| 香港成人在线视频| 亚洲国产女人aaa毛片在线| 久久午夜视频| 午夜在线观看免费一区| 欧美日韩在线直播| 亚洲电影免费在线观看| 久久久久成人网| 午夜精品福利一区二区蜜股av| 一区二区三区国产在线| 欧美三区在线观看| 最近中文字幕日韩精品| av成人动漫| 欧美成人dvd在线视频| 国模吧视频一区| 在线视频欧美一区| 免费日韩成人| 久久精品亚洲精品| 国产欧美日韩亚州综合| 一区二区欧美精品| 亚洲日本国产| 欧美成人免费小视频| 亚洲人人精品| 久久视频国产精品免费视频在线 | 亚洲天堂av在线免费观看| 久久久精品国产免大香伊 | 国产精品亚洲精品| 亚洲伊人网站| 久久女同互慰一区二区三区| 午夜亚洲精品| 亚洲视频精品| 夜夜爽99久久国产综合精品女不卡| 9人人澡人人爽人人精品| 欧美日韩精品免费观看视频| 日韩视频在线免费观看| 亚洲一区二区三区高清| 国产亚洲欧美aaaa| 欧美激情视频一区二区三区不卡| 欧美久久成人| 欧美韩日高清| 欧美有码在线观看视频| 国产精品一区视频网站| 老司机一区二区三区| 欧美另类99xxxxx| 久久国产毛片| 欧美午夜影院| 亚洲国产午夜| 在线观看视频一区二区欧美日韩| 亚洲精品久久久久久下一站| 欧美性开放视频| 久久久久久一区二区三区| 欧美区亚洲区| 亚洲国产精品久久久久秋霞蜜臀 | 欧美精品999| 亚洲一区综合| 国产精品国码视频| 一区二区高清视频| 99热这里只有精品8| 欧美激情在线观看| 夜夜嗨av一区二区三区网页| 亚洲精品一线二线三线无人区| 欧美在线观看一区二区| 久久国产精品久久w女人spa| 国产日韩精品久久| 久久国产一区二区三区| 老司机午夜精品视频在线观看| 尤物九九久久国产精品的分类| 久久久国产午夜精品| 欧美激情中文字幕一区二区| 99国内精品久久| 欧美日韩视频一区二区| 一区二区三区蜜桃网| 久久免费视频一区| 亚洲最黄网站| 在线 亚洲欧美在线综合一区| 欧美精品激情在线观看| 99综合视频| 牛牛精品成人免费视频| 最新成人在线| 久久久久国产一区二区| 亚洲精品少妇| 狠狠久久亚洲欧美| 国产精品sm| 开心色5月久久精品| 亚洲天堂av综合网| 亚洲视频高清| 日韩网站在线| 欧美jizzhd精品欧美巨大免费| 91久久夜色精品国产网站| 国产精品系列在线| 欧美激情第1页| 久久亚洲国产精品日日av夜夜| 亚洲午夜精品久久| 一二三区精品| 亚洲免费福利视频| 亚洲国产一区视频| 国内成+人亚洲| 国产一区二区三区在线观看免费 | 久久国产黑丝| 亚洲欧美美女| 欧美一区日本一区韩国一区| 亚洲在线一区二区| 午夜精品久久久久久久久久久久久| 午夜精品剧场| 久久一区二区三区四区五区| 久久免费观看视频| 久久久一本精品99久久精品66| 久久免费观看视频| 美女国内精品自产拍在线播放| 欧美aaaaaaaa牛牛影院| 欧美精品亚洲精品| 国产精品久久久一本精品| 国产精品久在线观看| 黑人操亚洲美女惩罚| 亚洲少妇一区| 久久综合久久综合九色| 亚洲国产精品视频一区| 9色精品在线| 久久精品人人| 国产精品激情| 亚洲国产成人久久综合| 亚洲欧美日韩国产一区| 欧美福利视频一区| 欧美一区二区三区电影在线观看| 奶水喷射视频一区| 在线观看欧美日韩| 欧美在线一二三区| 国产精品99久久久久久久女警 | 久久久久久久久久久一区 | 经典三级久久| 亚洲一区二区免费在线| 亚洲激情成人在线| 午夜在线观看免费一区| 国产精品chinese| 亚洲午夜未删减在线观看| 亚洲毛片一区二区| 欧美成年网站| 在线视频精品一区| 正在播放亚洲一区| 国产精品嫩草99av在线| 亚洲一区图片| 中文av一区特黄| 国产农村妇女精品一二区| 久久精品天堂| 免费在线视频一区| 久久精品国产清高在天天线| 亚洲激情社区| 亚洲综合首页| 激情一区二区三区| 最新日韩欧美| 韩国精品主播一区二区在线观看| 麻豆91精品| 欧美伦理影院| 久久精品视频网| 欧美日韩亚洲综合一区| 久久久久www| 国产精品美女一区二区在线观看| 久久久久看片| 国产精品一区一区三区| 99精品国产热久久91蜜凸| 国产一区二区激情| 中文亚洲字幕| 一区二区三区www| 欧美成人tv| 久久激情久久| 国产精品美女在线观看| 欧美激情精品久久久久久| 国产伦精品一区二区三区免费 | 亚洲欧美三级伦理| 欧美精品久久一区| 亚洲电影成人| 亚洲啪啪91| 欧美成在线视频| 亚洲国产日韩在线一区模特| 狠狠久久婷婷| 久久久国产视频91| 久久在线免费观看视频| 国产婷婷精品| 久久久久久国产精品mv| 久久精品国产欧美激情| 国产精品爽黄69| 亚洲欧美视频在线观看| 久久久国产视频91| 亚洲欧洲另类| 久久在线免费视频|