【嵌入式开发】 Linux Kernel 下载 配置 编译 安装 及 驱动简介(二)

4. 内核编译



简单命令 : 直接使用 make 命令进行编译;




过滤命令行输入 : 将 make 编译信息输出到文件中, 警告 和 错误 会输出到命令行;


-- 输出发到文件 : 使用 make > ../make_log 命令, 会自动将无关的日志存放到 make_log 文件中, 错误和警告提示会显示出来;


-- 输出到黑洞 : 使用 make > /dev/null 命令, 输出的日志信息会自动;




并行编译 : make 可以进行多作业并行编译, 在多核的机器上这样进行编译效率很高;


-- 并行编译弊端 : 如果 Makefile 出现依赖错误, 并行编译会报错, 使用 单作业 make 可以编译通过, 内核无此类错误, 可以并行编译;


-- 使用命令 : make -jn , 其中 n 是一个整数, 例如 make -j4 就是开起 4 个作业同时编译;


-- 多作业编译并输出文件 : make -j4 > make_log, 开启四个作业同时进行编译, 并将编译输出信息存放到 make_log 中;




编译过程 : 使用 make -j4 > make_log 命令, 省略了一部份;



octopus@octopus:~/uplooking/kernel/linux-2.6.32.63$ make -j4 > make_log
scripts/mod/modpost.c: In function ‘get_markers’:
scripts/mod/modpost.c:1562: warning: ignoring return value of ‘asprintf’, declared with attribute warn_unused_result
scripts/mod/modpost.c: In function ‘add_marker’:
scripts/mod/modpost.c:1982: warning: ignoring return value of ‘asprintf’, declared with attribute warn_unused_result
scripts/kallsyms.c: In function ‘read_symbol’:
scripts/kallsyms.c:112: warning: ignoring return value of ‘fgets’, declared with attribute warn_unused_result
WARNING: modpost: Found 2 section mismatch(es).
To see full details build your kernel with:
'make CONFIG_DEBUG_SECTION_MISMATCH=y'







5. 安装内核





(1) 安装内核



编译和安装系统 : 本人用的 Ubuntu 系统, 安装内核比较麻烦, 这里只列出 Redhat 系统安装内核的流程, 即 在 ubuntu 上编译, 在 Redhat 上安装;



a. 内核文件位置



文件位置 : 编译好的内核位于 arch 对应体系结构的目录下的 boot 目录下;


-- 使用默认编译好的配置内核地址 : arch/i386/boot ;



octopus@octopus:~/uplooking/kernel/linux-2.6.32.63/arch/i386/boot$ ls

bzImage


b. 拷贝内核文件



/boot 目录 : 内核文件存放在 /boot 目录下, 下面列举除了 boot 目录下的文件;



[root@ip28 boot]# ls

config-2.6.18-164.el5  initrd-2.6.18-164.el5.img  System.map-2.6.18-164.el5

grub                   symvers-2.6.18-164.el5.gz  vmlinuz-2.6.18-164.el5

-- 内核文件 : 其中的 vmlinuz-2.6.18-164.el5 就是内核, 将 bzImage 文件改名为 vmlinuz-2.6.32.63 即可, 后面是版本号, 该版本是 Linux 内核的 2.6.32.63 版本;



c. 修改 grub.conf 文件



文件位置 : 每个系统中该文件的位置不同, 在 redhat 中内核配置文件是 /etc/grub.conf 文件;



# grub.conf generated by anaconda
#
# Note that you do not have to rerun grub after making changes to this file
# NOTICE:  You do not have a /boot partition.  This means that
#          all kernel and initrd paths are relative to /, eg.
#          root (hd0,0)
#          kernel /boot/vmlinuz-version ro root=/dev/hda1
#          initrd /boot/initrd-version.img
#boot=/dev/hda
default=0
timeout=5
splashimage=(hd0,0)/boot/grub/splash.xpm.gz
hiddenmenu
title Red Hat Enterprise Linux Server (2.6.18-164.el5)
    root (hd0,0)
    kernel /boot/vmlinuz-2.6.18-164.el5 ro root=LABEL=/ console=tty0 console=ttyS0,9600n8 ide0=noprobe clock=tsccount
    initrd /boot/initrd-2.6.18-164.el5.img

