1.管道(pipe)是一种用来连接两个进程的虚拟文件,当进程A欲向进程B发送数据时,它把管道文件视作输出文件,向其中写数据,进程B则可将管道文件视作输入文件,从中读数据。于是,进程A和B之间的通信很像普通文件的读写。
2.MINIX中进程的存储空间分为三部分:正文段(即代码段),数据段(即变量),和堆栈段。
3.MINIX中一个简单的系统调用getpid返回调用进程的进程标识号,注意在调用fork时,只有父进程能够获得子进程的进程标识号。如果子进程要得到自己的进程表示号,它必须使用getpid。
4.内核启动流程:先走head.S的汇编流程,这里有个depress_kernel接口,它负责内核的自解压。解压完然后跳到start kernel这个C语言函数接口,这个里面大部分都是一些初始化,最后会有个rest_init()接口,这里面是剩余的初始化。rest_init()里有个try_to_run_init_process("sbin/init")接口,这个就是去运行1号进程(1号进程就是进程ID号PID为1的进程),运行init进程后,start kernel这个函数就不再返回了。如果找不到"sbin/init",内核会去/etc等目录下寻找init进程,都没有就return了。
5.驱动是以模块加载到内核中的,写好一个驱动模块后不能用gcc或者交叉编译来编译,而是要用以后该模块加载到的内核的编译系统来编译(如果要加载到本机的Linux系统中使用,则编译进的内核目录为:lib/modules/$(shell uname -r)/build,下图就是编译模块到本地Linux内核中的makefile,切记不是编译到目标机的内核,Linux系统移植时如果需要编译驱动模块则要换成目标机的内核路径)。如果编译好的内核是要load到arm中运行,则提前在内核的Makefile中修改编译用交叉编译就可以了。编译内核时命令为make uImage,编译设备树时命令为make dtbs,编译模块(驱动)时命令为make modules。
6.驱动模块分为driver和device两个部分,driver.c需要自己去编写,device既可以通过device.c去编写,也可以通过设备树文件添加子节点去设计
7.为了实现进程模型。操作系统维持着一张表格(一个结构数组),即进程表(process table)。每个进程占用一个进程表项。该表项包含了进程的状态、它的程序计数器、栈指针、内存分配状态、打开文件状态、计费和调度信息,以及其他在由运行态转到就绪态必须保存的信息,只有这样才能使进程随后被再次启动,就像从未被中断过一样。
8.在传统进程中,每个进程中只存在一条控制线索和一个程序计数器。但在现代操作系统中,提供了对单个进程中多条控制线索的支持。这些控制线索被称为线程(threads),有时候也称为轻量进程(lightweight processes)。
9.线程间共享数据好处是调度起来只需要加载各自线程的栈信息就可以了,优化了调度。但是坏处也是线程共享许多数据结构导致的。若一个线程关闭一个文件而另一个线程正在读该文件,将有什么后果?假设一个线程注意到内存不够并开始申请更多的内存,但此时发生线程切换,新运行的线程也注意到这个问题并再次申请内存。那么这里是申请一次呢,还是两次呢?
10.进程是通过时钟中断进行调度的,临界区是控制进程同步的方式,但是当进程A进入到临界区后发生时钟中断,进程B切换到运行态也进入到该临界区是非常危险的。这里有两种方式可以防止这种情况发生:(1)当进程A进入到临界区后需要关中断,时钟中断也会被关闭。(2)锁机制。
11.1Gbps意思是1G bits/per secont。根据等量关系1GB/s=8Gbps=8Gbit/s.
12.I/O设备可以粗略的分为两类:块设备和字符设备.
13.设备树解析过程:.dts文件经过编译后变成.dtb文件传给内核,内核解析.dtb文件将每个设备树节点总结为device_node结构体,然后再解析为platform_device资源;platform_device再和platform_driver匹配。
14.如下是设备树中的属性转为device_node结构体中的成员变量:
15.设备树编译解析过程:dts----->dtb------>device_node------>platform_device:一般情况下所有的设备树节点都会转化为一个device_node,包括根节点;但是不是所有的device_node都会转化为platform_device,比如根节点就不会,chosen节点也不会,还有在注册总线和设备时,会对dts节点的状态作一个判断,如果节点里面的status属性没有被定义,或者status属性被定义了并且值被设为“ok”或者“okay”,其他情况则不被注册到系统中。一般情况下只有根节点下面的子节点才会有platform_device结构体,比如下图的i2c节点会有一个platform_device结构体,但是at24c02这个节点不会转换为platform_device结构体,而是交给i2c驱动的probe函数去处理。但是这个规定不是死的,有些节点的compatible属性如果包含"simple-bus"字符串,则也会转换为platform_device结构体的。simple-bus表示简单的内存映射总线,表示cpu能够直接访问到。
16.platform_device和platform_driver匹配使用platform_match接口,比较就是比较name等字符串,下面一共有4个比较项,只要有一项比较成功,则返回1,然后调用platform_driver的probe函数。韦东山的设备树视频的第三章:platform_device和platform_driver的匹配,这一小节详细介绍了下面4种匹配过程。
17.uboot会把dtb文件传给内核,并且在内存上占一块空间,这块空间不会被占用,系统上电后我们可以去查看这块内存区域。可以通过/sys/firmware/fdt查看,可以使用命令:hexdump -C fdt去打印这里面的内容,这个里面是原始的dtb内容。在fdt同级目录有个devicetree目录,/sys/firmware/devicetree/base这里面的文件对应根节点的属性和子节点,子节点是目录存在形式。如果属性是字符串,可以使用cat去查看内容。
18.GPIO复用的dts配置时,如果有下面打印不要认为内核没有对qcom,rx-gpio进行解析,先看看配置的节点中有没有pinctrl-names属性,这2中配置的方式是一样的。
19.当中断发生时,cpu会跳到某个地址运行中断处理函数,这些中断处理函数的地址是连续排列的,这就是中断向量表。这些中断处理函数要:保护现场,调用函数,恢复现场。
20.一个中断控制器只能控制32种中断,这32种中断信息存放在irq_desc[]数组中(韦东山的Linux设备树详解对中断讲解的非常详细,以后细看)。
21.设备树中对设备配置中断时,需要指定2个内容,第一就是需要指定中断控制器是哪个(interrupt-parent属性);第二就是指定是该中断控制器中第几个中断(interrupts属性)。interrupt属性中有时会有2个参数,或者更多,这是因为比如有些中断不需要指定触发方式,这些参数的个数都是中断控制器来决定的。在设备树中搜索interrupt-controller就可以数出来当前系统有多少个中断控制器。有interrupt-controller这个属性的节点代表的便是中断控制器,中断控制器中#interrupt-cells属性的值表示它下一级的设备需要多少个32位的数据来描述某一个中断。比如我们数出来了当前系统设备树中有3个中断控制器,那么这3个中断控制器之间的关系是怎么样的呢?如下图,gpg和gpf是第一个中断控制器下面的子节点,所以gpg和gpf这2个中断控制器要想第一个中断控制器发送中断信息。
22.设备树的interrupt-extended属性(中断扩展属性)。比如说某块板子有4个按键(都可以产生按键中断),就可以用该属性描述设备树。其中第一个指定中断控制器,后面参数的个数由该中断控制器的#interrupt-cells来指定。intc控制器需要4个参数描述中断,第一个指定中断控制器,第二个指定主控制器,第三个指定子控制器,第四个指定中断号,第五个指定触发方式(上升沿,下降沿,双边沿,高电平,低电平)。该属性具体解释看韦东山的设备树视频的第五章第五小节。
23.I2C驱动程序分为:I2C总线驱动程序和I2C设备驱动程序。I2C总线驱动程序负责识别设备和提供读写接口,这个驱动程序内核中是有自带的。
24.字符设备驱动程序框架:应用层接口open,read,write;驱动层提供drv_open,drv_read,drv_write;再下面就是硬件层了。怎么构造驱动层接口呢,我们实现file_operations结构体就可以了,我们辛辛苦苦构造出来的驱动层的file_operations怎么用呢,我们告诉内核就可以了,怎么告诉内核呢,通过register_chrdev函数(这个可以简单的想象为它把file_operations结构体放到内核里面的某个数组,放到数组里的哪一项呢?放到主设备号下标那),那么谁来调用这个register_chrdev函数呢,我们在驱动的入口函数里调用这个注册函数。这就是字符设备驱动程序框架,非常简单。
25.config文件中宏的作用,比如CONFIG_MMC=y的作用就是执行对应Makefile时,看要不要将相应的目录编译进内核,如下: