Linux进程线程相关面试题【备战春招秋招】

本文介绍了面试中常被问到的进程调度策略、进程间通信(IPC)、进程状态、fork函数、僵尸进程回收、守护进程、写实拷贝与引用计数、进程与程序的区别、线程属性、资源共享、线程同步、死锁及其避免、作业、进程、线程和管程定义,以及多进程和多线程的应用场景。
摘要由CSDN通过智能技术生成

前言

        该文章只针对面试时面试官提问如何回答的更全更好,看此文章没有讲解太多太细节的知识点。如果知识点本身不会,背诵此文章可能能让你找到一份工作,但不能让你持续的干下去。还是需要自身精通对应知识点。

        该文章适合有学习过Linux进程线程的朋友阅读,主要是针对本科应届生。收录了十几道近几年非常经典的进程线程相关面试题。

        配合阅读Linux面试题专栏文章更容易帮您找到一份嵌入式开发工作。

进程

一、操作系统中进程调度策略有几种?

  1. 先来先服务(FCFS):按照进程到达的顺序进行调度,先到达的进程先被执行。
  2. 短作业优先(SJF):选择执行时间最短的进程优先执行,以减少平均等待时间。
  3. 优先级调度:根据进程的优先级来决定执行顺序,优先级高的进程先执行。
  4. 时间片轮转(Round Robin):每个进程被分配一个时间片,当时间片用完时,进程被暂停并放到队列末尾,轮流执行直到所有进程完成。
  5. 多级反馈队列调度:将进程按照优先级划分到不同的队列,每个队列有不同的时间片大小,进程根据优先级在不同队列中执行。
  6. 最短剩余时间优先(SRTF):在SJF的基础上,动态调整执行中的进程,如果有新进程到达且其执行时间比当前进程剩余时间短,则切换执行新进程。

扩展问题:Linux的任务调度机制

Linux的任务调度机制主要涉及进程调度,‌将进程分为不同类别以适应不同的需求,‌包括交互式进程、‌批处理进程和实时进程。‌此外,‌Linux还支持实时进程和普通进程的调度,‌其中实时进程应优先于普通进程运行。‌Linux中的任务调度机制包括多种调度策略,‌如FIFO(‌先来先服务)‌、‌RR(‌时间片轮转)‌等,‌每个进程有两个优先级(‌动态优先级和静态优先级)‌,‌而实时进程则有第三种优先级,‌即实时优先级。‌

  • 交互式进程:‌这类进程涉及大量的人机交互,‌需要不断处于睡眠状态等待用户输入,‌对系统响应时间要求较高,‌如编辑器等。‌
  • 批处理进程:‌不需要人机交互,‌在后台运行,‌可以占用大量系统资源,‌但能忍受较长的响应延迟,‌如编译器。‌
  • 实时进程:‌对调度延迟的要求最高,‌执行重要操作需要立即响应,‌如视频播放软件或飞机飞行控制系统。‌实时进程采用FIFO或RR调度策略,‌具有实时优先级,‌确保及时响应。‌

Linux中的调度器根据进程的类型和优先级决定哪个进程应该获得CPU时间。‌例如,‌CFS(‌Completely Fair Scheduler)‌调度器主要用来调度普通任务,‌而实时任务则采用其他调度策略如EDF(‌最早截止时间优先)‌等。‌这种机制确保了系统能够高效地管理和分配资源,‌满足不同类型进程的需求

二、进程间通信(IPC)有哪些?各有哪些特点?

  1. 管道(Pipe):单向通信,适用于有亲缘关系的进程间通信。
  2. 命名管道(Named Pipe):允许无亲缘关系的进程间进行通信。
  3. 信号(Signal):用于通知进程发生了某个事件,如中断、异常等
  4. 共享内存(Shared Memory):多个进程共享同一块内存区域,实现高效的数据交换。
  5. 消息队列(Message Queue):消息的链表,允许多个进程向同一个队列发送消息。
  6. 信号量(Semaphore):用于进程间同步和互斥访问共享资源,控制进程对共享资源的访问。
  7. 套接字(Socket):用于不同主机间的进程通信,可以实现网络通信。

三、为什么需要IPC?

  1. 进程间通信:不同进程之间需要进行数据交换和协作。
  2. 资源共享:多个进程需要共享资源,如内存、文件等。
  3. 并发控制:需要协调多个进程的执行顺序和互斥访问。
  4. 模块化设计:将系统拆分为多个独立的模块,需要模块间通信。

