1.Linux进程结构与调度思想 * 进程

1.进程结构

在Linux系统下,如果将一个ELF格式可执行文件加载到内存中运行,则将演变成一个或多个进程.进程是Linux事务的基本管理单元,所有的进程均拥有自己的独立的环境和资源.进程的环境由当前系统状态及其父进程信息决定和组成.一个正在运行的进程在内存空间中申请的代码区、初始化数据区、未初始化数据区、上下文信息以及挂载的信号等等.
(1)代码区,加载的是可执行文件的代码段,其加载到内存中的位置由加载器完成.
(2)全局初始化数据区/静态数据区,加载的是可执行文件数据段,位置可位于代码段后也可以分开.程序在运行之初就为该数据段申请了空间,在程序退出时才释放,因此,存储于数据段的数据的生存周期为整个程序运行过程.
(3)未初始化数据区,加载的是可执行文件BBS段,位置可以分开也可以紧靠数据段.程序在运行之初为该部分申请了空间,在程序退出时才释放,存储于该部分的数据的生存周期为整个程序运行过程.
(4),由编译器自动分配释放.自动变量以及每次函数调用所需要保存的信息都存放在此段中.每次调用函数时,其返回地址以及调用者的环境信息都存放在栈中.然后,最近被调用的函数在栈上为其自动和临时变量分配存储空间.通过这种方式使用栈,可以递归的调用C函数.递归函数每次调用自身时,就使用一个新的栈帧,因此一个函数调用实例中的变量集不会影响另一个函数调用实例中的变量.
(5),通常在堆中进行动态存储分配.一般由程序员分配和释放,若程序员不释放,程序结束是由系统收回.堆位于非初始化数据段和栈之间.

2.调度思想:效率优先,兼顾公平:

  • 由于linux提供抢占式的多任务模式,所以linux能同时并发地交互执行多个进程,而调度程序将决定哪一个进程投入运行、何时运行、以及运行多长时间。调度程序是像linux这样的多任务操作系统的基础, 只有通过调度程序的合理调度,系统资源才能最大限度地发挥作用,多进程才会有并发执行的效果。当系统中可运行的进程数目比处理器的个数多,就注定在某一时刻有一些进程不能执行,这些不能执行的进程在等待执行。调度程序的基本工作就是停止一个进程的运行,再在这些等待执行的进程中选择一个来执行。
  • 处理器的调度策略决定调度程序在何时让什么进程投入运行。调度策略通常需要在进程响应迅速(相应时间短)和进程吞吐量高之间寻找平衡。所以调度程序通常采用一套非常复杂的算法来决定最值得运行的进程投入运行。调度算法中最基本的一类当然就是基于优先级的调度,也就是说优先级高的先运行,相同优先级的按轮转式进行调度。优先级高 的进程使用的时间片也长。调度程序总是选择时间片未用尽且优先级最高的进程运行。这句话就是说用户和系统可以通过设置进程的优先级来响应系统的调度。基于此,linux设计上一套动态优先级的调度方法。一开始,先为进程设置一个基本的优先级,然而它允许调度程序根据需要来加减优先级。

3.睡眠(等待)时间

饥饿

