2019-08-19 12:38:47 wulitaotao96 阅读数 76
  • 自学有道-产品经理需要的工具视频教程

    自学,需要一种方法,让学习变成一件简单的事儿;产品经理的工具,思维导图又叫脑图,能够帮助产品经理进行用户角色以及功能结构梳理等等的工作;而且还可以提高工作效率的同时形成系统的学习和思维的习惯;本课程主要讲解了ProcessOn和XMind两款思维导图工具的使用。

    3913 人正在学习 去看看 孙伟

笔试感觉做的一般般,但是收到面试通知了,很开心...


大概是问答+改错+编程+场景面

1 Linux开机,在启动内核前会先运行什么

2 听说过flash吗 nandflash? norflash?

3 常用的Linux命令,ps后面的参数

4 自旋锁与其他锁的区别

5 用一个char能不能代替锁

6 我可以爬取你的IP吗,MAC地址呢

7 Shell用过吗

8 页的大小,页对应的物理地址连续吗

9 MMU?cache?

10 介绍下项目

改错:

C语言的指针题

编程:只能用C++

链表里出现次数最多的前十个元素

场景:

同事跟你做的是同一个模块,都想提交,如何处理

最后:

有什么问题想问我的吗


面试官人很好,神似丁磊,氛围很轻松,问的问题很有脉络,不是左一下右一下,不会用C++很难受,写出来像伪代码一样

,整体面试体验挺好


第二天接到通知,面试通过了..得好好准备下C++了。。


二面:

讲讲你的项目

linux下多线程与多进程

多核CPU的调度

TCP的稳定性

控制系的?如何评定一个系统的稳定性?PID调参的方法?学的好的科目?

编程多久了,最早大一?讲讲大一项目?用的传感器?用了几个?

你还有什么问我的吗

总共十多分钟....感觉有点凉..

2018-07-30 22:11:38 wangweijundeqq 阅读数 1976
  • 自学有道-产品经理需要的工具视频教程

    自学,需要一种方法,让学习变成一件简单的事儿;产品经理的工具,思维导图又叫脑图,能够帮助产品经理进行用户角色以及功能结构梳理等等的工作;而且还可以提高工作效率的同时形成系统的学习和思维的习惯;本课程主要讲解了ProcessOn和XMind两款思维导图工具的使用。

    3913 人正在学习 去看看 孙伟

接上一节:七.linux开发之uboot移植(七)——uboot源码分析2-启动第二阶段之start_armboot函数分析1

还是老规矩:有道云笔记地址:http://note.youdao.com/noteshare?id=fef34a7ee3bcdfdf7638db662564ae66&sub=69B7DF889DAF4064BF8D7852C69EE452

一.uboot启动第二阶段之start_armboot函数解析9

1、CFG_NO_FLASH

clipboard.png

(1)虽然NandFlash和NorFlash都是Flash,但是一般NandFlash会简称为Nand而不是Flash,一般讲Flash都是指的Norflash。这里2行代码是Norflash相关的。

(2)flash_init执行的是开发板中对应的NorFlash的初始化、display_flash_config打印的也是NorFlash的配置信息(Flash:
8 MB就是这里打印出来的)。

但是实际上X210中是没有Norflash的。所以着两行代码是可以去掉的(实验证明:加上CONFIG_NOFLASH宏之后编译出错,说明代码移植的不好,那个文件的包含没有被这个宏控制。于是乎移植的人就直接放这没管。)

clipboard.png

CONFIG_VFD和CONFIG_LCD是显示相关的,这个是uboot中自带的LCD显示的软件架构。但是实际上我们用LCD而没有使用uboot中设置的这套软件架构,我们自己在后面自己添加了一个LCD显示的部分。

2、mem_malloc_init

clipboard.png

(1)mem_malloc_init函数用来初始化uboot的堆管理器。

(2)uboot中自己维护了一段堆内存,肯定自己就有一套代码来管理这个堆内存。有了这些东西uboot中你也可以malloc、free这套机制来申请内存和释放内存。我们在DDR内存中给堆预留了896KB的内存。

二.uboot启动第二阶段之start_armboot函数解析10

1、开发板独有初始化:mmc初始化

clipboard.png

(1)从536到768行为开发板独有的初始化。意思是三星用一套uboot同时满足了好多个系列型号的开发板,然后在这里把不同开发板自己独有的一些初始化写到了这里。用#if条件编译配合CONFIG_xxx宏来选定特定的开发板。

(2)X210相关的配置在599行到632行。

clipboard.png

(3)mmc_initialize看名字就应该是MMC相关的一些基础的初始化,其实就是用来初始化SoC内部的SD/MMC控制器的。函数在uboot/drivers/mmc/mmc.c里。

clipboard.png

(4)uboot中对硬件的操作(譬如网卡、SD卡···)都是借用的linux内核中的驱动来实现的,uboot根目录底下有个drivers文件夹,这里面放的全都是从linux内核中移植过来的各种驱动源文件。

(5)mmc_initialize是具体硬件架构无关的一个MMC初始化函数,所有的使用了这套架构的代码都掉用这个函数来完成MMC的初始化。mmc_initialize中再调用board_mmc_init和cpu_mmc_init来完成具体的硬件的MMC控制器初始化工作。

clipboard.png

(6)cpu_mmc_init在uboot/cpu/s5pc11x/cpu.c中,这里面又间接的调用了drivers/mmc/s3c_mmcxxx.c中的驱动代码来初始化硬件MMC控制器。这里面分层很多,分层的思想一定要有,否则完全就糊涂了。

clipboard.png

三.uboot启动第二阶段之start_armboot函数解析11

1、env_relocate

clipboard.png

(1)env_relocate是环境变量的重定位,完成从SD卡中将环境变量读取到DDR中的任务。

(2)环境变量到底从哪里来?SD卡中有一些(8个)独立的扇区作为环境变量存储区域的。但是我们烧录/部署系统时,我们只是烧录了uboot分区、kernel分区和rootfs分区,根本不曾烧录env分区。所以当我们烧录完系统第一次启动时ENV分区是空的,本次启动uboot尝试去SD卡的ENV分区读取环境变量时失败(读取回来后进行CRC校验时失败),我们uboot选择从uboot内部代码中设置的一套默认的环境变量出发来使用(这就是默认环境变量);这套默认的环境变量在本次运行时会被读取到DDR中的环境变量中,然后被写入(也可能是你saveenv时写入,也可能是uboot设计了第一次读取默认环境变量后就写入)SD卡的ENV分区。然后下次再次开机时uboot就会从SD卡的ENV分区读取环境变量到DDR中,这次读取就不会失败了。

(3)真正的从SD卡到DDR中重定位ENV的代码是在env_relocate_spec内部的movi_read_env完成的。

clipboard.png

四.uboot启动第二阶段之start_armboot函数解析12

1、IP地址、MAC地址的确定

clipboard.png

gd->bd->bi_ip_addr gd数据结构赋值

gd->bd->bi_enetaddr gd数据结构赋值

clipboard.png

clipboard.png

(1)开发板的IP地址是在gd->bd中维护的,来源于环境变量ipaddr。getenv函数用来获取字符串格式的IP地址,然后用string_to_ip将字符串格式的IP地址转成字符串格式的点分十进制格式。

(2)IP地址由4个0-255之间的数字组成,因此一个IP地址在程序中最简单的存储方法就是一个unsigend
int。但是人类容易看懂的并不是这种类型,而是点分十进制类型(192.168.1.2)。这两种类型可以互相转换。

2、devices_init

clipboard.png

