最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會員登陸 & 注冊

Linux嵌入式啟動以及優(yōu)化

2023-02-14 11:13 作者:有AI野心的電工和碼農(nóng)  | 我要投稿

小破綻的這個文章編輯功能真是渣,難用的很!由于本文里很多有序條目,文章cp過來序號給亂標,一修改更亂,實在懶得改了!有興趣的可看我的博文網(wǎng)頁https://wikieee.com/blog/leiad/linux/linux%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%90%AF%E5%8A%A8%E4%BB%A5%E5%8F%8A%E4%BC%98%E5%8C%96/we

或者同用戶名的公眾號or頭條號。

Original address:?http://blog.21ic.com/user1/5593/archives/2010/67071.html

以前寫了一篇Linux PC啟動過程的日記, 最近項目中, 想優(yōu)化一下啟動過程, 減少啟動時間.

因此研究了我們項目的啟動全過程.

1. 第一步: BootLoader -- U boot

1.1 在cpu/arm926ejs/start.s中

  1. b reset ; //jump to reset

  2. set cpsr ;svc mode ,disable I,F interrupt

  3. 調(diào)用lowlevel_init (在board\xxxx\lowlevel_init.S中), 將調(diào)用

  • __platform_cmu_init (設(shè)置cpu時鐘,啟動那些模塊等)

  • __platform_mpmc_init (mpmc初始化,配置SDRAM時序)

  • __platform_static_memory_init

  • __platform_static_uart_init

  • __platform_mpmc_clear

  1. 用LDMIA,STMIA命令 copy uboot 到內(nèi)存中

  2. ldr pc ,_start_armboot

    執(zhí)行start_armboot

    1.2 start_armboot 在 lib-arm 中

    1. 根據(jù)init_sequence 執(zhí)行初始化序列, 包括:

    • cpu_init

    • board_init

    • 中斷初始化

    • initialize environment

    • initialze baudrate settings

    • serial communications setup

    • 打印uboot 版本

    • display_dram_config (打印DRAM大?。?/p>

    而在board_init中

    • 將打印公司名稱, 前后還加了delay

    • timer 初始化

    • dw_init --- I2C 設(shè)置

    • 驗證時鐘來源 (來自wifi還是DECT)

    • LCD初始化

    • 鍵盤初始化

    • Flash 初始化 (空函數(shù))

    • 網(wǎng)卡初始化 (其中有個udelay(1000) 1ms的delay )

    1. NOR FLASH 初始化

    display_flash_config (打印Flash大?。?/p>

    1. nand 初始化 (將scan整個nand chip,建立 bbt table)

    1. env_relocate 環(huán)境變量重新定位到內(nèi)存中

    1. 得到IP 地址和網(wǎng)卡 MAC地址

    1. devices_init

    1. 中斷enable

    然后: start_armboot --> main_loop

    1.3 main_loop在 common/main.c中

    getenv("bootdelay") ? ?--> 循環(huán) readline ? ? ? ?run_command

    2. 第二步: Kernel

    2.1 Kernel自解壓

    arch\arm\boot\compressed\head.S中調(diào)用decompress_kernel(misc.c),完了打印出"done,booting the kernel", 然后根據(jù)arch_id = 多少, 打印出 arch_id

    2.2

    在arch\arm\kernel\head.S中

    • check cpu 以及 machine ID

    • build the initial 頁表

    • _switch_data (arm\kernel\head_common.s中) 將process id存入process_id變量中

    • start_kernel

    2.3 start_kernel

    1. 打印Linux version information

    1. call setup_arch,(它將打印cpu特定的信息,

    machine look_machine_type -> arm\tools\mach_types look_processor_type --> .proc.info.init. -->arm\mm\proc_arm926.S

    在/arm\mach_xx\xx.c中,有MACHINE_START(….)

    1. 打印commnad_line

    1. 初始化

    vfs_caches_init

    虛擬文件系統(tǒng)VFS初始化,主要初始化dentry等,它將調(diào)用 mnt_init. 而mnt_init將調(diào)用init_rootfs,注冊rootfs文件系統(tǒng),init_mount_tree()創(chuàng)建rootfs文件系統(tǒng),會把rootfs掛載到/目錄.

    1. rest_init

    啟動init kernel thread

    在init 線程中:

    1. populate_rootfs()

    函數(shù)負責加載initramfs.

    我們的系統(tǒng)沒有定義CONFIG_BLK_DEV_INITRD,因此populate_rootfs什么也沒做

    1. do_basic_setup

    -->driver_init()->platform_bus_init()->…初始化platform bus(虛擬總線)

    這樣以后設(shè)備向內(nèi)核注冊的時候platform_device_register()->platform_device_add()->…內(nèi)核把設(shè)備掛在虛擬的platform bus下,

    驅(qū)動注冊的時候 platform_driver_register()->driver_register()->bus_add_driver()->driver_attach()->bus_for_each_dev() 對每個掛在虛擬的platform bus的設(shè)備作 __driver_attach()->driver_probe_device()->drv->bus->match()==platform_match()->比較strncmp(pdev->name, drv->name, BUS_ID_SIZE),如果相符就調(diào)用platform_drv_probe()->driver->probe(),如果probe成 功則綁定該設(shè)備到該驅(qū)動.

    好象聲卡怎么先注冊驅(qū)動,再注冊設(shè)備呢?反了?

    -->do_initcalls

    而do_initcalls將調(diào)用__initcall_start到__initcall_end中的所有函數(shù)

    __initcall_start和__initcall_end定義在arch/arm/kernel/vmlinux.lds.S中

    它是這樣定義的:

    __initcall_start = .; *(.initcall1.init) *(.initcall2.init) *(.initcall3.init) *(.initcall4.init) *(.initcall5.init) *(.initcall6.init) *(.initcall7.init) __initcall_end = .;

    而在include/linux/init.h中

    #define core_initcall(fn) __define_initcall("1",fn) #define postcore_initcall(fn) __define_initcall("2",fn) #define arch_initcall(fn) __define_initcall("3",fn) #define subsys_initcall(fn) __define_initcall("4",fn) #define fs_initcall(fn) __define_initcall("5",fn) #define device_initcall(fn) __define_initcall("6",fn) #define late_initcall(fn) __define_initcall("7",fn)

    其中

    #define __define_initcall(level,fn) \ static initcall_t __initcall_##fn __attribute_used__ \ __attribute__((__section__(".initcall" level ".init"))) = fn

    這說明core_initcall宏的作用是將函數(shù)指針(注意不是函數(shù)體本身)將放在.initcall1.init section中, 而device_initcall宏將函數(shù)指針將放在.initcall6.init section中.

    函數(shù)本身用_init標識,在include/linux/init.h中

    #define __init __attribute__ ((__section__ (".init.text")))

    這些_init函數(shù)將放在.init.text這個區(qū)段內(nèi).函數(shù)的擺放順序是和鏈接的順序有關(guān)的,是不確定的。

    因此函數(shù)的調(diào)用順序是:

    core_initcall postcore_initcall 如amba_init arch_init 如 subsys_initcall fs_initcall device_initcall ---> module_init late_initcall

    先調(diào)用core_initcall區(qū)段中的函數(shù),最后調(diào)用late_initcall中的函數(shù),而對于上述7個區(qū)段中每個區(qū)段中的函數(shù)指針,由于其擺放順序和鏈接的順序有關(guān)的,是不確定的,因此其調(diào)用順序也是不確定的.

    1. rootfs 加載

    prepare_namespace 掛載真正的根文件系統(tǒng),

    在do_mounts.c中:

    c static int __init root_dev_setup(char *line) { strlcpy(saved_root_name, line, sizeof(saved_root_name)); return 1; } __setup("root=", root_dev_setup);

    也就是說: 在bootargs中root=/dev/nfs rw 或者 root=/dev/mtdblock4等將傳入saved_root_name.

    c void __init prepare_namespace(void) { int is_floppy; mount_devfs(); if (root_delay) { printk(KERN_INFO "Waiting %dsec before mounting root device...\n", root_delay); ssleep(root_delay); } md_run_setup(); if (saved_root_name[0]) { root_device_name = saved_root_name; //保存在root_device_name中 ROOT_DEV = name_to_dev_t(root_device_name); //在root_dev.h中定義了Root_NFS,Root_RAM0等結(jié)點號 if (strncmp(root_device_name, "/dev/", 5) == 0) root_device_name += 5; } is_floppy = MAJOR(ROOT_DEV) == FLOPPY_MAJOR; if (initrd_load()) goto out; if (is_floppy && rd_doload && rd_load_disk(0)) ROOT_DEV = Root_RAM0; mount_root(); //加載rootfs out: umount_devfs("/dev"); sys_mount(".", "/", NULL, MS_MOVE, NULL); sys_chroot("."); security_sb_post_mountroot(); mount_devfs_fs (); }

    1. yaffs2_read_super被調(diào)用來建立文件系統(tǒng), 它scan所有的block

    1. free_initmem

    釋放init內(nèi)存

    1. 打開/dev/console

    失敗則會打印:

    printk(KERN_WARNING "Warning: unable to open an initial console.\n");

    1. 判斷是否有execute_command,這個參數(shù)是在uboot參數(shù)的bootargs中init=xxx ,如果定義了的話則執(zhí)行 run_init_process(execute_command).

    可以通過這種方法實現(xiàn)自己的init process,

    或者可以init=/linuxrc, 這樣執(zhí)行l(wèi)inuxrc

    1. 如果沒有execute_command, init kernel線程缺省的也是最后的步驟是:

    run_init_process("/sbin/init"); run_init_process("/etc/init"); run_init_process("/bin/init"); run_init_process("/bin/sh");

    如果/sbin/init沒有, 則執(zhí)行/etc/init.

    /etc/init沒有則執(zhí)行/bin/init, 如果這四者都沒有, 則Linux打印

    panic("No init found. Try passing init= option to kernel.");

    3. 第三步: Init Process

    run_init_process也就是調(diào)用execve, 這樣就啟動了init process

    上面的/sbin/init,/etc/init,/bin/init,/bin/sh這四者都指向busybox, 但對于/bin/sh則只是打開shell, 然后等待用戶命令.

    而對于/sbin/init ,將分析/etc/inittab.

    在/etc/inittab中

    1. id:5:initdefault: 缺省的runlevel x

    1. si::sysinit:/etc/init.d/rcS

    執(zhí)行 rcS腳本

    1. l5:5:wait:/etc/init.d/rc 5

    1. S:2345:respawn:/sbin/getty 38400 ttyDW0

    getty提示用戶輸入username, 然后調(diào)用login, login的參數(shù)為username, 登錄后啟動了shell

    如果修改為 /bin/sh 則直接啟動shell, 此時你可以輸入命令 比如ls

    在/etc/init.d/rcS中

    1. mount proc 文件系統(tǒng)

    1. /etc/default/rcS (設(shè)置一些參數(shù))

    1. exec /etc/init.d/rc S

    執(zhí)行 /etc/init.d/rc S --> 這樣將執(zhí)行/etc/rcS.d中以S開頭的腳本

    S00psplash.sh psplash S02banner.sh make node /dev/tty S03sysfs.sh mount sysfs S03udev 啟動udev S06alignment.sh 為什么為3? S10checkroot.sh 讀取fatab ,mount 這些文件系統(tǒng) S20modutils.sh 加載module S35mountall.sh 不做什么事情 S37populate-volatile.sh S38devpts.sh mount devpts File System S39hostname.sh set hostname to /etc/hostname S40networking ifup -a to up the lo interface S45mountnfs.sh read /etc/fstab to whether NFS exists and then mount the NFS S55bootmisc.sh 調(diào)用/etc/init.d/hwclock.sh去設(shè)置時間,日期等 S60ldconfig.sh ldconfig建立庫的路徑

    l5:5:wait:/etc/init.d/rc 5將執(zhí)行 /etc/rc5.d/ 依次為:

    S00qpe 啟動qpe S02dbus-1 D_BUS dameon S10dropbear SSH service S20cron 自動執(zhí)行指定任務的程序 cron , in etc/crontab , ntpd will run to get the NTP time S20ntpd Not used , should delete S20syslog run /sbin/klogd S39wifiinit.sh wifi init and calibration S70regaccess mknod regaccess.ko S99rmnologin.sh do nothing since DELAYLOGIN = no in /etc/default/rcS

    整個系統(tǒng)啟動后 ,將有 25 個進程 :其中12個內(nèi)核的進程 ,13個用戶進程

    ??

    4. 優(yōu)化:

    uboot:

    1. setenv bootcmd1 "nand read.jffs2 0x62000000 kernel 0x180000 ; bootm 62000000"

    這樣 load內(nèi)核的時候 從以前0x300000的3M->1.5M 省1S

    2.setenv bootdelay 1 從2變?yōu)? 加上CONFIG_ZERO_BOOTDELAY_CHECK

    1. quiet=1

    bootargs=root=/dev/mtdblock4 rootfstype=yaffs2 console=ttyDW0 mem=64M mtdparts=dwnand:3m(kernel),3m(splash),64m(rootfs),-(userdata);dwflash.0:384k(u-boot),128k(u-boot_env) quiet

    加上quiet 省不到1S

    1. 啟動的時候不掃描整個芯片的壞塊, 因為uboot只會用到kernel和splash區(qū),只需要檢驗這兩個區(qū)的壞塊。

    可以省不到 0.2s ,沒什么明顯的改進

    1. 將環(huán)境變量verify設(shè)置為n, 這樣load kernel后, 不會去計算校驗kernel image的checksum

    1. 開始打印公司 這些可以去掉 ,在這里還有delay ,以及其他的一些不必要的打印 ,一起去掉

    1. 修改memcpy函數(shù) 在./lib_generic/string.c下:

    (在linux 中,arm的memcpy有優(yōu)化的版本 , 在/arch/arm/lib/memcpy.S中)

    下面2個建議,沒試過:

    1. 在環(huán)境變量區(qū)的末尾, 存有CRC,啟動的時候會校驗CRC ,去掉可以省一些時間

    1. 把一些驅(qū)動的初始化在正常啟動的時候不執(zhí)行,當用戶按了鍵,進入uboot命令模式的時候執(zhí)行

    1. 修改SDRAM控制器時序

    Kernel:

    啟動時間 有兩種方法 :

    1. 在u-boot的 bootargs 中加上參數(shù) time

    2. 在內(nèi)核的 kernel hacking 中選擇 PRINTK_TIME

    方法2的好處是可以得到內(nèi)核在解析command_line前所有信息的時間, 而之前會有:打印linux 版本信息,CPU D cache , I cache 等等 。。。

    啟動完后用 :

    dmesg -s 131072 > ktime

    然后用:

    /usr/src/linux-x.xx.xx/s/show_delta ktime > dtime

    這樣得到啟動內(nèi)核時間的報告

    1. 修改Nand驅(qū)動 提高讀速度

    2. 從 JFFS2 換成 yaffs

    3. kernel變?yōu)榉菈嚎s的image, 但這樣的話內(nèi)核變大了, 從NAND中搬運內(nèi)核的時間將變長, 所以需要測試是否使得時間變短

    建議:

    1. 把delay的calibration去掉

      上面改動后基本上8s從開機到 Freeing init memory

    Application:

    1. udev 啟動很花時間

    2. 安排好啟動順序。


    Linux嵌入式啟動以及優(yōu)化的評論 (共 條)

    分享到微博請遵守國家法律
    凤台县| 芦山县| 天台县| 庄河市| 铜川市| 定襄县| 蓝山县| 长子县| 许昌县| 江川县| 铅山县| 湖南省| 彭阳县| 古田县| 柘荣县| 和平县| 济源市| 城口县| 荣成市| 玉环县| 阿巴嘎旗| 恩施市| 鲁山县| 昭苏县| 波密县| 英山县| 通许县| 崇信县| 东乌珠穆沁旗| 霍州市| 辉县市| 大荔县| 平潭县| 揭阳市| 基隆市| 肃南| 新干县| 砚山县| 时尚| 英吉沙县| 亳州市|