2.操作系统如何调用设备的 * 驱动

  • 1.对设备的用户级访问往往要通过位于/dev目录下的特殊设备文件。内核把对这些文件操作映射到对驱动程序代码的调用上面。
  • 2.大多数硬件设备都在/dev目录中有一个对应的设备文件,网络设备除外。在/dev中的每个文件都有与之相关的主设备号和一个次设备号内核用这些设备号把对一个设备文件的引用映射到相应的驱动程序上。主设备号标明与文件相关的驱动程序(换句话说是设备类型)。次设备号常常是指定某种给定设备类型的特定实例,次设备号有时被称为单元号。
  • 3.设备文件分两种类型:
    块设备文件:
    一个块设备文件每次读取或者写入一块数据(一组字节,通常是521的倍数),我们熟知的磁盘就是块设备,在/dev中对应的设备文件就是块设备文件。块设备文件在用ls -l查看时文件类型为b。
    字符设备文件:
    字符设备每次读取或者写入一个字节。磁盘和磁带可以是块设备也可以是字符设备,而终端和打印机不行。字符设备文件在用ls -l查看时文件类型为c。
  • 4.在Linux下,一般不需要手动创建设备文件,因为在Linux下设备文件的创建有专门的udev系统来管理,当系统有新的设备出现(或者消失),会动态地管理设备文件的创建和删除。守护进程udevd监听内核传来的有关设备状态变化的消息。根据/etc/udev 和/lib/udev两个目录的配置信息,在找到设备或者断开设备的时候,udevd能够自动采取相应措施。在默认情况下,它只创建/dev里的设备文件。手动创建设备文件用mknod命令来创建 。

3.为什么点击键盘产生中断——键盘点击产生中断的过程 * 中断

当用户按键时,键盘接口会得到一个代表该按键的键盘扫描码,同时产生一个中断请求。
键盘中断服务程序先从键盘接口取得按键的扫描码,然后根据其扫描码判断用户所按的键并作相应的处理,最后通知中断控制器本次中断结束并实现中断返回。

当键盘上的一个按键按下时,键盘会发送一个中断信号给CPU,与此同时,键盘会在指定端口(0x60) 输出一个数值,这个数值对应按键的扫描码(make code),当按键弹起时,键盘又给端口输出一个数值,这个数值叫断码(break code).
处理键盘中断时,要获取按键的扫描码和断码的数值,同时将数值转换为字符串,最后再将字符串的每一个字符绘制到界面上。这一系列其实是很耗时的计算。假设这时候有网络数据抵达系统,但是CPU忙于处理键盘中断,不能及时接收网络数据,这样,系统便会丢失网络数据。
所以,对于中断,我们要尽可能快的处理,然后把控制器交还给原来的任务。对于键盘中断,我们可以把键盘发送的扫描码和断码数值缓存起来,然后把控制器交换给原来任务,等到CPU稍微空闲时再处理键盘事件,以设置一个缓冲区。

4.fd是什么东西,open与write * 文件系统

我们都知道在Linux下一切皆文件。当然设备也不例外,如果要对某个设备进行操作,就不得不打开此设备文件,打开文件就会获得该文件的文件描述符fd( file discriptor), 它就是一个很小的整数,每个进程在PCB(Process Control Block)中保存着一份文件描述符表,文件描述符就是这个表的索引,每个表项都有一个指向已打开文件的指针。
总之,fd:为打开文件的文件描述符,而每个进程都有一张文件描述符表,fd文件描述符就是这张表的索引,同样这张表中有一表项,该表项又是指向前面提到打开文件的file结构体,file结构体才是内核中用来描述文件属性的结构体。

  • open:
    注释:
    (1)函数说明:用来打开一个已经存在的文件或者创建一个普通文件
    (2)参数解释:
    pathname:要打开或创建的目标文件
    flags:打开文件时,可以传入多个参数选项,用下面的一个或多个进行“或”运算,构成flags;
    参数:O_RDONLY—只读打开、 Q_WRONLY—只写打开、O_RDWR—读、写打开 ;这三个变量只能指定一个
    O_CREAT—若文件不存在,则创建它。需要使用mode(文件权限标志)选项,来指明新文件的访问权限
    O_APPEND—追加写
    (3)返回值:成功返回新打开文件的描述符,失败则返回-1
  • read:
    注释:
    (1)函数说明:是从 fd所描述的打开文件中读取 buf所指缓冲区中的 n个字节。
    (2)参数说明
    fd:文件描述符,用来指向要操作的文件的文件结构体
    buf:一块内存空间
    count:请求读取的字节数,读上来的数据保存在缓冲区buf中,同时文件的当前读写位置向后移。
    ssize_t:有符号整型
    (3)返回值:成功返回读取的字节数,出错返回-1并设置errno,如果在调用read之前已到达文件末尾,则这次read返回0。
    注意:读操作从文件的当前位移量处开始,在成功返回之前,该位移量增加实际读的字数。
  • write:
    注释:
    (1)函数说明:将 buf所指向的缓冲区的 n字节写入 fd 所描述的打开文件中
    (2)参数说明:
    fd:文件指针
    buf:写入的数据保存在缓冲区buf中,同时文件的当前读写位置向后移
    count:请求写入的字节数
    (3)返回值:成功返回写入的字节数,出错返回-1并设置errno写常规文件时,write的返回值通常等于请求写的字节数count,而向终端或者网络写则不一定。
  • close:
    注释:
    (1)函数说明:关闭指定文件
    (2)参数解释:fd—文件描述符,用来指向要操作的文件的文件结构体
    (3)返回值:若成功返回0,出错返回-1;
    注意:关闭一个文件时也释放该进程加在该文件上的所有记录锁,当一个进程终止时,它所有的打开文件都由内核自动关闭。