(2) 安装模块



模块安装介绍 : modules 安装可以自动完成, 首先切入到 root 权限下, 执行 make modules_install 命令, 即可将编译好的模块安装到 /lib/modules 目录下;






(3) 符号对照表



生成目录 : 编译的是时候在内核源码根目录生成一个 System.map 的对照表, 这份对照表是 内核中的符号 和 地址对应起来, 比如函数的地址, 变量的地址等;



00000000 A VDSO32_PRELINK
00000040 A VDSO32_vsyscall_eh_frame_size
000001d5 A kexec_control_code_size
00000400 A VDSO32_sigreturn
0000040c A VDSO32_rt_sigreturn
00000414 A VDSO32_vsyscall
00000424 A VDSO32_SYSENTER_RETURN
01000000 A phys_startup_32
c1000000 T _text
c1000000 T startup_32
c1000054 t default_entry



三. 内核开发





1. 关于头文件



不能使用标准库 : 内核中是不存在 标准库 和 标准头文件的;


-- 没有引用 : 标准库实质上就是调用内核中的东西, 此时内核还不存在;


-- 性能考虑 : 链接执行标准库性能很低, 对于内核来说降低性能是致命的;




使用头文件 : 内核中实现了大部分 C 函数, 只要将相关的头文件引入即可;


-- 基本头文件 : 这类头文件在 include 目录下的, 如 字符串处理相关的头文件路径是 include/linux/string.h, 引入 <linux/string.h> 即可使用里面的方法;


-- 体系结构相关头文件 : 这类头文件在 arch/architecture/include/asm 目录下, 如 x86 相关的体系结构头文件在 arch/x86/include/asm 目录下, 引入的时候引入 <asm/xxx.h> 即可;






2. 内核 C 语言标准



内核C语言符合的标准 : 内核不符合 ANSI C 标准, 符合 ISO C99 和 GNU C 标准;






(1) 内联函数



内联函数介绍 : 函数会在其调用的位置展开, 没有函数调用和返回的开销;


-- 函数调用开销 : 函数调用的时候需要 寄存器的存储和恢复;


-- 内联函数优点 : 编译器会把函数代码和其本身一起优化;


-- 内联函数缺点 : 代码展开后会占用跟多空间, 占用更多的指令缓存, 内核中把对时间要求高 并且 本身比较短 的函数定义为内联函数;




内联函数定义 : 使用 sttaic inline 限定函数;


-- 示例 : static inline void hello(char * argv);


-- 使用 static 限制 : 编译时不会为其创建函数体;






(2) 内联汇编



嵌入汇编 : 使用 asm() 指令嵌入汇编;


-- 前提 : C 语言中嵌入的汇编需要与体系结构对应才可以, 例如在笔记本上执行的 C 程序必须嵌入 x86 的汇编才可以, 不能使用 arm 汇编;


-- 示例 : asm volatile("mov %1, %0\n\t" : "=r"(output) : "r"(input));


-- 使用场景 : 在内核中 体系结构底层代码 或者 对时间要求严格的地方使用 汇编语言实现;






(3) 分支声明



选择优化 : gcc 编译器会根据分支选择优化, 条件很少出现的代码不进行优化, 条件很多时候出现的时候才优化;


-- 示例 : likely() 和 unlikely();




标记分支示例 : if(istrue){} 是条件;


-- 标记为很少发生的情况 : if(unlikely(istrue)){};


-- 标记为经常发生的情况 : if(likely(istrue)){};






3. 关于内存保护机制



用户空间的内存保护 : 当用户程序出现非法内存访问, 内核就会发送错误信号, 杀死整个进程;




内核出现内存非法访问 : 会导致整个内核 oops, 这种错误很常见, 而且很难排查;


-- 技巧 : 内核中的内存不分页, 用掉哪些内存都要记住, 否则会出现错误;






4. 关于浮点数



