进程和线程面试问题

1. 什么是进程

  • 进程是的程序在特定数据集合上的运行过程
  • 进程是操作系统管理的实体,对应了程序的执行过程。
  • 进程是由正文段、用户数据段及进程控制块共同组成的执行环境,操作系统通过这些数据描述来管理进程
    • 正文段存放被执行的机器指令
    • 用户数据段存放进程在执行时直接进行操作的用户数据
    • 进程控制块存放程序的运行环境

2. 进程的特征

  • 并发性:多个进程实体能在一段时间间隔内同时运行。并发性是现代操作系统的重要特性
  • 动态性:进程是有生命周期的,因程序被执行而创建,因获得cpu而被执行进程的指令,因运行中之而撤销。
  • 独立性:引入线程概念的操作系统中,进程是独立运行和资源调度的基本单位。
  • 异步性:进程在微观上运行是不连续的,但是宏观上运行是连续的。微观上不连续不受进程自身控制。
  • 结构性:进程实体包括用户正文段,用户数据段和进程控制块。

3. 进程和程序的区别

  • 进程是动态的,程序是静态的
  • 程序是持久保存的,进程暂时存在的。
  • 进程是的程序在特定数据集合上的运行过程
  • 一个程序可以对应多个进程

4. 线程和进程的区别

  • 进程是资源(包括内存、打开的文件等)分配的单位,线程是CPU调度的单位。
  • 线程的创建时间比进程快,因为进程在创建的过程中,还需要资源管理信息,比如内存管理信息、文件管理信息,而线程在创建的过程中,不会涉及这些资源管理信息,而是共享其说属进程的这些资源管理信息
  • 线程的终止时间比进程快,因为线程释放的资源相比进程少很多;
  • 同一个进程内的线程切换比进程切换快,因为线程具有相同的地址空间(虚拟内存共享),这意味着同一个进程的线程都具有同一个页表,那么在切换的时候不需要切换页表。而对于进程之间的切换,切换的时候要把页表给切换掉,而页表的切换过程开销是比较大的;
  • 由于同一进程的各线程间共享内存和文件资源,那么在线程之间数据传递的时候,就不需要经过内核了,这就使得线程之间的数据交互效率更高了;
  • 当进程中的一个线程崩溃时,会导致其所属进程的所有线程崩溃

5. 进程间通信方式

  • 匿名管道:只能用于具有亲缘关系的进程之间的通信(父子进程或者兄弟进程)
  • 有名管道:也是半双工的通信方式,但是它允许无亲缘关系进程间的通信
  • 消息队列:本质是一个消息的链表,是保存在内核中消息的链表
  • 信号量:用于进程间同步互斥
  • 共享内存:本质是将两个进程的各自的一块虚拟内存地址空间映射到同一块物理内存中去。虽然两个进程有着独立的虚拟内存空间,但是有一部分映射到相同的物理内存,就完成了内存共享机制,一般会搭配信号量
  • 套接字:既能支持本地进程通信,也能支持网络进程通信

6. 线程间的通信方式(同一进程)

  • 锁机制:包括互斥锁、条件变量、读写锁
    • 互斥锁提供了以排他方式防止数据结构被并发修改的方法。
    • 读写锁允许多个线程同时读共享数据,而对写写操作、读写操作是互斥的。
    • 条件变量可以以原子的方式阻塞进程,直到某个特定条件为真为止。对条件的测试是在互斥锁的保护下进行的。条件变量始终与互斥锁一起使用。

7. 线程同步机制

  • 临界区:临界区指的是一个访问共用资源(例如:共用设备或是共用存储器)的程序片段,而这些共用资源具有无法同时被多个线程访问的特性。当有线程进入临界区段时,其他线程或是进程必须等待,需要有一些同步的机制必须在临界区段的进入点与离开点实现,以确保这些共用资源是被互斥获得使用。通过对多线程的串行化来访问公共资源或者一段代码。
  • 互斥锁:为协调对一个共享资源的单独访问而设计,只有拥有互斥锁的线程才有权限访问系统高的公共资源,因为互斥锁只有一个,所以能保证资源不会同时被多个线程访问。
  • 信号量:为控制对具有有限数量的用户资源的访问而设计。它允许多个线程在同一时刻去访问同一类资源,但一般需要限制同一时刻访问此类资源的最大线程数目。具体操作流程:
    • 信号量不能小于零
    • 线程访问资源时,信号量减一,访问完成加一。
    • 信号量为0时候,其他访问线程需要等待,直到大于0
  • 条件变量(事件):线程主动阻塞,直到收到其他线程的事件通知才能继续运行。一般条件变量需要跟互斥锁搭配使用。

8. 死锁原理

  • 死锁是指多个进程/线程在运行过程中因争夺资源而造成的一种僵局,若无外力帮助,则它们都无法继续向前推进,直到程序崩溃为止。
  • 产生的原因可归结如下
    • 对共享资源的竞争。
    • 进程/线程间的推进顺序不当
  • 死锁产生的四个必要条件
    • 互斥:对资源的访问是互斥的
    • 请求和保持:拥有了一部分资源,还想要另一部分资源
    • 不可剥夺:线程已获得的资源不可被其他线程剥夺
    • 循环等待

