1.bss(未初始的全局变量内存区域),数据段是初始化的全局变量区域段,两者都是静态内存分配。
代码段是存放程序执行代码的一个内存区域,一般是只读。
堆是动态分配的内存段,malloc/free操作,栈是存放用户程序临时创建的局部变量。
2.
U-Boot使用了一个存储在寄存器中的指针gd来记录全局数据区的地址:
#define DECLARE_GLOBAL_DATA_PTR register volatile gd_t *gd asm ("r8")
DECLARE_GLOBAL_DATA_PTR定义一个gd_t全局数据结构的指针,这个指针存放在指定的寄存器r8中。这个声明也避免编译器把r8分配给其它的变量。任何想要访问全局数据区的代码,只要代码开头加入“DECLARE_GLOBAL_DATA_PTR”一行代码,然后就可以使用gd指针来访问全局数据区了。
3. 关于rtc时间与系统时间有两个命令,分别是hwclock和date命令,两者相结合就可以测试出系统的rtc是否正常工作了。
4. 内核空间执行用户空间程序的方法: (1) call_usermodehelper (2) kernel_execve
最后都是通过int $0x80在内核空间发起一个系统调用来完成的。从用户态到内核态,必然有cs寄存器的值从_KERNEL_CS到_USER_CS的转变过程,
执行系统调用的函数是kene_execve,核心代码是 int $0x80,同时对应 struct pt_regs 将传递给C的参数在调用栈中准备好,为汇编程序调用C之前做好准备。
最后在 start_thread 中,将regs->cs修改为 _USER_CS,regs->ip = nex_ip,其实就是人为改变系统调用int $0x80指令压入堆栈的下条指令的地址,当系统
调用返回时,执行设定的地址指令。
5. linux设备加载的流程:linux启动过程中会扫描系统总线上所有的设备,对发现的每一个设备都调用device_add函数,然后此时udev被通知,负责找到
对应的驱动程序进行加载。
6. 睡眠等待不能用于中断上下文,因为中断上下文不允许执行schedule和睡眠(不能被调度,需要一次性执行完毕)。
7. 使用自旋锁时,其余线程获取不到锁只能原地打转,消耗CPU资源;而互斥锁获取不到会将当前线程置于睡眠状态。
8. 原子操作可以保证单次操作的串行化,不需要锁来保护。
9. linux内存管理要解决的几个关键问题是:(1). 隔离各个进程之间的地址 (2) 使用虚拟内存技术将暂时不用的数据写回磁盘使用时回读的做法,
效率很低 (3)程序运行的地址不能确定。
这些问题的解决通过软硬件配合的方法来解决,硬件上用MMU,通过虚拟地址查找物理地址,软件上采用虚拟内存技术,使各个进程之间只看到各自虚拟内存。
如果某个进程访问了异常的地址,内核被终止此进程并记录调用栈,这样也不用担心重定向的问题,因为程序永远按照虚拟地址空间来放置变量、代码不需要
重新定位。
软件上采用了分段和分页的思想,分段就是将程序所需的内存地址空间大小的虚拟地址空间映射到某个物理地址空间。分页是为了解决第二个问题,分页是将
内存地址空间划分为若干个很小的固定大小的页,划分的越小,内存和磁盘利用率就越高,同时利用程序局部性原理,程序运行的某一段时间内只有一部分数据
会用到,把常用的数据和代码页放到内存中,把不常使用的保存在磁盘中,当程序访问的也不在内存中时,会产生缺页异常,内核异常处理程序重新将对应的页
读入内存中。
10.