四、说出进程中的基本状态,并简述其概念

基本状态有三种:就绪态、运行态、等待态。其中等待态包含可中断等待态不可中断等待态

  • 就绪态:进程所有需要的条件已经准备完成,等待CPU的调度
  • 运行态(R):进程占用CPU,并在CPU上运行
  • 可中断等待态(S):进程正在休眠,等待某个资源来唤醒它。也可以被其他信号中断唤醒
  • 不可中断等待态(D):进程正在休眠,等待某个资源来唤醒它。不能被其他信号中断唤醒

补充其它状态:

  • 停止态(T):进程暂停接受某种处理。例如:gdb调整断点信息处理
  • 僵尸态(Z):进程已经结束但还没有释放进程资源。例如:PID,PPID未释放
  • 退出态(X):也被称为死亡状态,进程即将被销毁,状态时间很短,几乎不会被捕捉到

状态转换图如下:

五、fork函数如何使用?为何fork的返回值如此设计?

        函数原型为:pid_t fork(void);

        是UNIX或类UNIX的系统调用,用于创建子进程。若成功调用一次则返回两个值,子进程返回0,父进程返回子进程ID(PID);否则,出错返回-1。UNIX将复制父进程的地址空间内容给子进程,因此,子进程有了独立的地址空间。

        如此返回的原因是任何子进程都只有一个父进程,而且子进程总是可以通过getppid函数获取父进程ID,相反一个父进程可以有多个子进程,而且无法获取各子进程ID。如果父进程想跟踪所有的子进程,则需要记录每次调用fork函数的返回值。

六、为何进程需要有僵尸态?描述几种僵尸子进程回收的方法。

        僵尸态是进程退出后,其父进程没有进行收尸操作而进入的状态。此时依然占用资源。不直接释放资源的原因在于父进程可能需要获取子进程的退出状态,用来判断子进程运行情况。若子进程因异常退出而直接释放不进入僵尸态,父进程无法得到结果则无法处理这些异常。

        回收僵尸子进程可使用 pid_t wait(int *status); 函数,功能为回收僵尸态子进程,如果子进程还未结束则阻塞等待,如果没有子进程则立刻返回。

        或使用pid_t  waitpid(pid_t pid, int *status, int options); 函数,功能为按照指定的方式探测指定子进程状态改变,回收僵尸态子进程。可通过配置options参数改成非阻塞方式等待。

七、守护进程的功能是什么?如何创建守护进程?

        通常说的是Daemon进程,是linux中的后台服务进程,主要用于执行特定的系统任务。独立于控制终端并且周期性的执行某种任务或等待处理某些发生的事件。守护进程有以下特点:

  • 后台运行,独立于终端,完成一定的任务
  • 一般是在图形界面或是终端不可见
  • 运行之后不接收终端的输入也不向终端输出
  • 除开以上特性外,守护进程与普通进程基本上没有区别

创建守护进程的步骤:

  1. 创建子进程,父进程退出  (fork(),exit())
  2. 在子进程中创建的新会话[脱离控制终端](setsid())
    • setsid将当前进程变为新的会话头进程,以及新进程组组长,从而不在有控制终端
    • 只有不是进程组的组长才能调用setsid函数
  3. 忽略SIGHUP信号 signal(SIGHUP, SIG_IGN);
    1. 当会话头进程退出时,所有的子进程都会收到这个信号,所以需要忽略掉,为后面二次创建子进程做铺垫
  4. 再次创建子进程,退出父进程
    1. 创建两次子进程的目的是确保成为守护进程的进程不是会话头进程
  5. 改变进程的工作目录到"/"  (chdir("/"))
    1. 守护进程所在的目录是不允许删除等其他操作的,防止后续需要修改此目录改为不可删除的根目录
  6. 重设文件掩码(umask(0))
  7. 关掉不需要的文件描述符 (close(0)  close(1)   close(2))

八、简述写实拷贝技术 与 引用计数技术

        在早期Linux内核设计时,每创建一个新的子进程则会直接复制父进程的地址空间,如果创建子进程数量过多会极大的消耗掉内存空间,在3.0之后版本的内核中引入了写实拷贝技术,该技术在创建子进程时并不会立即复制整个父进程地址空间,此时子进程与父进程是共享同一个地址空间,直到其中某个进程执行“写”操作时,才会创建一个新的地址空间

        在创建子进程时,所有由父进程打开的文件描述符也会被复制到子进程中,此时会有两个文件描述符在内核中指向同一个file结构体,file结构体中包含一个引用计数器,该计数器会自增,当其中一个进程调用close()关闭文件时,计数器会自减,只有减到0的时候才会真正关闭该文件