5.文件系统:已注册文件和mount系统的关系 * 文件系统

  • 1.Linux内核是可加载的,许多模块式可选的,只有真正需要使用时才加载他们。文件系统注册过程就是把对应某类型文件系统相关的模块加载到内核,并创建相关的数据结构。每个文件系统模块都有一个初始化例程,它的作用就是VFS中进行注册,即填写一个叫做file_system_type的数据结构。所有已注册的文件系统的file_system_type结构形成一个链表,我们把这个链表称为注册链表。
  • 2.每个设备在mount时都要搜索该注册链表,选择适合自己设备文件系统的一项,并从中取出read_super()函数获取设备的超级块(存储在具体设备上,记录存储设备各种信息的一个存储块),并解析其内容。因为每种类型文件系统的超级块的格式不同,并且各自有特定的信息,每种文件系统必须使用对应的解析函数,否则内核就因为不认识该文件系统而无法完成安装。这就是注册文件系统的意义所在。(生成对应file_system_type文件系统结构并提供相应的xxx_get_sb函数)

6.内核启动 * 启动过程

  • 1.基本的硬件初始化(stage1)
    • 屏蔽所有的中断
    • 设置 CPU 的速度和时钟频率。
    • RAM 初始化
    • 初始化 LED
    • 关闭 CPU 内部指令/数据 cache。
    • 为加载 stage2 准备 RAM 空间
  • 2.(stage2)
    • 拷贝 stage2 到 RAM 中
    • 设置堆栈指针 sp
    • 跳转到 stage2 的 C 入口点
  • 3.初始化本阶段要使用到的硬件设备(加载设备驱动)
  • 4.检测系统的内存映射
  • 5.加载内核映像和根文件系统映像、加载磁盘文件系统
  • 6.设置内核的启动参数
  • 7.调用内核,调用init()
  • 8.调用用户进程
  • 9.启动完成

7.根文件系统内容,根文件系统初始化,根文件系统挂载 * 根文件系统

参考
正常来说,根文件系统至少包括以下目录:
/etc/:存储重要的配置文件。
/bin/:存储常用且开机时必须用到的执行文件。
/sbin/:存储着开机过程中所需的系统执行文件。
/lib/:存储/bin/及/sbin/的执行文件所需的链接库,以及Linux的内核模块。
/dev/:存储设备文件。