用户空间使用浮点数 : 用户进行浮点运算的时候, 内核会进行从整数到浮点数操作的模式转换;




内核空间使用浮点数 : 内核不能完美支持浮点操作, 其本身不能陷入, 如果非要使用的话 需要人工操作浮点寄存器, 过程相当繁琐;






5. 栈的大小



用户栈 : 用户空间的栈很大, 可以存放巨大的结构体等大数据;




内核栈 : 内核的栈很小, 32位 x86 体系结构上 是 8KB, 64 位的是 16 KB;






6. 同步和并发



内核的同步并发机制 : 内核经常产生竞争, 需要并发访问共享数据, 需要有同步机制保证不出现竞争条件;


-- Linux系统抢占属性 : Linux 是抢占式多任务操作系统, 内核必须和任务同步, 内核中一段代码可能会被另一段抢占, 几段代码可能访问同一资源, 因此需要 自旋锁 信号量等机制保护;


-- Linux 系统对称多处理(SMP)属性 : 多个处理器执行内核代码 可能会导致访问同一资源, 这里也使用 自旋锁 和 信号量机制;


-- 中断保护 : 中断可能在一段代码访问资源的时候到来, 中断处理程序又可能访问同一资源, 又出现多个代码访问同一资源;






四. 驱动简介





1. 驱动和模块



驱动概念 :


-- 实现位置 : 驱动在内核中实现, 一般在内核中的 driver 目录下;


-- 驱动作用 : 驱动的作用是提供 机制(实现什么功能), 不是提供 策略(用户如何使用这种功能), 编写访问硬件的内核代码的时候, 不要给用户强加策略;




可加载模块 : 可以在内核运行时加载的代码叫模块, 每个模块由目标代码组成, 可以在内核运行的时候动态连接到内核中;


-- 机制特点 : 这是一种可以动态加载功能单元来扩展功能的机制, 类似与插件;


-- 与驱动关系 : 内核为每个驱动创建不同的模块, 实现驱动的扩展性;






2. 驱动分类



驱动分类 : 驱动分为 字符设备, 块设备 和  网络设备;




a. 字符设备



字符设备概念 : 这种设备可以像字节流一样访问, 字符设备驱动程序实现这种特性;


-- 字符设备访问方式 : 通过 /dev 下的字符设备文件来访问, 该驱动程序需要实现 open close rad write 系统调用;


-- 顺序访问 : 大部分字符设备只能顺序访问数据, 指针不能前后移动, 如 串口驱动, 只能顺序读写数据;


-- 灵活访问 : 有些字符设备可以移动指针访问, 如 framebuffer 设备;




b. 块设备



块设备概念 :  这种设备按照文件块访问数据, 如 一块为 512 字节, 那么会按照 512 字节访问设备;


-- 块设备访问方式 : 通过 /dev 下面的文件系统访问;


-- 与字符设备区别 : 内核内部管理数据的方式不同, 内核和驱动的接口不同;


-- 访问方式 : 块设备除了提供与字符设备相同的接口之外, 还要提供专门面向块设备的接口;


-- 块设备与文件系统关系 : 块设备支持文件系统挂载, 其能够容纳文件系统, 应用程序可以通过文件系统访问块设备;




c. 网络设备



网络设备概念 : 网络设备不在 /dev 下表示, 是通过单独的网络接口来代表这种设备的;


-- 访问方式 : 网络设备需要通过网络接口进行访问;


-- 实现方式 : 内核与网络设备通信 与 字符设备 和 块设备都不同, 是调用一套数据传输相关的 api 完成这种操作;






3. 设备文件和设备驱动



设备文件简介 :


-- 概念 : 文件系统上的一个节点, 是一种特殊的文件, 每个设备文件代表了一个设备;


-- 位置 : 设备文件一般在 /dev 目录下;


-- 创建命令 : 使用 mknod 命令进行创建;


-- 设备接口 : 设备文件是应用程序和设备驱动之间的接口, 应用程序通过操作设备文件使用设备驱动的功能;


-- 与字符和块设备对应 : 字符设备 和 块设备 必定与一个设备文件对应;




驱动位置 :


-- 内核源码中的位置 : 内核中的驱动在根目录下的 drivers 目录下;



octopus@octopus:~/uplooking/kernel/linux-2.6.32.63/drivers$ pwd
/home/octopus/uplooking/kernel/linux-2.6.32.63/drivers
octopus@octopus:~/uplooking/kernel/linux-2.6.32.63/drivers$ ls
accessibility  built-in.o   dio       hwmon       Kconfig    message        oprofile  ps3        sh         usb
acpi           cdrom        dma       i2c         leds       mfd            parisc    rapidio    sn         uwb
amba           char         edac      ide         lguest     misc           parport   regulator  spi        video
ata            clocksource  eisa      idle        macintosh  mmc            pci       rtc        ssb        virtio
atm            connector    firewire  ieee1394    Makefile   modules.order  pcmcia    s390       staging    vlynq
auxdisplay     cpufreq      firmware  ieee802154  mca        mtd            platform  sbus       tc         w1
base           cpuidle      gpio      infiniband  md         net            pnp       scsi       telephony  watchdog
block          crypto       gpu       input       media      nubus          power     serial     thermal    xen
bluetooth      dca          hid       isdn        memstick   of             pps       sfi        uio        zorro

-- Linunx系统中的位置 : /usr/src/linux-headers-3.11.0-15/drivers ;



root@octopus:/usr/src/linux-headers-3.11.0-15/drivers# ls
accessibility  bus          devfreq   gpu         infiniband  mailbox   mtd      pinctrl    remoteproc  spi      vfio      zorro
acpi           cdrom        dio       hid         input       Makefile  net      platform   reset       ssb      vhost
amba           char         dma       hsi         iommu       md        nfc      pnp        rpmsg       staging  video
ata            clk          edac      hv          ipack       media     ntb      power      rtc         target   virt
atm            clocksource  eisa      hwmon       irqchip     memory    nubus    pps        s390        tc       virtio
auxdisplay     connector    extcon    hwspinlock  isdn        memstick  of       ps3        sbus        thermal  vlynq
base           cpufreq      firewire  i2c         Kconfig     message   parisc   ptp        scsi        tty      vme
bcma           cpuidle      firmware  ide         leds        mfd       parport  pwm        sfi         uio      w1
block          crypto       fmc       idle        lguest      misc      pci      rapidio    sh          usb      watchdog
bluetooth      dca          gpio      iio         macintosh   mmc       pcmcia   regulator  sn          uwb      xen
root@octopus:/usr/src/linux-headers-3.11.0-15/drivers# pwd
/usr/src/linux-headers-3.11.0-15/drivers

常用的驱动简介 :

-- block : 块设备驱动;


-- char : 虚拟中断驱动;


-- serial : 串口驱动;


-- net : 网络设备驱动;


-- vedio : framebuffer 设备驱动;


-- sound : 声卡驱动;



系统中设备驱动信息 : 使用  dmesg 命令查看;


octopus@octopus:~/uplooking/kernel/linux-2.6.32.63/drivers$ dmesg
[    0.000000] Initializing cgroup subsys cpuset
[    0.000000] Initializing cgroup subsys cpu
[    0.000000] Initializing cgroup subsys cpuacct
[    0.000000] Linux version 3.11.0-15-generic (buildd@akateko) (gcc version 4.6.3 (Ubuntu/Linaro 4.6.3-1ubuntu5) ) #25~precise1-Ubuntu SMP Thu Jan 30 17:42:40 UTC 2014 (Ubuntu 3.11.0-15.25~precise1-generic 3.11.10)
[    0.000000] KERNEL supported cpus:
[    0.000000]   Intel GenuineIntel
[    0.000000]   AMD AuthenticAMD
[    0.000000]   NSC Geode by NSC
[    0.000000]   Cyrix CyrixInstead
[    0.000000]   Centaur CentaurHauls
[    0.000000]   Transmeta GenuineTMx86
上一篇:【Android 系统开发】 Android 系统启动流程简介(一)


下一篇:【IOS 开发】Objective - C 入门 之 数据类型详解(二)