(1)devices_init看名字就是设备的初始化。这里的设备指的就是开发板上的硬件设备。放在这里初始化的设备都是驱动设备,这个函数本来就是从驱动框架中衍生出来的。uboot中很多设备的驱动是直接移植linux内核的(譬如网卡、SD卡),linux内核中的驱动都有相应的设备初始化函数。linux内核在启动过程中就有一个devices_init(名字不一定完全对,但是差不多),作用就是集中执行各种硬件驱动的init函数。

(2)uboot的这个函数其实就是从linux内核中移植过来的,它的作用也是去执行所有的从linux内核中继承来的那些硬件驱动的初始化函数。

3、jumptable_init

clipboard.png

(1)jumptable跳转表,本身是一个函数指针数组,里面记录了很多函数的函数名。看这阵势是要实现一个函数指针到具体函数的映射关系,将来通过跳转表中的函数指针就可以执行具体的函数。这个其实就是在用C语言实现面向对象编程。在linux内核中有很多这种技巧。

(2)通过分析发现跳转表只是被赋值从未被引用,因此跳转表在uboot中根本就没使用。

五.uboot启动第二阶段之start_armboot函数解析13

1、console_init_r

clipboard.png

(1)console_init_f是控制台的第一阶段初始化,console_init_r是第二阶段初始化。实际上第一阶段初始化并没有实质性工作,第二阶段初始化才进行了实质性工作。

(2)console_init_r就是console的纯软件架构方面的初始化(说白了就是去给console相关的数据结构中填充相应的值),所以属于纯软件配置类型的初始化。

(3)uboot的console实际上并没有干有意义的转化,它就是直接调用的串口通信的函数。所以用不用console实际并没有什么分别。(在linux内console就可以提供缓冲机制等不用console不能实现的东西)。

.2、enable_interrupts

clipboard.png

(1)看名字应该是中断初始化代码。这里指的是CPSR中总中断标志位的使能。

(2)因为我们uboot中没有使用中断,因为没有定义CONFIG_USE_IRQ宏,因此我们这里这个函数是个空壳子。

(3)uboot中经常出现一种情况就是根据一个宏是否定义了来条件编译决定是否调用一个函数内部的代码。uboot中有2种解决方案来处理这种情况:方案一:在调用函数处使用条件编译,然后函数体实际完全提供代码。方案二:在调用函数处直接调用,然后在函数体处提供2个函数体,一个是有实体的一个是空壳子,用宏定义条件编译来决定实际编译时编译哪个函数进去。

3、loadaddr、bootfile两个环境变量

clipboard.png

(1)这两个环境变量都是内核启动有关的,在启动linux内核时会参考这两个环境变量的值。

4、board_late_init

clipboard.png

(1)看名字这个函数就是开发板级别的一些初始化里比较晚的了,就是晚期初始化。所以晚期就是前面该初始化的都初始化过了,剩下的一些必须放在后面初始化的就在这里了。侧面说明了开发板级别的硬件软件初始化告一段落了。

(2)对于X210来说,这个函数是空的。

六.uboot启动第二阶段之start_armboot函数解析14

1、eth_initialize

clipboard.png

(1)看名字应该是网卡相关的初始化。这里不是SoC与网卡芯片连接时SoC这边的初始化,而是网卡芯片本身的一些初始化。

(2)对于X210(DM9000)来说,这个函数是空的。X210的网卡初始化在board_init函数中,网卡芯片的初始化在驱动中。

2、x210_preboot_init(LCD和logo显示)

clipboard.png

clipboard.png

(1)x210开发板在启动起来之前的一些初始化,以及LCD屏幕上的logo显示。

clipboard.png

3、check menukey to update from sd

clipboard.png

(1)uboot启动的最后阶段设计了一个自动更新的功能。就是:我们可以将要升级的镜像放到SD卡的固定目录中,然后开机时在uboot启动的最后阶段检查升级标志(是一个按键。按键中标志为”LEFT”的那个按键,这个按键如果按下则表示update
mode,如果启动时未按下则表示boot mode)。如果进入update
mode则uboot会自动从SD卡中读取镜像文件然后烧录到iNand中;如果进入boot
mode则uboot不执行update,直接启动正常运行。

(2)这种机制能够帮助我们快速烧录系统,常用于量产时用SD卡进行系统烧录部署。

4、死循环、

clipboard.png

(1)解析器(即接收命令,解析命令,执行命令)

(2)开机倒数自动执行

(3)命令补全

到这里为止,start_armboot函数就解析完了!!

七.uboot启动之第二阶段流程图详细总结

1、启动流程回顾、重点函数标出

(1)第二阶段主要是对开发板级别的硬件、软件数据结构进行初始化。

(2)init_sequence函数指针数组里面的函数元素在上节最后已经总结,这里不再赘述。,一下是接着往下分析的。

mem_malloc_init函数初始化uboot自己维护的堆管理器的内存

mmc_initialize函数inand/SD卡的SoC控制器和卡的初始化

env_relocate函数环境变量重定位

gd->bd->bi_ip_addr gd数据结构赋值

gd->bd->bi_enetaddrgd数据结构赋值

devices_init函数 空的

jumptable_init函数 不用关注的

console_init_r函数真正的控制台初始化

enable_interrupts函数 空的

loadaddr、bootfile 环境变量读出初始化全局变量

board_late_init 函数 空的

eth_initialize函数 空的

x210_preboot_init函数 LCD初始化和显示logo

check_menu_update_from_sd函数 检查自动更新

main_loop函数主循环

  • 第二阶段详细流程图

这里写图片描述

2、启动过程特征总结

(1)第一阶段为汇编阶段、第二阶段为C阶段

(2)第一阶段在SRAM中、第二阶段在DRAM中

(3)第一阶段注重SoC内部、第二阶段注重SoC外部Board内部(比如网卡,lcd)

八.最后,对两个阶段总结主要任务:

  • stage1 负责的主要任务有:

① 硬件设备初始化包括:关闭 Watchdog、关闭中断、设置 CPU 的速度和时钟频 率、配置 SDRAM 存储控制器及 IO、关闭处理器内部指令/数据 Cache 等;
② 为加载 Bootloader 的 stage2 代码准备 DRAM 空间(这个地址由链接脚本指定 为运行域地址,通常为 RAM 的高端地址)测试内存空间是否有效;
③ 复制 Bootloader 的 stage2 代码到 DRAM 空间中;
④ 设置好堆栈;
⑤ 跳转到 stage2 的 C 函数入口点。 stage2 则是通常用 C 语言来实现,这样可以实现更复杂的功能,而且代码会 具有更好的可读性和可移植性。

.

  • 2.stage2 负责的主要任务有:

① 初始化本阶段要使到的硬件设备(如串口、 Flash 和网卡等);
② 检测系统内存映射;
③ 没有用户干预时将内核映像从 Flash 读到 DRAM 空间中;
④ 为内核设置启动参数;
⑤ 调用内核。**

uboot 调用内核的方法就是直接跳转到内核的第一条指令处。在调 用内核之前下列的条件必须要满足: (所以前面stage1和stage2 已经把这些初始化了)

  • 1.CPU 寄存器的设置

① R0 为 0;
② R1 为机器码;
③ R2 为启动参数,标记列表在 DRAM 中的起始基地址。

  • 2.CPU 工作模式

① 必须禁止中断( IRQ 和 FIQ);
② CPU 必须设置为 SVC 模式。 3.Cache 和 MMU 的设置
① MMU 必须关闭;
② 指令 Cache 可以打开或关闭;
③ 数据 Cache 必须关闭。