注:五大目录必须存储在根文件系统上,缺一不可。

  • 1.创建必要的挂载点目录/dev、/root、/sys、/proc等;然后,将VFS中的sysfs挂载到rootfs的/sys目录下,将tmpfs挂载到/dev目录下(/dev的文件系统类型为tmpfs);最后,为了输出打印信息,创建了/dev/console、/dev/null两个特殊的设备文件。
  • 2.磁盘文件系统的挂载一般有两种方式:本地方式和网络方式。根据BOOT变量的值,init选择执行本地加载或者网络加载,如果是本地加载则执行/scripts/local脚本;如果是网络加载则执行/scripts/nfs脚本。个人pc一般都是本地加载,数据中心的服务器一般是nfs加载。最后,由init程序调用/scripts/local脚本挂载磁盘文件系统。
  • 3.成功挂载磁盘文件系统后,需要将rootfs下的/sys、/proc、/dev等重要的目录都迁移到磁盘文件系统下。最后,通过调用/sbin/run-init程序将内核的根文件系统从rootfs切换到磁盘文件系统的根目录。

根文件系统之所以在前面加一个”根“,说明它是加载其它文件系统的”根“,那么如果没有这个根,其它的文件系统也就没有办法进行加载的。
根文件系统包含系统启动时所必须的目录和关键性的文件,以及使其他文件系统得以挂载(mount)所必要的文件。例如:
init进程的应用程序必须运行在根文件系统上;
根文件系统提供了根目录“/”;
linux挂载分区时所依赖的信息存放于根文件系统/etc/fstab这个文件中;
shell命令程序必须运行在根文件系统上,譬如ls、cd等命令;
总之:一套linux体系,只有内核本身是不能工作的,必须要rootfs(上的etc目录下的配置文件、/bin /sbin等目录下的shell命令,还有/lib目录下的库文件等···)相配合才能工作。
Linux启动时,第一个必须挂载的是根文件系统;若系统不能从指定设备上挂载根文件系统,则系统会出错而退出启动。成功之后可以自动或手动挂载其他的文件系统。因此,一个系统中可以同时存在不同的文件系统。在 Linux 中将一个文件系统与一个存储设备关联起来的过程称为挂载(mount)。使用 mount 命令将一个文件系统附着到当前文件系统层次结构中(根)。在执行挂装时,要提供文件系统类型、文件系统和一个挂装点。根文件系统被挂载到根目录下“/”上后,在根目录下就有根文件系统的各个目录,文件:/bin /sbin /mnt等,再将其他分区挂接到/mnt目录上,/mnt目录下就有这个分区的各个目录和文件。

8.交叉编译环境建立 * 环境搭建

交叉开发环境是指用于嵌入式软件开发的所有工具软件的集合,一般包括:

  • 文本编辑器
  • 交叉编译器
  • 交叉调试器
  • 仿真器
  • 下载器等

交叉开发:在一台通用计算机上进行软件的编辑编译,然后下载到嵌入式设备中运行调试的开发方式。开发计算机一般称宿主机,嵌入式设备称为目标机,在宿主机上编译好的程序,下载到目标机上运行。

交叉开发环境一般由运行于宿主机上的交叉开发软件、宿主机到目标机的调试通道组成。

交叉编译环境:下载交叉编译工具链-设置参数-编译-安装

9.嵌入式Linux开发主要软件工作 * 开发流程

开发主要流程:
建立开发环境
配置开发主机
配置网络
建立引导装载程序BOOTLOADER
下载移植好的LINUX 操作系统
建立根文件系统
建立应用程序的flash 磁盘分区
开发应用程序
主要软件工作:
BootLoader 引导的移植与开发
Kernel 内核的剪裁移植与开发
FS 文件系统的开发
Driver 设备驱动的移植与开发
GUI 交互界面的设计与开发
APP 应用程序开发

10.uboot的板级移植/CPU移植 * 移植

  • 获得发布的最新版本U-Boot源码,阅读相关文档,配置编译环境,在建立的开发环境下进行移植工作。
  • 在目标板与开发主机间接入硬件调试器。这是进行U-Boot移植应当具备且非常关键的调试工具。
    如果在参考开发板上移植U-Boot,可能需要移除目标板上已有的BOOT LOADER。

11.不同平台移植后的程序无法运行原因分析 * 原因分析

缺少必要的运行库,或者程序没有被烧录进去,版本的问题,缺少编译环境