九、请描述进程和程序的区别?

程序是静态的,它是一些保存在磁盘上的指令的有序集合,没有任何执行的概念
进程是一个动态的概念,它是程序执行的过程,包括创建、调度和消亡

  • 进程是一个独立的可调度的任务
  • 进程是一个抽象实体。当系统在执行某个程序时,分配和释放的各种资源
  • 进程是一个程序的一次执行的过程
  • 进程是程序执行和资源管理的最小单位

线程

一、线程的属性有哪些?他们有什么区别?如何设置线程的属性?

        线程的属性分为结合式属性分离式属性。在任何一个时间点上,线程是可结合的(joinable),或者是分离的(detached)。一个可结合的线程能够被其他线程收回其资源和杀死;在被其他线程回收之前,它的存储器资源(如栈)是不释放的。相反,一个分离的线程是不能被其他线程回收或杀死的,它的存储器资源在它终止时由系统自动释放

        线程属性默认是结合式,设置为分离式可以在创建线程时,配置pthread_create()函数的第二个参数,也可以在创建子线程后调用pthread_detach()函数改为分离式线程。

二、线程间有哪些资源是共享的?哪些资源是独有的?

        共享资源:全局变量,进程中打开的文件描述符等,用户ID等

        私有资源:线程ID(tid),系统相关的数据[函数运行环境],局部变量,私有栈

三、线程同步与互斥的具体实现方法

互斥:一个线程访问资源的时候,不允许其他线程访问

        实现互斥可以使用互斥锁,将临界区锁住即可

        使用互斥锁需要先定义并初始化,上锁与解锁函数分别为pthread_mutex_lock() 和pthread_mutex_unlock(),用完后需要销毁互斥锁pthread_mutex_destroy()。

临界资源:多个线程/进程共享的资源,且同一时刻只能有一个人使用的资源

临界区:访问临界资源的代码

同步:线程间相互配合,按照一定的顺序完成任务

实现同步可以使用条件变量信号量实现

        条件变量利用线程间共享的全局变量进行同步通信的一种机制。多个线程等待某个条件满足,在条件达到时由一个线程去通知其他等待的线程。条件变量总是和互斥锁结合在一起使用。

        实现流程是先用互斥锁将临界区锁住在使用资源时进行判断,如果不满足条件则使用条件变量进行阻塞pthread_cond_wait(),该函数在阻塞的同时还会释放掉互斥锁,此时其他线程拿到资源后开始执行,另一个线程执行结束后唤醒该阻塞的线程即可, pthread_cond_broadcast()函数唤醒所有等待的线程。

        信号量是一个与队列有关的整型变量,实现同步的方式主要有P操作与V操作。

        实现流程是先分配资源每个线程从自己的资源区获取资源,只有拿到资源才可以执行,申请资源使用的则是P操作sem_wait(),执行结束后将该资源传递到其它线程的资源区,也就是V操作sem_post()。

        

四、死锁是如何产生的?怎么避免死锁的产生

死锁的产生条件必须满足以下四个

  • 互斥条件:资源在同一时刻只能被一个进程或线程使用。
  • 请求与保持条件:一个进程在等待资源时,会保持已经获得的资源,同时继续请求其他资源。
  • 不剥夺条件:已经获得的资源在未使用完之前,不能被剥夺或抢占。
  • 循环等待条件:存在一个进程资源请求的环形链,即一个进程等待另一个进程占用的资源,而后者又在等待前者占用的资源。

避免死锁的产生只需要打破以上任意一个条件即可,其中第一个条件不可打破

  • 打破请求与保持条件:资源分配策略上采取措施,给资源设定优先级,申请顺序必有由高到低,或者将多个资源整合成一个资源。
  • 打破不剥夺条件:使用可剥夺资源,允许关键资源共享。
  • 打破循环等待条件:使用顺序锁或轮询锁,确保资源正确获取和释放。

如果无法打破条件,产生死锁后如何解除有以下方式:

  • 检测死锁:当死锁发生时,系统能够检测到并采取措施修复,如重启服务或终止线程。
  • 超时重试:设置超时机制,当进程等待资源超时时,释放已占有的资源。