2018-09-04 21:52:41 wangweijundeqq 阅读数 2029
  • 自学有道-产品经理需要的工具视频教程

    自学,需要一种方法,让学习变成一件简单的事儿;产品经理的工具,思维导图又叫脑图,能够帮助产品经理进行用户角色以及功能结构梳理等等的工作;而且还可以提高工作效率的同时形成系统的学习和思维的习惯;本课程主要讲解了ProcessOn和XMind两款思维导图工具的使用。

    3913 人正在学习 去看看 孙伟

有道云笔记地址:

详情看这里链接,记录太多,就不一一排版了。

http://note.youdao.com/noteshare?id=e68caf68c259bc3491dd7f8c4ac4bb99&sub=435C54F4A4C444B995888BCC71CDB607

2.6.35.7版本kernel

参考:http://blog.chinaunix.net/uid-26806098-id-3141136.html

一.做好内核分析的准备工作

1、删除无用文件

(1)我们现在分析的是开发板厂商九鼎科技移植好的针对X210开发板的kernel,因此其中一些无用文件已经被删掉了。

2、建立SI工程并解析

建立方法和uboot中是一样的,这里不再赘述。

3、Makefile分析

(1)Makefile中刚开始定义了kernel的内核版本号。这个版本号挺重要(在模块化驱动安装时会需要用到),要注意会查,会改。

(2)在make编译内核时,也可以通过命令行给内核makefile传参(跟uboot配置编译时传参一样)。譬如make O=xxx可以指定不在源代码目录下编译,而到另外一个单独文件夹下编译。这样做的好处就是不污染源代码。

(3)kernel的顶层Makefile中定义了2个变量很重要,一个是ARCH,一个是CROSS_COMPILE。ARCH决定当前配置编译的路径,譬如ARCH = arm的时候,将来在源码目录下去操作的arch/arm目录。CROSS_COMPILE用来指定交叉编译工具链的路径和前缀。

(4)CROSS_COMPILE = xxx和ARCH = xxx和O=xxx这些都可以在make时通过命令行传参的方式传给顶层Makefile。  (所以上图中有个❓,代表如果使用命令行传参方式,则优先使用命令行传参方式,这里的?之后的代码就作废;如果没有传参,则执行这里

所以有时候你会看到别人编译内核时:make O=/tmp/mykernel ARCH=arm CROSS_COMPILE=/usr/local/arm/arm-2009q3/bin/arm-none-linux-gnueabi-

4、内核Makefile体系概述

(1)Kconfig系统

(以下内容转载注明:http://blog.chinaunix.net/uid-26806098-id-3141136.html

http://blog.51cto.com/9291927/category5.html

顶层Makefile

根据不同的平台,对各类target分类并调用相应的规则Makefile生成目标

.config

内核配置文件

arch/$(ARCH)/Makefile

具体平台相关的Makefile

scripts/Makefile.*

通用规则文件,面向所有的Kbuild Makefiles,所起的作用可以从后缀名中得知。

各子目录下的Makefile 文件

由其上层目录的Makefile调用,执行其上层传递下来的命令

顶层Makefile主要是负责完成vmlinux(内核文件)与*.ko(内核模块文件) 的编译

顶层 Makefile读取.config 文件,并根据.config 文件确定访问哪些子目录,并通过递归向下访问子目录的形式完成。顶层Makefile同时根据.config 文件原封不动的包含一个具体架构的Makefile,其名字类似于 arch/$(ARCH)/Makefile。该架构Makefile 向顶层Makefile 提供其架构的特别信息。

    每一个子目录都有一个Makefile 文件,用来执行从其上层目录传递下来的命令。子目录的 Makefile也从.config 文件中提取信息,生成内核编译所需的文件列表。

5、内核Makefile导读与情景分析

    根据Makefile的执行规则,在分析Makefile时,首先必须确定一个目标 ,然后才能确定所有的依赖关系 ,最后根据更新情况决定是否执行相应的命令。所以要看懂内核Makefile的目标框架,我们首先要了解她里面所定义的目标。而内核Makefile所定义的目标基本上可以通过 make help打印出来(因为help本身就是顶层Makefile的一个目标,里面是打印帮助信息的“echo”命令)

目标

常用目标举例

作用

配置

%config

config

启动Kconfig,以不同界面来配置内核。

menuconfig

xconfig

编译

all

编译vmlinux内核映像和内核模块

vmlinux

编译vmlinux内核映像

modules

编译内核模块

安装

headers_install

安装内核头文件/模块

modules_install

源码浏览

tags

生成代码浏览工具所需要的文件

TAGS

cscope

静态分析

checkstack

检查并分析内核代码

namespacecheck

headers_check

内核打包

%pkg

以不同的安装格式编译内核

文档转换

%doc

把kernel文档转成不同格式

构架相关(arm)

zImage

生成压缩的内核映像

uImage

生成压缩的u-boot可引导的内核映像

install

安装内核映像

    构架相关目标在顶层Makefile上并未出现,而是被包含在平台相关的Makefile(arch/$(ARCH)/Makefile)中。

(3)、Makefile编译过程

  A、生成控制C程序的头文件、控制编译连接的文件

    控制C程序的头文件

  include/linux/version.h include/linux/utsrelease.h、include/linux/autoconf.h

        控制编译连接的文件

  arch/arm/kernel/vmlinux.lds、include/config/auto.conf等文件。

  B、由C程序源码和汇编语言源码生成目标文件(*.o)

  C、将目标文件连接成*.built-in.o、*/lib.a等文件

  D、将顶层目录的子目录中的*.built-in.o以及部分重要的*.o文件连接生成vmlinux

  E、根据arch/arm/Makefile的规则生成zImage等

(4)、Kernel编译构成元素

A、Makefile的目标

     总目标实际上是在arch/arm/Makefile中定义了,比方说zImage、uImage,顶层Makefile紧接着定义了这些终极目标直接的依赖目标vmlinux。

     各级子目标是在scripts/Makefile.build中的__build中定义的,例如传递参数obj=drivers后的目标是drivers/built-in.o。

 目标的依赖又成为了新的目标,例如drivers/net/built-in.o、drivers/net/dm9000.o。

B、Makefile的依赖

    总目标的依赖

  vmlinux-lds  vmlinux-init vmlinux-main vmlinux.o kallsyms.o

各级子目标的依赖

    各级子目标的依赖是由子目录中的Makefile(实际是scripts/Makefile.build的包含文件)和scripts/Makefile.lib共同完成确定的。

   子目录中的Makefile负责选材,而scripts/Makefile.lib负责加工。

C、Makefile的规则

总目标vmlinux的连接规则就是在顶层Makefile中定义的,至于zImage、uImage则是在arch/arm/Makefile中定义的。

子目标的编译连接规则主要是在scripts/Makefile.build、scripts/Kbuild.include中定义的,其中scripts/Kbuild.include定义了许多诸如if_changed的函数。

(5)、Kernel Makefile体系的特点

A、两个Makefile

     顶层Makefile文件负责将各个目录生成的*.built-in.o、lib.a等文件连接到一起生成vmlinux。而scripts/Makefile.build 包含子目录中的Makefile文件以及scripts/中的众多脚本来生成这些*.built-in.o、lib.a、*.o等文件。

     通过“make -f scripts/Makefile.build obj=”的方法完成了顶层Makefile到scripts/Makefile.build的调用生成*/built-in.o,以及scripts /Makefile.build的自调用生成更低一级子目录的*/built-in.o。

B、编译的目录始终是顶层目录

  “make -C”命令会先切换工作目录,然后执行该目录中的Makefile,u-boot就是采用这种方法。而linux则是利用“make -f”的方法,所以编译的目录始终是顶层目录。

C、通用规则

  Linux内核Makefile的通用子Makefile是scripts/Makefile.build,而通用的其他规则则是scripts中的其他文件。

(6)、在内核中添加自己的模块

虽然内核Makefile体系很是复杂,但是这种复杂带来的确是开发时的便利。其实内核Makefile体系之所以复杂,其中的一个原因就是为了方便扩展。对于一个开发者来要在内核中添加自己的一个驱动代码是非常简单的事情。

一般来说,对于一个新驱动代码的添加,驱动工程师只需要在内核源码的drivers目录的相应子目录下添加新设备驱动源码,并增加或修改该目录下的Kconfig和Makefile文件即可。

比如你已经写好了一个针对TI 的AM33XX芯片的 LED的驱动程序,名为am33xx_led.c。

(1)       将驱动源码am33xx_led.c等文件复制到linux-X.Y.Z/drivers/char目录。

(2)       在该目录下的Kconfig文件中添加LED驱动的配置选项:

config AM33XX_LED

       bool "Support for am33xx led drivers"

       depends on  SOC_OMAPAM33XX

       default n

       ---help---

          Say Y here if you want to support for AM33XX LED drivers.

(3)     在该目录下的Makefile文件中添加对LED驱动的编译:

obj-$(CONFIG_AM33XX_LED)   +=  am33xx_led.o

这样你就可以在make menuconfig的时候看到这个配置选项,并进行配置了。

当然,上面的例子只是一个意思,对于Kconfig文件和Makefile的详细语法,请参考内核文档:Documentation/kbuild/makefile.txt

5、链接脚本分析

(1)分析链接脚本的目的就是找到整个程序的entry入口

(2)kernel的链接脚本并不是直接提供的,而是提供了一个汇编文件vmlinux.lds.S,然后在编译的时候再去编译这个汇编文件得到真正的链接脚本vmlinux.lds。

(3)vmlinux.lds.S在arch/arm/kernel/目录下。

(4)思考:为什么linux kernel不直接提供vmlinux.lds而要提供一个vmlinux.lds.S然后在编译时才去动态生成vmlinux.lds呢?

猜测:.lds文件中只能写死,不能用条件编译。但是我们在kernel中链接脚本确实有条件编译的需求(但是lds格式又不支持),于是乎kernel工作者找了个投机取巧的方法,就是把vmlinux.lds写成一个汇编格式,然后汇编器处理的时候顺便条件编译给处理了,得到一个不需要条件编译的vmlinux.lds。

(5)入门在哪里?从vmlinux.lds中ENTRY(stext)可以知道入口符号是stext,在SI中搜索这个符号,发现arch/arm/kernel/目录下的head.S和head-nommu.S中都有。

(6)head.S是启用了MMU情况下的kernel启动文件,相当于uboot中的start.S。

    head-nommu.S是未使用mmu情况下的kernel启动文件。

二.head.S文件分析

1、内核运行的物理地址与虚拟地址

(1)KERNEL_RAM_VADDR(VADDR就是virtual address),这个宏定义了内核运行时的虚拟地址。值为0xC0008000 查找该宏时,遇到CONFIG开头的,一般都在.config中有定义。

TEXT_OFFSET这个宏在SI中是搜不到的,在linux中使用grep "TEXT_OFFSET"  * -nR

TEXT_OFFSET的值为0x00008000

(2)KERNEL_RAM_PADDR(PADDR就是physical address),这个宏定义内核运行时的物理地址。值为0x30008000

(3)总结:内核运行的物理地址是0x30008000,对应的虚拟地址是0xC0008000。

2、内核的真正入口

(1)内核的真正入口就是ENTRY(stext)处

(2)前面的__HEAD定义了后面的代码属于段名为.head.text的段

3、内核运行的硬件条件(解读上面这段英文说明)

(1)内核的起始部分代码是被解压代码调用的。回忆之前讲zImage的时候,uboot启动内核后实际调用运行的是zImage前面的那段未经压缩的解压代码,解压代码运行时先将zImage后段的内核解压开,然后再去调用运行真正的内核入口。

(2)内核启动不是无条件的,而是有一定的先决条件,这个条件由启动内核的bootloader(我们这里就是uboot)来构建保证。

 

(3)ARM体系中,函数调用时实际是通过寄存器传参的(函数调用时传参有两种设计:一种是寄存器传参,另一种是栈内存传参)。

所以uboot中最后theKernel (0, machid, bd->bi_boot_params);执行内核时,运行时实际把0放入r0中,machid放入到了r1中,bd->bi_boot_params放入到了r2中。ARM的这种处理技巧刚好满足了kernel启动的条件和要求。

(4)kernel启动时MMU是关闭的,因此硬件上需要的是物理地址。但是内核是一个整体(zImage)只能被连接到一个地址(不能分散加载),这个连接地址肯定是虚拟地址。因此内核运行时前段head.S中尚未开启MMU之前的这段代码就很难受。所以这段代码必须是位置无关码,而且其中涉及到操作硬件寄存器等时必须使用物理地址。

三.head.S文件分析之内核启动的汇编阶段

1、调用__lookup_processor_type函数

(1)我们从cp15协处理器的c0寄存器中读取出硬件的CPU ID号,然后调用这个函数来进行合法性检验。如果合法则继续启动,如果不合法则停止启动,转向__error_p启动失败。

(2)该函数检验cpu id的合法性方法是:内核会维护一个本内核支持的CPU ID号码的数组,然后该函数所做的就是将从硬件中读取的cpu id号码和数组中存储的各个id号码依次对比,如果没有一个相等则不合法,如果有一个相等的则合法。

(3)内核启动时设计这个校验,也是为了内核启动的安全性着想。

2、__lookup_machine_type

(1)该函数的设计理念和思路和上面校验cpu id的函数一样的。不同之处是本函数校验的是机器码。如果不合法则停止启动,转向__error_a启动失败。

3、__vet_atags

(1)该函数的设计理念和思路和上面2个一样,不同之处是用来校验uboot给内核的传参ATAGS格式是否正确。这里说的传参指的是uboot通过tag给内核传的参数(主要是板子的内存分布memtag、uboot的bootargs)

(2)内核认为如果uboot给我的传参格式不正确,那么我就不启动。

(3)uboot给内核传参的部分如果不对,是会导致内核不启动的。譬如uboot的bootargs设置不正确内核可能就会不启动。

4、__create_page_tables

(1)顾名思义,这个函数用来建立页表。

(2)linux内核本身被连接在虚拟地址处,因此kernel希望尽快建立页表并且启动MMU进入虚拟地址工作状态。但是kernel本身工作起来后页表体系是非常复杂的,建立起来也不是那么容易的。kernel想了一个好办法

(3)kernel建立页表其实分为2步。第一步,kernel先建立了一个段式页表(和uboot中之前建立的页表一样,页表以1MB为单位来区分的),这里的函数就是建立段式页表的。段式页表本身比较好建立(段式页表1MB一个映射,4GB空间需要4096个页表项,每个页表项4字节,因此一共需要16KB内存来做页表),坏处是比较粗不能精细管理内存;第二步,再去建立一个细页表(4kb为单位的细页表),然后启用新的细页表废除第一步建立的段式映射页表。

(4)内核启动的早期建立段式页表,并在内核启动前期使用;内核启动后期就会再次建立细页表并启用。等内核工作起来之后就只有细页表了。

5、__switch_data

(1)建立了段式页表后进入了__switch_data部分,这东西是个函数指针数组。

(2)分析得知下一步要执行__mmap_switched函数

(3)复制数据段、清除bss段(目的是构建C语言运行环境)

(4)保存起来cpu id号、机器码、tag传参的首地址。

(5)b start_kernel跳转到C语言运行阶段。

总结:汇编阶段主要就是校验ID号、机器码的启动合法性、uboot给内核的传参ATAGS格式校验、建立段式映射的页表并开启MMU以方便使用内存、以及构建C语言的运行环境后、跳入C语言阶段。

下面,我们就将跟踪start_kernel函数顺着代码执行路径抓全分析内核启动的C语言阶段

四.内核启动的C语言阶段之start_kernel函数分析

具体学习方法

(1)顺着代码执行路径抓全。这是我们的学习主线。

(2)对照内核启动的打印信息进行分析

几条学习线路

(1)分析uboot给kernel传参的影响和实现

(2)硬件初始化与驱动加载

(3)内核启动后的结局与归宿

1、了解即可的函数

(1)smp。smp就是对称多处理器(其实就是我们说的多核心CPU)

(2)lockdep。锁定依赖,是一个内核调试模块,处理内核自旋锁死锁问题相关的。

(3)cgroup。control group,内核提供的一种来处理进程组的技术。

2、打印内核版本信息

(1)代码位于:kernel/init/main.c中的572行

(2)printk函数是内核中用来从console打印信息的,类似于应用层编程中的printf。内核编程时不能使用标准库函数,因此不能使用printf,其实printk就是内核自己实现的一个printf。

(3)printk函数的用法和printf几乎一样,不同之处在于可以在参数最前面用一个宏来定义消息输出的级别。为什么要有这种级别?主要原因是linux内核太大了,代码量太多,里面的printk打印信息太多了。如果所有的printk都能打印出来而不加任何限制,则最终内核启动后得到海量的输出信息。

(4)为了解决打印信息过多,无效信息会淹没有效信息这个问题,linux内核的解决方案是给每一个printk添加一个打印级别。级别定义0-7(注意编程的时候要用相应的宏定义,不要直接用数字)分别代表8种输出的重要性级别,0表示最重要,7表示最不重要。我们在printk的时候自己根据自己的消息的重要性去设置打印级别。

(5)linux的控制台监测消息的地方也有一个消息过滤显示机制,控制台实际只会显示级别比我的控制台定义的级别高的消息。譬如说控制台的消息显示级别设置为4,那么只有printk中消息级别为0-3(也可能是0-4)的才可以显示看见,其余的被过滤掉了。

(6)linux_banner的内容解析。

对应linux_banner的内容显示在终端如下

在linux下使用gerp命令在内核中搜索UTS_RELEASE、LINUX_COMPILE_HOST等,与打印信息一致

3、setup_arch函数简介

(1)从名字看,这个函数是CPU架构相关的一些创建过程。

(2)实际上这个函数是用来确定我们当前内核的机器(arch、machine)的。我们的linux内核会支持一种CPU的运行,CPU+开发板就确定了一个硬件平台,然后我们当前配置的内核就在这个平台上可以运行。之前说过的机器码就是给这个硬件平台一个固定的编码,以表征这个平台。

(3)当前内核支持的机器码以及硬件平台相关的一些定义都在这个函数中处理。

4、Machine查找

(1)setup_processor函数用来查找CPU信息,可以结合串口打印的信息来分析。

(2)setup_machine函数的传参是机器码编号,

machine_arch_type符号在include/generated/mach-types.h的32039-32050行定义了。经过分析后确定这个传参值就是2456.

(3)函数的作用是通过传入的机器码编号,找到对应这个机器码的machine_desc描述符,并且返回这个描述符的指针。

(4)其实真正干活的函数是lookup_machine_type,找这个函数发现在head-common.S中,真正干活的函数是__lookup_machine_type

(5)__lookup_machine_type函数的工作原理:内核在建立的时候就把各种CPU架构的信息组织成一个一个的machine_desc结构体实例,然后都给一个段属性.arch.info.init,链接的时候会保证这些描述符会被连接在一起。__lookup_machine_type就去那个那些描述符所在处依次挨个遍历各个描述符,比对看机器码哪个相同。

Machine函数的查找就结束了。

重新回到setup_arch函数,继续分析。

5.、setup_arch函数进行了基本的cmdline处理

static char default_command_line[COMMAND_LINE_SIZE] __initdata = CONFIG_CMDLINE;

(1)这里说的cmdline就是指的uboot给kernel传参时传递的命令行启动参数,也就是uboot的bootargs。

(2)有几个相关的变量需要注意:

default_command_line:看名字是默认的命令行参数,实际是一个全局变量字符数组,这个字符数组可以用来存东西。

CONFIG_CMDLINE:在.config文件中定义的(可以在make menuconfig中去更改设置),这个表示内核的一个默认的命令行参数。

(3)内核对cmdline的处理思路是:内核中自己维护了一个默认的cmdline(就是.config中配置的这一个),然后uboot还可以通过tag给kernel再传递一个cmdline。如果uboot给内核传cmdline成功则内核会优先使用uboot传递的这一个;如果uboot没有给内核传cmdline或者传参失败,则内核会使用自己默认的这个cmdline。以上说的这个处理思路就是在setup_arch函数中实现的。

6、实验验证内核的cmdline确定

(1)验证思路:首先给内核配置时配置一个基本的cmdline,然后在uboot启动内核时给uboot设置一个bootargs,然后启动内核看打印出来的cmdline和uboot传参时是否一样。

(2)在uboot中去掉bootargs,然后再次启动内核看打印出来的cmdline是否和内核中设置的默认的cmdline一样。

重新使用tffp下载内核文件,可以看到,由于我们的uboot的bootargs设置为空,没有给内核传cmdline,内核使用了自己默认的这个cmdline。

就验证了上面内核对cmdline的处理思路的正确性。

注意:uboot给内核传递的cmdline非常重要,会影响内核的运行,所以要谨慎。有时候内核启动有问题,可以分析下是不是uboot的bootargs设置不对。

注意:这个传参在这里确定出来之后,还没完。后面还会对这个传参进行解析。解析之后cmdline中的每一个设置项都会对内核启动有影响。

思考:内核为什么要这样设计?

具有灵活性,便于我们在终端中直接设置对应的环境变量即可,这样,就不用动庞大的内核代码了。直接在启动之前,设置不同的启动参数,则会触发不同的结果

7、setup_command_line

(1)也是在处理和命令行参数cmdline有关的任务。

2、parse_early_param&parse_args

(1)解析cmdline传参和其他传参

(2)这里的解析意思是把cmdline的细节设置信息给解析出来。

譬如cmdline:console=ttySAC2,115200 root=/dev/mmcblk0p2 rw init=/linuxrc rootfstype=ext3,则解析出的内容就是就是一个字符串数组,数组中依次存放了一个设置项目信息。

console=ttySAC2,115200     一个

root=/dev/mmcblk0p2 rw      一个

init=/linuxrc                   一个

rootfstype=ext3              一个

(3)这里只是进行了解析,并没有去处理。也就是说只是把长字符串解析成了短字符串,最多和内核里控制这个相应功能的变量挂钩了,但是并没有去执行。执行的代码在各自模块初始化的代码部分。

8、仅需要了解的函数

(1)trap_init     设置异常向量表

(2)mm_init     内存管理模块初始化

(3)sched_init     内核调度系统初始化

(4)early_irq_init&init_IRQ  中断初始化

(5)console_init     控制台初始化

总结:start_kernel函数中调用了很多的xx_init函数,全都是内核工作需要的模块的初始化函数。这些初始化之后内核就具有了一个基本的可以工作的条件了。

如果把内核比喻成一个复杂机器,那么start_kernel函数就是把这个机器的众多零部件组装在一起形成这个机器,让他具有可以工作的基本条件。

9、rest_init

(1)这个函数之前内核的基本组装已经完成。

(2)剩下的一些工作就比较重要了,放在了一个单独的函数中,叫rest_init。

总结:start_kernel函数做的主要工作:打印了一些信息、内核工作需要的模块的初始化被依次调用(譬如内存管理、调度系统、异常处理···)、我们需要重点了解的就是setup_arch中做的2件事情:机器码架构的查找并且执行架构相关的硬件的初始化、uboot给内核的传参cmdline。

下面就开始分析rest_init函数,引出进程的概念!

五.内核启动的C语言阶段之rest_init函数分析

1、操作系统去哪了

(1)rest_init中调用kernel_thread函数启动了2个内核线程,分别是:kernel_init和kthreadd

(2)调用schedule函数开启了内核的调度系统,从此linux系统开始转起来了。

(3)rest_init最终调用cpu_idle函数结束了整个内核的启动。也就是说linux内核最终结束了一个函数cpu_idle。这个函数里面肯定是死循环。

(4)简单来说,linux内核最终的状态是:有事干的时候去执行有意义的工作(执行各个进程任务),实在没活干的时候就去死循环(实际上死循环也可以看成是一个任务)。

(5)之前已经启动了内核调度系统,调度系统会负责考评系统中所有的进程,这些进程里面只有有哪个需要被运行,调度系统就会终止cpu_idle死循环进程(空闲进程)转而去执行有意义的干活的进程。这样操作系统就转起来了。

2、什么是内核线程

(1)进程和线程。简单来理解,一个运行的程序就是一个进程。所以进程就是任务、进程就是一个独立的程序。独立的意思就是这个程序和别的程序是分开的,这个程序可以被内核单独调用执行或者暂停。

(2)在linux系统中,线程和进程非常相似,几乎可以看成是一样的。实际上我们当前讲课用到的进程和线程的概念就是一样的。

(3)进程/线程就是一个独立的程序。应用层运行一个程序就构成一个用户进程/线程,那么内核中运行一个函数(函数其实就是一个程序)就构成了一个内核进程/线程。

(4)所以我们kernel_thead函数运行一个函数,其实就是把这个函数变成了一个内核线程去运行起来,然后他可以被内核调度系统去调度。说白了就是去调度器注册了一下,以后人家调度的时候会考虑你。

3、进程0、进程1、进程2

(1)截至目前为止,我们一共涉及到3个内核进程/线程。

(2)操作系统是用一个数字来表示/记录一个进程/线程的,这个数字就被称为这个进程的进程号。这个号码是从0开始分配的。因此这里涉及到的三个进程分别是linux系统的进程0、进程1、进程2.

(3)在linux命令行下,使用ps命令可以查看当前linux系统中运行的进程情况。

(4)我们在ubuntu下ps -aux可以看到当前系统运行的所有进程,可以看出进程号是从1开始的。为什么不从0开始,因为进程0不是一个用户进程,而属于内核进程。

(5)三个进程

进程0:进程0其实就是刚才讲过的idle进程,叫空闲进程,也就是死循环。

进程1:kernel_init函数就是进程1,这个进程被称为init进程。

进程2:kthreadd函数就是进程2,这个进程是linux内核的守护进程这个进程是用来保证linux内核自己本身能正常工作的。

idle进程到底做了什么??

idle进程最终调用了cpu_idle()函数

进程0 系统态,内核启动的时候手动构成的 ,唯一一个没有通过fork()产生的进程。

进程1 系统态,由进程0 ,通过fork产生的进程

进程2 用户态,每个进程都是fork诞生的

总结1:本节课的重点在于理解linux内核启动后达到的一个稳定状态。注意去对比内核启动后的稳定状态和uboot启动后的稳定状态的区别。(内核具有调度系统

总结2:本节课的第二个重点就是初步理解进程/线程的概念。

总结3:你得明白每个进程有个进程号,进程号从0开始依次分配的。明白进程0是idle进程(idle进程是干嘛的?空闲进程,没事做的时候就运行该进程,死循环);进程2是ktheadd进程(基本明白干嘛的就行)

分析到此,发现后续的料都在进程1.所以后面课程会重点从进程1出发,分析之后发生的事情。

六.内核启动之init进程详解

1、进程1 : init:开始是内核态,后来转变为用户态

init进程完成了从内核态向用户态的转变

(1)一个进程2种状态。init进程刚开始运行的时候是内核态,它属于一个内核线程,然后他自己运行了一个用户态下面的程序后把自己强行转成了用户态。因为init进程自身完成了从内核态到用户态的过度,因此后续的其他进程都可以工作在用户态下面了。

(2)内核态下做了什么?

重点就做了一件事情,就是挂载根文件系统并试图找到用户态下的那个init程序。init进程要把自己转成用户态就必须运行一个用户态的应用程序(这个应用程序名字一般也叫init),要运行这个应用程序就必须得找到这个应用程序,要找到它就必须得挂载根文件系统,因为所有的应用程序都在文件系统中。

说直接点,最主要的工作就是挂载根文件系统

内核源代码中的所有函数都是内核态下面的,执行任何一个都不能脱离内核态。应用程序必须不属于内核源代码,这样才能保证自己是用户态。也就是说我们这里执行的这个init程序和内核不在一起,他是另外提供的。提供这个init程序的就是根文件系统。

(3)用户态下做了什么?

init进程大部分有意义的工作都是在用户态下进行的。init进程对我们操作系统的意义在于:其他所有的用户进程都直接或者间接派生自init进程。

主要作用是:构建了用户交互界面

(4)如何从内核态跳跃到用户态?还能回来不?

init进程在内核态下面时,通过一个函数kernel_execve来执行一个用户空间编译连接的应用程序就跳跃到用户态了。

注意这个跳跃过程中进程号是没有改变的,所以一直是进程1.这个跳跃过程是单向的

也就是说一旦执行了init程序转到了用户态下整个操作系统就算真正的运转起来了,以后只能在用户态下工作了,用户态下想要进入内核态只有走API这一条路了。

 

2、init进程构建了用户交互界面

(1)init进程是其他用户进程的老祖宗。

linux系统中一个进程的创建是通过其父进程创建出来的。根据这个理论只要有一个父进程就能生出一堆子孙进程了。

(2)init启动了login进程(用来做用户登录)、命令行进程(用户登录后分配命令行)、shell进程(提供命令行的解析、执行,用于交互)

(3)shell进程启动了其他用户进程。命令行和shell一旦工作了,用户就可以在命令行下通过./xx的方式来执行其他应用程序,每一个应用程序的运行就是一个进程。

总结:本节的主要目的是让大家认识到init进程如何一步步发展成为我们平时看到的那种操作系统的样子。

 

3、打开控制台

(1)linux系统中每个进程都有自己的一个文件描述符表,表中存储的是本进程打开的文件。

(2)linux系统中有一个设计理念:一切皆是文件。所以设备也是以文件的方式来访问的。我们要访问一个设备,就要去打开这个设备对应的文件描述符。譬如/dev/fb0这个设备文件就代表LCD显示器设备,/dev/buzzer代表蜂鸣器设备,/dev/console代表控制台设备。

(3)这里我们打开了/dev/console文件,并且复制了2次文件描述符,一共得到了3个文件描述符。这三个文件描述符分别是0、1、2.

这三个文件描述符就是所谓的:标准输入、标准输出、标准错误。

(4)进程1打开了三个标准输出输出错误文件,因此后续的进程1衍生出来的所有的进程默认都具有这3个文件描述符。

4、挂载根文件系统

(1)prepare_namespace函数中挂载根文件系统

(2)根文件系统在哪里?根文件系统的文件系统类型是什么? uboot通过传参来告诉内核这些信息。

set bootargs console=ttySAC2,115200 root=/dev/mmcblk0p2 rw init=/linuxrc rootfstype=ext3

uboot传参中的root=/dev/mmcblk0p2 rw    这一句就是告诉内核根文件系统在哪里

uboot传参中的rootfstype=ext3                   这一句就是告诉内核rootfs的类型。

(3)如果内核挂载根文件系统成功,则会打印出:VFS: Mounted root (ext3 filesystem) on device 179:2.

如果挂载根文件系统失败,则会打印:No filesystem could mount root, tried:  yaffs2

(4)如果内核启动时挂载rootfs失败,则后面肯定没法执行了,肯定会死。内核中设置了启动失败休息5s自动重启的机制,因此这里会自动重启,所以有时候大家会看到反复重启的情况。

(5)如果挂载rootfs失败,可能的原因有:

最常见的错误就是uboot的bootargs设置不对。

rootfs烧录失败(fastboot烧录不容易出错,以前是手工烧录很容易出错)

rootfs本身制作失败的。(尤其是自己做的rootfs,或者别人给的第一次用)

5、执行用户态下的进程1程序

(1)上面一旦挂载rootfs成功,则进入rootfs中寻找应用程序的init程序,这个程序就是用户空间的进程1.找到后用run_init_process去执行他

(2)我们如果确定init程序是谁?方法是:

先从uboot传参cmdline中看有没有指定,如果有指定先执行cmdline中指定的程序。

set bootargs console=ttySAC2,115200 root=/dev/mmcblk0p2 rw init=/linuxrc rootfstype=ext3

cmdline中的init=/linuxrc这个就是指定rootfs中哪个程序是init程序。这里的指定方式就表示我们rootfs的根目录下面有个名字叫linuxrc的程序,这个程序就是init程序。

如果uboot传参cmdline中没有init=xx或者cmdline中指定的这个xx执行失败,

还有备用方案:

第一备用:/sbin/init,第二备用:/etc/init,第三备用:/bin/init,第四备用:/bin/sh。

如果以上都不成功,则认命了,死了。

到这里,kernel将根文件系统挂载成功后,kernel的任务就执行完了。

下面是一些内核的补充

七、cmdline常用参数

这里说的cmdline就是指的uboot给kernel传参时传递的命令行启动参数,也就是uboot的bootargs。

例如:

set bootargs console=ttySAC2,115200 root=/dev/mmcblk0p2 rw init=/linuxrc rootfstype=ext3

1、格式简介

(1)格式就是由很多个项目用空格隔开依次排列,每个项目中都是项目名=项目值

(2)整个cmdline会被内核启动时解析,解析成一个一个的项目名=项目值的字符串。这些字符串又会被再次解析从而影响启动过程。

2、root=

(1)这个是用来指定根文件系统在哪里的

(2)一般格式是root=/dev/xxx

(一般如果是nandflash上则/dev/mtdblock2,如果是inand/sd的话则/dev/mmcblk0p2)

(3)如果是nfs的rootfs,则root=/dev/nfs。

3、rootfstype=

(1)根文件系统的文件系统类型,一般是jffs2、yaffs2、ext3、ubi

4、console=

(1)控制台信息声明,譬如console=/dev/ttySAC2,115200表示控制台使用串口2,波特率是115200.

(2)正常情况下,内核启动的时候会根据console=这个项目来初始化硬件,并且重定位console到具体的一个串口上,所以这里的传参会影响后续是否能从串口终端上接收到内核的信息。

5、mem=

(1)mem=用来告诉内核当前系统的内存有多少

6、init=

(1)init=用来指定进程1的程序pathname,一般都是init=/linuxrc

7、常见cmdline介绍

(1)console=ttySAC2,115200 root=/dev/mmcblk0p2 rw init=/linuxrc rootfstype=ext3

第一种这种方式对应rootfs根文件系统在SD/iNand/Nand/Nor等物理存储器上这种对应产品正式出货工作时的情况。

(2)root=/dev/nfs nfsroot=192.168.1.141:/root/s3c2440/build_rootfs/aston_rootfs ip=192.168.1.10:192.168.1.141:192.168.1.1:255.255.255.0::eth0:off  init=/linuxrc console=ttySAC0,115200

第二种这种方式对应rootfs根文件系统在nfs网络上这种对应我们实验室开发产品做调试的时候。

八、内核中架构相关代码简介

1、内核代码目录基本分为3块

(1)arch。  本目录下全是cpu架构有关的代码

(2)drivers  本目录下全是硬件的驱动

(3)其他   相同点是这些代码都和硬件无关,因此系统移植和驱动开发的时候这些代码几乎都是不用关注的。

2、架构相关的常用目录名及含义

(1)mach。(mach就是machine architecture)。

arch/arm目录下的一个mach-xx目录就表示一类machine的定义,这类machine的共同点是都用xx这个cpu来做主芯片。(譬如mach-s5pv210这个文件夹里面都是s5pv210这个主芯片的开发板machine);mach-xx目录里面的一个mach-yy.c文件中定义了一个开发板(一个开发板对应一个机器码),这个是可以被扩展的。

(2)plat(plat是platform的缩写,含义是平台)plat在这里可以理解为SoC,也就是说这个plat目录下都是SoC里面的一些硬件(内部外设)相关的一些代码。

在内核中把SoC内部外设相关的硬件操作代码就叫做平台设备驱动。

(3)include。这个include目录中的所有代码都是架构相关的头文件。(linux内核通用的头文件在内核源码树根目录下的include目录里)

3、补充

(1)内核中的文件结构很庞大、很凌乱(不同版本的内核可能一个文件存放的位置是不同的),会给我们初学者带来一定的困扰。

(2)头文件目录include有好几个,譬如:

        kernel/include                              内核通用头文件

kernel/arch/arm/include      架构相关的头文件

kernel/arch/arm/include/asm

kernel\arch\arm\include\asm\mach

kernel\arch\arm\mach-s5pv210\include\mach

kernel\arch\arm\plat-s5p\include\plat

(3)内核中包含头文件时有一些格式

#include <linux/kernel.h>  kernel/include/linux/kernel.h

#include <asm/mach/arch.h>  kernel/arch/arm/include/asm/mach/arch.h

#include <asm/setup.h>   kernel\arch\arm\include\asm/setup.h

#include <plat/s5pv210.h>  kernel\arch\arm\plat-s5p\include\plat/s5pv210.h

(4)有些同名的头文件是有包含关系的,有时候我们需要包含某个头文件时可能并不是直接包含他,而是包含一个包含了他的头文件。

2019-07-26 18:41:56 tanghong1996 阅读数 1083
  • 自学有道-产品经理需要的工具视频教程

    自学,需要一种方法,让学习变成一件简单的事儿;产品经理的工具,思维导图又叫脑图,能够帮助产品经理进行用户角色以及功能结构梳理等等的工作;而且还可以提高工作效率的同时形成系统的学习和思维的习惯;本课程主要讲解了ProcessOn和XMind两款思维导图工具的使用。

    3913 人正在学习 去看看 孙伟

设置root密码

sudo passwd

更换软件源

建议换成中科大的软件源
然后

  1. 使用sudo apt-get update即可更新获取 软件源 提供的软件列表;
  2. 使用sudo apt-get upgrade即可更新软件;
sudo apt-get update
sudo apt-get upgrade

卸载不需要的软件

sudo apt-get remove libreoffice-common 
sudo apt-get remove unity-webapps-common 
sudo apt-get remove totem rhythmbox empathy brasero simple-scan gnome-mahjongg aisleriot gnome-mines cheese transmission-common gnome-orca webbrowser-app gnome-sudoku  landscape-client-ui-install 
sudo apt-get remove onboard deja-dup 

vim git vpnc

sudo apt-get install vim  git vpnc

多线程下载工具

sudo apt install axel

pip

ubuntu16.04预装了python2.7与python3.5,但是没有预装pip

sudo apt-get install python-pip #for python2.7
sudo apt-get install python3-pip #for python3.5

Terminator的安装/配置/默认启动/使用

1.安装

sudo apt-get install terminator

2.配置

cd ~/.config/terminator/
sudo vim config

如果报错,Unable to open ~/.config/terminator/config ,解决方法:
打开terminator终端,然后右击终端的黑色背景,选择preference->layouts->add,关闭该窗口即可找到config文件。

修改配置文件,我的配置文件如下:

[global_config]
[keybindings]
[profiles]
  [[default]]
    use_system_font = False # 是否启用系统字体
    login_shell = True
    background_darkness = 0.92 # 背景颜色
    background_type = transparent
    background_image = None
    cursor_color = "#3036ec" # 光标颜色
    foreground_color = "#00ff00"
    show_titlebar = False # 不显示标题栏,也就是 terminator 中那个默认的红色的标题栏
    custom_command = tmux
    font = Ubuntu Mono 15  # 字体设置,后面的数字表示字体大小
[layouts]
  [[default]]
    [[[child1]]]
      type = Terminal
      parent = window0
    [[[window0]]]
      type = Window
      parent = ""
[plugins]

另外附上一些搜索的配置文件参考:
https://blog.csdn.net/ipatient/article/details/51547658
http://www.javaranger.com/archives/1126

3.默认启动

安装dconf-tools,这个是设置默认终端的必须

sudo apt-get install dconf-tools

默认快捷键的终端为系统终端,则更改为:

exec  gnome-terminal
exec-arg -x

默认启动terminator,即:

exec  x-terminal-emulator
exec-arg -e

安装unrar

系统默认不带解压缩rar文件的功能,手动安装unrar程序。虽然不知道为什么这么多人偏爱rar,不过网络环境如此,这个必须装。但我个人还是建议多用些开源的压缩格式比较讲究。

sudo apt-get install unrar

安装smplayer

免费开源的媒体播放器SMPlayer。内置解码器,因此任何额外的编解码器。虽然高清解码不怎么给力,不过一次安装,就解决所有影音文件的播放问题。

sudo apt-add-repository ppa:rvm/smplayer
sudo apt-get update
sudo apt-get install smplayer smplayer-skins smplayer-themes

安装Unity Tweak Tool

如果想安装自定义主题的话,那么Unity Tweak Tool是必装的。虽然Unity并没有留给各位多少能Tweak的地方,但是只要是想个性化界面,一定不要忘了先安装它。

sudo apt-get install unity-tweak-tool

https://blog.csdn.net/mutilcam_prince/article/details/78217176?locationNum=8&fps=1

耳机驱动

输入下面命令安装pavucontrol:

sudo apt install pavucontrol

然后输入pavucontrol启动该软件

pavucontrol

打开选项卡后进行:
“回放”–>系统下面的选项–>内置音频模拟立体声
“输出设备”–>port:headphone(unplugged) 模拟耳机

状态栏中添加网速显示

sudo add-apt-repository ppa:nilarimogard/webupd8
sudo apt-get update
sudo apt-get install indicator-netspeed

Teamviewer

https://blog.csdn.net/wu_l_v/article/details/79184923

pip、conda等添加国内景象源

https://blog.csdn.net/YZXnuaa/article/details/79710673

安装Pycharm

https://blog.csdn.net/tanghong1996/article/details/81701769

CUDA CUDNN

https://blog.csdn.net/syysyf99/article/details/93778390

安装caffe

http://blog.csdn.net/yhaolpz/article/details/71375762

安装cuda

http://blog.csdn.net/u010837794/article/details/63251725

https://blog.csdn.net/wopawn/article/category/6382963

https://blog.csdn.net/dgyuanshaofeng/article/details/78151510

https://blog.csdn.net/dc1994dc/article/details/79162886

安装 py faster rcnn

https://www.cnblogs.com/zjutzz/p/6034408.html

pcl源码编译

http://blog.csdn.net/xs1102/article/details/74736298(gitclone下载)

深度工具合集安装

(Nvidia+CUDA+cuDNN+Tensorflow+OpenBLAS+Caffe+Theano+Keras)
http://blog.csdn.net/langb2014/article/details/51579491

火狐浏览器

Ubuntu系统自带国际版火狐浏览器
但国内用户大多使用的是中国版的火狐
这两个版本的帐号是不互通的,书签等不能进行同步
所以如果想在Ubuntu上使用中国版Firefox,需要先卸载系统自带的浏览器:
https://blog.csdn.net/xuehua789/article/details/48756031
https://blog.csdn.net/weixin_39837402/article/details/79964525
先下载中文版安装包Linux-Firefox中文位版
然后卸载国际版
搞反了你就GG了
保留配置文件

sudo apt-get autoremove firefox firefox-branding firefox-gnome-support ubufox 

配置文件也一起删掉

sudo apt-get purge firefox firefox-branding firefox-gnome-support ubufox 

将下载好的压缩包使用如下命令解压:

sudo tar jxvf Firefox-latest-x86_64.tar.bz2
sudo mv firefox /usr/local
sudo ln -s /usr/local/firefox/firefox /usr/bin/firefox

通过上述步骤即可以通过终端打开firefox了
将其固定在启动器
自己个性化创建一个快捷方式,保存为firefox.desktop:

[Desktop Entry]
Name=firefox
Comment=firefox
Exec=/usr/local/firefox/firefox
Icon=/usr/local/firefox/browser/icons/mozicon128.png
Terminal=false
Type=Application
Categories=Application;
Encoding=UTF-8
StartupNotify=true

将快捷方式移动至/usr/share/applications中:

sudo mv firefox.desktop /usr/share/applications

将该firefox.desktop拖至启动器即可

wps for linux

下载wps-office_10.1.0.5672~a21_amd64.deb

sudo dpkg -i wps-office_10.1.0.5672_a21_amd64.deb
sudo apt-get -f install

打开会有字体缺失错误,下载缺失字体

sudo unzip wps_symbol_fonts.zip

复制字体到/usr/share/fonts,至此安装完毕

有道词典

https://blog.csdn.net/brad1994/article/details/56484634

Online

安装XX-Net 3.3.6
https://blog.csdn.net/summer6364/article/details/77647292

看视频时禁止系统休眠

Ubuntu 的休眠很不智能,只要鼠标不动,就判定你没有活动。在看 MOOC 视频时,时常会自动关闭屏幕,令人烦恼。
这个名为 Caffeine 的软件可以在状态栏生成一个指示器:
安装Ubuntu后的个人常用配置
就是图中那个咖啡杯形状的图标。当它处于 Active 状态(咖啡冒热气)时,便会阻止系统休眠。点击可以图标进行状态切换。
这里写图片描述

sudo add-apt-repository ppa:caffeine-developers/ppa
sudo apt-get update
sudo apt-get install caffeine

在终端中输入 caffeine-indicator ,或直接在 Dash 中寻找图标启动软件。
这里写图片描述

技巧

文件管理器不显示具体文件路径,请按Ctrl+L

Linux多线程

阅读数 797

Linux基础操作

阅读数 41

没有更多推荐了,返回首页