《拉依达的嵌入式\驱动面试宝典》—操作系统篇(一)
《拉依达的嵌入式\驱动面试宝典》—操作系统篇(一)
你好,我是拉依达。
感谢所有阅读关注我的同学支持,目前博客累计阅读 27w,关注1.5w人。其中博客《最全Linux驱动开发全流程详细解析(持续更新)-CSDN博客》已经是 Linux驱动
相关内容搜索的推荐首位,感谢大家支持。
《拉依达的嵌入式\驱动面试宝典》 最开始我个人的面试学习笔记,里面整合了所有我认为可能遇到的技术面试问题。随着我个人的学习以及参与面试,对内容不断完善。现在我已经步入工作阶段,空闲时间将之前的学习内容进行重新编排整理,并且加入了工作后更新系统的理解。是所有博客中投入最大的一个系列。包含我个人学习中所有的精华内容,希望可以最大限度的帮助到你。
所有问题及其答案均是我个人学习后查阅资料总结,每个回答都做了仔细的分析,在嵌入式及其驱动面试相关问题解析做到独一无二。
其中包含嵌入式软件开发、嵌入式驱动开发、linux驱动开发等职位遇到的所有技术方向问题。**尤其是对驱动等底层问题的解析,适合准备相关工作或者学习提升的同学 **
————————————————————————————————————————————
三、操作系统
3.1 进程线程管理
操作系统定义?
整个系统完成最基本功能和系统管理的部分。
操作系统组成?
内核、设备驱动程序、启动引导程序、命令行shell/用户界面、文件管理工具、系统工具
内核组成?
- 负责响应中断——中断服务程序
- 负责管理多个进程——调度程序
- 负责管理进程地址空间——内存管理程序
- 网络、进程通信等——系统服务程序
进程的生命流程
- 父进程调用fork()函数,从一个已有进程复制出一个全新的进程
- 调用exec()函数,创建新的地址空间,载入新的程序
- 调用exit() 函数,退出执行,终结进程并释放占用资源
- 父进程调用wait()函数,查询子进程是否终结
进程描述符 task_struct
- 内核把进程存放在叫任务队列(task list)的双向链表中
- 任务队列(链表)中每一项就是一个task_struct,进程描述符
- 进程描述符包含进程完整信息:打开文件、进程地址空间、挂起信号、进程状态等
- 进程描述符的分配:slab分配器
- 进程的标识:PID号,存放在进程描述符中
进程家族树
- 初始进程——init进程,pid=1,内核启动阶段创建
- 父进程——每个进程都有一个父进程,task_struct 中parent指针指向
- 子进程——每个进程有多个子进程,task_struc中children子进程链表
进程的退出
- 进程终结的清理工作和进程描述符删除工作分开进程
- 进程最后调用exit()函数,进程释放内存资源,进行清理。保留进程的进程描述符task_struct 。
- 父进程获得子进程的信息后,子进程的进程描述符删除
什么的进程?什么是线程?
- 进程是资源分配的基本单位,它是程序执行时的一个实例,在程序运行时创建。拥有自己的地址空间。
- 线程是程序执行的最小单位,是进程的一个执行流,一个进程由多个线程组成的。多个线程共用地址空间
- Linux中线程和进程不做区分,线程是一种特殊的进程,本质都是内核的一个task_struct。线程被看作是一个和其他进程共用某些资源的进程。
- 同一时间,如果CPU是单核,只有一个进程在执行,所谓的并发执行,也是顺序执行,只不过由于切换速度太快,你以为这些进程在同步执行而已。多核CPU可以同一时间点有多个进程在执行。
进程的组成
进程实体(进程映像)由三部分组成:程序段、数据段、PCB(进程控制块)组成。
PCB是进程存在的唯一标志。PCB是给操作系统用的,而程序段和数据段是给进程自己使用的。
进程和线程的区别?
-
进程是资源分配的最小单位。
线程是程序执行的最小单位,也是处理器调度的基本单位。两者均可并发执行。
-
进程有自己的独立地址空间,每启动一个进程,系统就会为它分配地址空间,建立数据表来维护代码段、堆栈段和数据段,这种操作非常昂贵。
而线程是共享进程中的数据,使用相同的地址空间,因此,CPU切换一个线程的花费远比进程小很多,同时创建一个线程的开销也比进程小很多。
-
线程之间的通信更方便,同一进程下的线程共享全局变量、静态变量等数据,
进程之间的通信需要以通信的方式(IPC)进行。
-
进程切换时,消耗的资源大,效率低。多进程程序更健壮,一个进程死掉并不会对另外一个进程造成影响址空间。
线程切换时,消耗的资源小,效率高。多线程程序只要有一个线程死掉,整个进程也跟着死掉了。
-
执行过程:每个独立的进程有一个程序运行的入口、顺序执行序列和程序入口。但是线程不能独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。
-
线程执行开销小,但是不利于资源的管理和保护。进程执行开销大,但是能够很好的进行资源管理和保护,可以跨机器迁移。
什么时候用多进程?什么时候用多线程?
- 创建和销毁较频繁使用线程,因为创建进程花销大。
- 需要大量数据传送使用线程,因为多线程切换速度快,不需要跨越进程边界。
- 安全稳定选进程;快速频繁选线程;
多进程、多线程同步(通讯)的方法
进程间通讯:
(1)有名管道/无名管道(2)信号(3)共享内存(4)消息队列(5)信号量(6)socket
线程通讯(锁):
(1)信号量(2)读写锁(3)条件变量(4)互斥锁(5)自旋锁
线程可以独立运行吗?一个线程崩溃会导致整个进程崩溃吗?
线程不能独立运行,但一个线程崩溃不一定导致整个进程崩溃。
(1)线程属于进程,线程的运行需要依赖进程的地址空间和系统资源。
(2)线程崩溃的本质就是内存出错,若出错的内存没有被其他线程访问,则不会导致其他线程出错,也就不会导致进程崩溃。
不同线程之间不共享的内容?
栈、寄存器、阻塞信号掩码、线程号。
Linux进程状态
五种基本状态:创建、运行、就绪、阻塞、终止
- 创建状态:一个应用程序从系统上启动,首先就是进入创建状态,需要获取系统资源创建进程管理块(PCB:Process Control Block)完成资源分配。
- 就绪状态:在创建状态完成之后,进程已经准备好,但是还未获得处理器资源,无法运行。
- 运行状态:获取处理器资源,被系统调度,开始进入运行状态。如果进程的时间片用完了就进入就绪状态。
- 阻塞状态:在运行状态期间,如果进行了阻塞的操作,如耗时的I/O操作,此时进程暂时无法操作就进入到了阻塞状态,在这些操作完成后就进入就绪状态。
- 终止状态:进程结束或者被系统终止,进入终止状态
select、poll 和epoll的区别
-
select,poll,epoll都是IO多路复用的机制。可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作。但select,poll,epoll本质上都是同步I/O,因为他们都需要在读写事件就绪后自己负责进行读写,也就是说这个读写过程是阻塞的,而异步I/O则无需自己负责进行读写,异步I/O的实现会负责把数据从内核拷贝到用户空间。
-
select的时间复杂度O(n)。select采用无差别轮询所有流的方式,找出事件发生的流。最大只能支持1024个文件描述符,每次使用都需要把文件描述符集合从用户态拷贝到内核态,且内核需要遍历所有的fd,这在fd很多的时候会造成大的开销。
-
poll 和select 本质没有区别,但是它没有最大连接数的限制,原因是它是基于链表来存储的
-
epoll的时间复杂度O(1)。epoll没有了文件描述符的限制,使用一个文件描述符管理多个描述符。只在注册的时候拷贝没有添加的文件描述符到红黑树上,使用回调函数来返回数据,效率大大提高。
join和detach的区别?
join在当前线程调用join的话,该线程会等待子线程运行结束再继续向下运行。
而detach则是让子线程在后台运行。一般如果子线程和当前线程的一些资源的话,那么该线程就需要使用join来等待子线程使用完自己再向下执行,否则当前线程执行完把资源释放掉就会出错。
fork()和vfork()的作用以及区别
fork()和vfork()都是创建一个进程
- fork()子进程拷贝父进程所有资源,只有pid和ppid不一样;(实际是写时拷贝:平时是只读共享,当写入时才会拷贝)
- vfork()出来的子进程会共享父进程的代码段和数据段,但是子进程会拥有自己的堆栈,而父进程会暂时被挂起,直到子进程调用exec()或者exit()函数才会恢复
- fork()父子进程的执行次序不确定;vfork()保证子进程先运行,在调用exec()或者exit()函数才运行父进程
- clone:选择性的和父进程共享一部分东西。
子进程从父进程继承的资源有哪些?
子进程继承父进程的绝大部分资源,包括堆栈、内存、用户号和组号、打开的文件描述符、当前工作目录、根目录。
子进程独有进程号、不同的父进程号、自己的文件描述符。
cpu处理器的三种活动情况
- 运行用户空间,执行用户进程。(如:常规的用户态程序)
- 运行内核空间,处于进程上下文,代表某个特定的进程执行。进程通过进程上下文和内核关联,可以睡眠,可以进行调度(如:用户态程序进行系统调用)
- 运行内核空间,处于中断上下文,和任何一个进程都无关,处理特定的中断。中断上下文和进程无关,没有后备进程,不能睡眠。因为睡眠系统会进行调度,用后备进程代替当前进程(如:中断服务程序,cpu空闲的空进程)