五、简述线程池的概念与作用

        线程池是一种管理和复用线程的机制,它包含一组预先创建的线程,用于执行任务队列中的任务。线程池的作用包括:

  • 提高性能:减少线程创建和销毁的开销,避免频繁创建线程带来的性能损耗,提高系统的响应速度和吞吐量。
  • 控制并发度:通过限制线程池中线程的数量,可以控制系统的并发度,避免因线程过多导致系统资源耗尽或性能下降。
  • 管理线程:线程池可以统一管理线程的生命周期,包括线程的创建、销毁、复用和调度,简化了线程管理的复杂性。
  • 提高系统稳定性:线程池可以有效地管理系统资源,避免因线程过多导致系统崩溃或资源耗尽的情况,提高系统的稳定性和可靠性。
  • 减少资源消耗:线程池可以重复利用线程,减少线程创建和销毁的开销,降低系统资源消耗,提高系统的效率和性能。

综合类问题

一、试解释操作系统原理中的作业、进程、线程、管程各自的定义

        作业:用户在一次解题或一个事务处理过程中要求计算机系统所做工作的集合。它包括用户程序、所需要的数据及控制命令等。作业是由一系列有序的步骤组成的

        进程一个程序在一个数据集合上的一次运行过程。所以一个程序在不同数据集合上运行,乃至一个程序在同样数据集合上的多次运行都是不同的进程。

        线程线程是进程中的一个实体,是被系统独立调度和执行的基本单位

        管程管程实际上是定义了一个数据结构和在该数据结构上的能为并发进程所执行的一组操作,这组操作能同步进程和改变管程中的数据。

二、进程和线程的区别有哪些?什么时候使用多进程?什么时候使用多线程?

进程和线程的区别

  1. 资源分配:进程是操作系统分配资源的基本单位,包括独立的内存空间和文件描述符;线程是进程内的执行单元,共享进程的资源。
  2. 通信:进程间通信需要额外的机制,如管道、消息队列;线程间通信更方便,可以直接共享内存。
  3. 切换开销:进程切换开销大,涉及到上下文切换;线程切换开销小,因为线程共享进程的地址空间。
  4. 独立性:进程间相互独立,一个进程崩溃不会影响其他进程;线程共享进程的资源,一个线程崩溃可能导致整个进程崩溃。

多进程和多线程的选择:

多进程适合于需要独立运行、相互隔离、稳定性要求高的场景,如服务器端程序、并行计算等。

多线程适合于需要共享数据、资源共享、响应速度要求高的场景,如图形界面程序、网络编程等。

结语

        编写该文章目的主要为想从事相关工作的同学找到一份好的工作,以上题目在面试中经常出现,如果有在外面试的朋友发现有更常见更经典的题目也可以私信告知,后续也会更新到博客当中。

        如果有朋友想系统的学习嵌入式相关知识,从事相关的行业,可以私信,有一些经典的电子档书籍资料和开源网课学习链接

C语言面试八股文是指在春季招聘中常见的C语言相关面试题目和知识点。下面是一份常见的C语言面试八股文,供您参考: 1. C语言的基本数据类型有哪些? C语言的基本数据类型包括整型、浮点型、字符型和指针型。 2. 请介绍一下C语言中的变量和常量。 变量是用来存储数据的内存位置,可以通过变量名来访问和修改其值。常量是指在程序执行过程中不会改变的值。 3. 什么是数组?请介绍一下C语言中的数组。 数组是一种存储相同类型数据的集合,通过索引来访问数组中的元素。在C语言中,数组的大小在定义时就需要确定,并且数组的下标从0开始。 4. 请介绍一下C语言中的指针。 指针是一个变量,其值为另一个变量的地址。通过指针可以直接访问和修改内存中的数据。使用指针可以提高程序的效率和灵活性。 5. 请介绍一下C语言中的函数。 函数是一段完成特定任务的代码块,可以通过函数名来调用执行。函数可以接收参数并返回一个值,也可以不接收参数或不返回值。 6. 请介绍一下C语言中的流程控制语句。 C语言中的流程控制语句包括条件语句(if-else语句、switch语句)、循环语句(for循环、while循环、do-while循环)和跳转语句(break语句、continue语句、goto语句)。 7. 请介绍一下C语言中的结构体。 结构体是一种自定义的数据类型,可以包含多个不同类型的成员变量。通过结构体可以将多个相关的数据组织在一起。 8. 请介绍一下C语言中的文件操作。 C语言中的文件操作主要包括打开文件、读写文件和关闭文件。可以使用标准库函数来进行文件操作,如fopen、fread、fwrite、fclose等。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值