9. 什么是抢占式进程调度

  • 每个进程都会有相应的优先级,对于操作系统来说,他会优先执行优先级高的进程。
  • 抢占式进程调度就是,当操作系统正在执行优先级较低的进程A时,此时突然来了一个优先级更高的进程B,则操作系统会先暂停执行优先级较低的进程A,去执行优先级较高的进程B

10. 原子操作的原理是什么

  • 原子:是“不能被进一步分割的最小粒子”
  • 原子操作:意为“不可被中断的一个或一系列操作”。要么全执行,要么全不执行。
  • 处理器提供两种方式:总线锁定和缓存锁定两个机制。一般锁总线开销比较大,就采用缓存锁定,但是有些特殊情况不会使用缓存锁定:
    • .当操作的数据不能被缓存在处理器内部,或操作的数据跨多个缓存行时,则处理器会调用总线锁定。
    • 处理器不支持缓存锁定
  • 总线锁定:就是使用处理器提供的一个LOCK#信号,当一个处理器在总线上输出此信号,其他处理器的请求将被阻塞住,那么该处理器可以独占共享内存。
  • 缓存锁定:cup会将频繁使用的内存会缓存在处理器的L1、L2和L3高速缓存里,那么原子操作就可以直接在处理器内部缓存中进行。缓存锁定就是内存区域如果被缓存在处理器的缓存行中,并在Lock操作期间缓存行被锁定,当缓存行数据回写内存时,可以通过缓存一致性机制来保证操作的原子性,因为缓存一致性会阻止同时修改有两个以上处理器缓存的内存区域数据,当其他处理器回写已经被锁定的缓存行的数据时,会使缓存行无效。

11. volatile解决了什么问题

  • volatile关键字解决了变量的可见性和代码的有序性问题
  • 被volatile修饰的变量,每次读取的时候会确保它是内存中最新的数据。每次修改的时候会确保它能立即更新到内存中去。这样就保证了变量的可见性。(里面用到的技术很多,比如缓存一致性)
  • volatile关键字避免当前变量的指令重排序(保证有序性)的底层实现原理是内存屏障
    • 对volatile变量的写指令后会加入写屏障
    • 对volatile变量的读指令前会加入读屏障

12. 什么是循环锁

  • 循环锁也叫自旋锁
  • 线程获取锁失败,会被挂起,挂起会从用户态切换到内核态,这种切换的开销是很大的,为了减少这种开销,当线程获取锁失败的时候,他不会马上阻塞自己,而是再多次尝试获取CPU使用权,如果多次还是没有获取到,则阻塞,这样在一定程度上可以减少切换开销

13. 读写锁是怎么实现的

  • 利用互斥锁+条件变量来实现(还需要一些计数的变量)
    • 条件变量的特点是:线程自身阻塞自己时会释放锁
    • 利用的是阻塞唤醒机制
  • 利用两个互斥锁+1个整型变量来实现
    • 一个互斥锁充当读写互斥锁
    • 另一个互斥锁调和读读如何加锁
  • 利用两个信号量+1个整型变量来实现(二元信号量,其实也就是互斥锁)

14. 如何设计一个线程池

  • 线程池要包含哪些东西
    • 第一点,我们使用多线程就是为了异步高效的处理任务,所以,肯定有一个任务队列。
    • 第二点,处理任务的线程,构成一个执行队列,即工作线程组;
    • 第三点,执行队列要去处理任务,必须知道任务队列是否有任务,所以,我们需要进行线程同步,即要有一个条件变量。
    • 第四点,多个线程去操作同一个任务队列,必然产生竞争,所以,需要进行加锁,即要有一个互斥锁。
  • 任务都应该包含那些东西
    • 第一点,任务肯定有需要被处理的数据。
    • 第二点,线程池本身不应该关心任务具体应该怎么处理,且不同的任务,处理方式可能不一样,所以,我们需要提供一个回调函数,通过回调函数来处理任务。
    • 第三点,任务队列使用双向链表,任务队列中,每一个任务需要知道自己的前驱和后继任务。(这点非必须)
  • 工作线程都应该包含那些东西
    • 第一点,线程自己的线程ID
    • 第二点,执行单元需要知道自己属于哪一个线程池。
    • 第三点,队列使用双向链表,执行队列中,每一个执行单元需要知道自己的前驱和后继。(这点非必须)

15. 动态链接和静态链接的区别

  • 静态链接:
    • 特点:在生成可执行文件的时候(链接阶段),把所有需要的函数的二进制代码都包含到可执行文件中去。
    • 优点:在程序发布的时候就不需要的依赖库,也就是不再需要带着库一块发布,程序可以独立执行。
    • 缺点:程序体积会相对大一些。如果静态库有更新的话,所有可执行文件都得重新链接才能用上新的静态库。
  • 动态链接:
    • 特点: 在编译的时候不直接拷贝可执行代码,而是通过记录一系列符号和参数,在程序运行或加载时将这些信息传递给操作系统,操作系统负责将需要的动态库加载到内存中,然后程序在运行到指定的代码时,去共享执行内存中已经加载的动态库可执行代码,最终达到运行时连接的目的。
    • 优点: 多个程序可以共享同一段代码,而不需要在磁盘上存储多个拷贝。
    • 缺点: 由于是运行时加载,可能会影响程序的前期执行性能。
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值