牛客网:C++面试宝典——操作系统(1)进程和线程,

目录

● 请你说一下进程与线程的概念,以及为什么要有进程线程,其中有什么区别,他们各自又是怎么同步的

进程间通信的方式:

线程间通信的方式: 参考

● 请你说一说操作系统中的程序的内存结构

● 请你说一说操作系统中的缺页中断

● 请你说一说并发(concurrency)和并行(parallelism)

● 请问MySQL的端口号是多少,如何修改这个端口号

● 请你说一说有了进程,为什么还要有线程?

● 请问单核机器上写多线程程序,是否需要考虑加锁,为什么?

● 请问线程需要保存哪些上下文,SP、PC、EAX这些寄存器是干嘛用的

● 请你说一说线程间的同步方式,最好说出具体的系统调用

● 请你说一下多线程和多进程的不同

● 请你说一说死锁发生的条件以及如何解决死锁

● 请你说一说操作系统中的结构体对齐,字节对齐

● 请问进程间怎么通信

● 请你说一下虚拟内存置换的方式


● 请你说一下进程与线程的概念,以及为什么要有进程线程,其中有什么区别,他们各自又是怎么同步的

进程由数据。程序。进程控制块(PCB)

进程是对运行时程序的封装,是系统进行资源调度和分配的的基本单位,实现了操作系统的并发;

线程是进程的子任务,是CPU调度和分派的基本单位,用于保证程序的实时性,实现进程内部的并发;线程是操作系统可识别的最小执行和调度单位。每个线程都独自占用一个虚拟处理器:独自的寄存器组,指令计数器和处理器状态。每个线程完成不同的任务,但是共享同一地址空间(也就是同样的动态内存,映射文件,目标代码等等),打开的文件队列和其他内核资源。

区别:

1.一个线程只能属于一个进程,而一个进程可以有多个线程,但至少有一个线程。线程依赖于进程而存在。

2.进程在执行过程中拥有独立的内存单元,而多个线程共享进程的内存(资源分配给进程,同一进程的所有线程共享该进程的所有资源。同一进程中的多个线程共享代码段(代码和常量),数据段(全局变量和静态变量),扩展段(堆存储)

3.进程是资源分配的最小单位,线程是CPU调度的最小单位;

4.系统开销:由于在创建或撤消进程时,系统都要为之分配或回收资源,如内存空间、I/o设备等。因此,操作系统所付出的开销将显著地大于在创建或撤消线程时的开销。类似地,在进行进程切换时,涉及到整个当前进程CPU环境的保存以及新被调度运行的进程的CPU环境的设置。而线程切换只须保存和设置少量寄存器的内容,并不涉及存储器管理方面的操作。可见,进程切换的开销也远大于线程切换的开销。

5.通信:由于同一进程中的多个线程具有相同的地址空间,致使它们之间的同步和通信的实现,也变得比较容易。进程间通信IPC,线程间可以直接读写进程数据段(如全局变量)来进行通信——需要进程同步和互斥手段的辅助,以保证数据的一致性。

6.进程间不会相互影响 ;线程一个线程挂掉将导致整个进程挂掉

 

进程间通信的方式:

1.管道:

普通管道PIPE:只存在于内存中。

1)它是半双工的(即数据只能在一个方向上流动),具有固定的读端和写端

2)它只能用于具有亲缘关系的进程之间的通信(也是父子进程或者兄弟进程之间)

命名管道:可以在无关的进程之间交换数据,存在于文件系统中

2.信号量

它是一个计数器,可以用来控制多个进程对共享资源的访问。信号量用于实现进程间的互斥与同步,而不是用于存储进程间通信数据。

1)信号量基于操作系统的 PV 操作,程序对信号量的操作都是原子操作。

2)每次对信号量的 PV 操作不仅限于对信号量值加 1 或减 1,而且可以加减任意正整数。

3.共享内存

它使得多个进程可以访问同一块内存空间,不同进程可以及时看到对方进程中对共享内存中数据得更新。这种方式需要依靠某种同步操作,如互斥锁和信号量

(1)因为多个进程可以同时操作,所以需要进行同步

(2)信号量+共享内存通常结合在一起使用,信号量用来同步对共享内存的访问

4.套接字SOCKET

socket也是一种进程间通信机制,与其他通信机制不同的是,它可用于不同主机之间的进程通信。

 

线程间通信的方式: 参考

1.全局变量:进程中线程间共享内存。同一进程中的不同线程共享同一全局内存区域,包括初始化数据段,未初始化数据段以及堆内存数据段。因此线程间可以方便、快速的共享信息。只需要将数据复制到共享(全局/堆)变量中即可。

注:要避免多个线程试图同时修改同一份信息。

定义全局变量时最好使用volatile定义,以防编译器对此变量优化

2.使用消息实现通信

每一个线程都可以拥有自己的消息队列,消息进行线程间通信sendMessage,postMessage。

3.CEvent对象

CEvent为MFC中的一个对象,可以通过对CEvent的触发状态进行改变,从而实现线程间的通信和同步。

 

● 请你说一说操作系统中的程序的内存结构

一个程序本质上都是由BSS段、data段、text段三个组成的。

BSS段(未初始化数据区):通常用来存放程序中未初始化的全局变量和静态变量的一块内存区域。BSS段属于静态分配,程序结束后静态变量资源由系统自动释放。

数据段:存放程序中已初始化的全局变量的一块内存区域。

代码段:存放程序执行代码的一块内存区域。在代码段中,也有可能包含一些只读的常数变量

 

● 请你说一说操作系统中的缺页中断

缺页中断:在请求分页系统中,可以通过查询页表中的状态位来确定所要访问的页面是否存在于内存中。每当所要访问的页面不在内存是,会产生一次缺页中断,此时操作系统会根据页表中的外存地址在外存中找到所缺的一页,将其调入内存。

缺页本身是一种中断,与一般的中断一样,需要经过4个处理步骤:

1、保护CPU现场

2、分析中断原因

3、转入缺页中断处理程序进行处理

4、恢复CPU现场,继续执行

但是缺页中断是由于所要访问的页面不存在于内存时,由硬件所产生的一种特殊的中断,因此,与一般的中断存在区别:

在指令执行期间产生和处理缺页中断信号

一条指令在执行期间,可能产生多次缺页中断

缺页中断返回是,执行产生中断的一条指令,而一般的中断返回是,执行下一条指令。

 

● 请你说一说并发(concurrency)和并行(parallelism)

并发(concurrency):指宏观上看起来两个程序在同时运行,比如说在单核cpu上的多任务。但是从微观上看两个程序的指令是交织着运行的。在单个周期内只运行了一个指令。这种并发并不能提高计算机的性能,只能提高效率。只是把时间分成若干段,使多个进程快速交替的执行。

并行(parallel):指在同一时刻,有多条指令在多个处理器上同时执行。所以无论从微观还是从宏观来看,二者都是一起执行的。

并行在多处理器系统中存在,而并发可以在单处理器和多处理器系统中都存在

● 请问MySQL的端口号是多少,如何修改这个端口号

使用命令show global variables like 'port';查看端口号 ,mysql的默认端口是3306

sqlserver默认端口号为:1433

修改端口号:编辑/etc/my.cnf文件

● 请你说一说有了进程,为什么还要有线程?

线程产生的原因:

进程可以使多个程序能并发执行,以提高资源的利用率和系统的吞吐量;但是其具有一些缺点:

进程在同一时间只能干一件事

进程在执行的过程中如果阻塞,整个进程就会挂起,即使进程中有些工作不依赖于等待的资源,仍然不会执行。

因此,操作系统引入了比进程粒度更小的线程,作为并发执行的基本单位,从而减少程序在并发执行时所付出的时空开销,提高并发性。和进程相比,线程的优势如下:

1.系统开销:由于在创建或撤消进程时,系统都要为之分配或回收资源,如内存空间、I/o设备等。因此,操作系统所付出的开销将显著地大于在创建或撤消线程时的开销。启动一个新的进程必须分配给它独立的地址空间

2.在进行进程切换时,涉及到整个当前进程CPU环境的保存以及新被调度运行的进程的CPU环境的设置。而线程切换只须保存和设置少量寄存器的内容,并不涉及存储器管理方面的操作。可见,进程切换的开销也远大于线程切换的开销。

3.通信:由于同一进程中的多个线程具有相同的地址空间,致使它们之间的同步和通信的实现,也变得比较容易。进程间通信IPC,线程间可以直接读写进程数据段(如全局变量)来进行通信——需要进程同步和互斥手段的辅助,以保证数据的一致性。

● 请问单核机器上写多线程程序,是否需要考虑加锁,为什么?

在单核机器上写多线程程序,仍然需要线程锁。因为线程锁通常用来实现线程的同步和通信。在单核机器上的多线程程序,仍然存在线程同步的问题。因为在抢占式操作系统中,通常为每个线程分配一个时间片,当某个线程时间片耗尽时,操作系统会将其挂起,然后运行另一个线程。如果这两个线程共享某些数据,不使用线程锁的前提下,可能会导致共享数据修改引起冲突。

eg:i++

● 请问线程需要保存哪些上下文,SP、PC、EAX这些寄存器是干嘛用的

线程在切换的过程中需要保存当前线程Id、线程状态、堆栈、寄存器状态等信息。其中寄存器主要包括SP PC EAX等寄存器,其主要功能如下:

SP:堆栈指针,指向当前栈的栈顶地址

PC:程序计数器,存储下一条将要执行的指令所在单元的地址

EAX:累加寄存器,用于加法乘法的缺省寄存器

● 请你说一说线程间的同步方式,最好说出具体的系统调用

信号量

系统调用:sem_wait,sem_post

信号量是一种特殊的变量,可用于线程同步。它只取自然数值,并且只支持两种操作:

P(SV):如果信号量SV大于0,将它减一;如果SV值为0,则挂起该线程。

V(SV):如果有其他进程因为等待SV而挂起,则唤醒,然后将SV+1;否则直接将SV+1。

https://blog.csdn.net/just_kong/article/details/98871393

互斥量(只用于线程间)

互斥量又称互斥锁,主要用于线程资源的互斥访问

当进入临界区时,需要获得互斥锁并且加锁;当离开临界需要解锁

pthread_mutex_init、destroy/lock/unlock

 

http://blog.chinaunix.net/uid-20671208-id-4935154.html

条件变量
条件变量(condition variable)是利用共享的变量进行线程之间同步的一种机制。
对条件变量的使用包括两个动作: 
1) 线程等待某个条件, 条件为真则继续执行,条件为假则将自己挂起(避免busy wait,节省CPU资源); 
2) 线程执行某些处理之后,条件成立;则通知等待该条件的线程继续执行。 

条件变量常与互斥锁同时使用,达到线程同步的目的:条件变量通过允许线程阻塞和等待另一个线程发送信号的方法弥补了互斥锁的不足。

pthread_wait_init/destroy/wait/signal

● 请你说一下多线程和多进程的不同

进程是资源分配的最小单位,而线程时CPU调度的最小单位。多线程之间共享同一个进程的地址空间,线程间通信简单,同步复杂,线程创建、销毁和切换简单,速度快,占用内存少,适用于多核分布式系统,但是线程间会相互影响,一个线程意外终止会导致同一个进程的其他线程也终止,程序可靠性弱。

而多进程间拥有各自独立的运行地址空间,进程间不会相互影响,程序可靠性强,但是进程创建、销毁和切换复杂,速度慢,占用内存多,进程间通信复杂,但是同步简单,适用于多核、多机分布。

我们知道,同一个进程中的多个线程共享进程资源,包括主内存、文件句柄、锁资源等。那么当一个线程死了(非正常退出、死循环等)就会导致线程该占有的资源永远无法释放,从而影响其他线程的正常工作

应用场景:https://blog.csdn.net/weixin_39731083/article/details/82015830

1、需要频繁创建销毁的优先用线程(进程的创建和销毁开销过大)

2、需要进行大量计算的优先使用线程(CPU频繁切换)

3、可能要扩展到多机分布的用进程,多核分布的用线程

 

PS:

饥饿(Starvation)指一个进程一直得不到资源。

死锁和饥饿都是由于进程竞争资源而引起的。饥饿一般不占有资源,死锁进程一定占有资源。

● 请你说一说死锁发生的条件以及如何解决死锁

死锁是指两个或两个以上进程在执行过程中,因争夺资源而造成的下相互等待的现象。死锁发生的四个必要条件如下:

互斥条件:进程对所分配到的资源不允许其他进程访问,若其他进程访问该资源,只能等待,直至占有该资源的进程使用完成后释放该资源;

请求和保持条件:进程获得一定的资源后,又对其他资源发出请求,但是该资源可能被其他进程占有,此时请求阻塞,但该进程不会释放自己已经占有的资源

不可剥夺条件:进程已获得的资源,在未完成使用之前,不可被剥夺,只能在使用后自己释放

循环等待:存在一种进程资源的循环等待链,链中每一个进程已获得的资源同时被 链中下一个进程所请求

解决死锁:

1,预防死锁,破坏上述的四个条件

(1)破坏“占有并等待”条件:

方法一:创建进程时,要求它申请所需的全部资源,系统或满足其所有要求,或什么也不给它。这是所谓的 “ 一次性分配”方案。

方法二:要求每个进程提出新的资源申请前,释放它所占有的资源。

(2)破坏“不可抢占”条件:
破坏“不可抢占”条件就是允许对资源实行抢夺。

(3)破坏“循环等待”条件:

是将系统中的所有资源统一编号,进程可在任何时刻提出资源申请,但所有申请必须按照资源的编号顺序(升序)提出。

2.避免死锁: 在资源的动态分配过程中,用某种方法去防止系统进入不安全状态,从而避免死锁的发生。

 

● 请你说一说操作系统中的结构体对齐,字节对齐

性能原因:数据结构(尤其是栈)应该尽可能地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。

元素的起始地址是sizeof(元素类型)#pragma pack指定的数值

结构体大小是最大元素和#pragma pack指定的数值最小值的整数倍

结构体作为成员:如果一个结构里有某些结构体成员,则结构体成员要从其内部最大元素大小的整数倍地址开始存储。

● 请问进程间怎么通信

管道仅仅是将进程的输出和另一个进程的输入相连接的单向通信的方法

进程间通信主要包括管道、系统IPC(包括消息队列、信号量、信号、共享内存等)、以及套接字socket。

1.管道:

管道主要包括无名管道和命名管道:管道可用于具有亲缘关系的父子进程间的通信,有名管道除了具有管道所具有的功能外,它还允许无亲缘关系进程间的通信

1.1 普通管道PIPE:

1)它是半双工的(即数据只能在一个方向上流动),具有固定的读端和写端

2)它只能用于具有亲缘关系的进程之间的通信(也是父子进程或者兄弟进程之间)

3)它可以看成是一种特殊的文件,对于它的读写也可以使用普通的read、write等函数。但是它不是普通的文件,并不属于其他任何文件系统,并且只存在于内存中。

1.2 命名管道FIFO:

1)FIFO可以在无关的进程之间交换数据

2)FIFO有路径名与之相关联,它以一种特殊设备文件形式存在于文件系统中。

 

2.1 消息队列

消息队列,是消息的链接表,存放在内核中。一个消息队列由一个标识符(即队列ID)来标记。 (消息队列克服了信号传递信息少,管道只能承载无格式字节流以及缓冲区大小受限等特点)具有写权限得进程可以按照一定得规则向消息队列中添加新信息;对消息队列有读权限得进程则可以从消息队列中读取信息;

· 消息队列独立于发送与接收进程。进程终止时,消息队列及其内容并不会被删除。

消息队列可以实现消息的随机查询,消息不一定要以先进先出的次序读取,也可以按消息的类型读取。

2.2 信号量semaphore

信号量(semaphore)与已经介绍过的 IPC 结构不同,它是一个计数器,可以用来控制多个进程对共享资源的访问。信号量用于实现进程间的互斥与同步,而不是用于存储进程间通信数据。

(1)信号量基于操作系统的 PV 操作,程序对信号量的操作都是原子操作。

(2)每次对信号量的 PV 操作不仅限于对信号量值加 1 或减 1,而且可以加减任意正整数。

2.4 共享内存(Shared Memory)

它使得多个进程可以访问同一块内存空间,不同进程可以及时看到对方进程中对共享内存中数据得更新。这种方式需要依靠某种同步操作,如互斥锁和信号量等

3. 套接字SOCKET:

socket也是一种进程间通信机制,与其他通信机制不同的是,它可用于不同主机之间的进程通信。

 

● 请你说一下虚拟内存置换的方式

1、FIFO(先进先出淘汰算法)

思想:最近刚访问的,将来访问的可能性比较大。

实现:使用一个队列,新加入的页面放入队尾,每次淘汰队首的页面,即最先进入的数据,最先被淘汰。

弊端:无法体现页面冷热信息

2、LFU(最不经常访问淘汰算法)

思想:如果数据过去被访问多次,那么将来被访问的频率也更高。

实现:每个数据块一个引用计数,所有数据块按照引用计数排序,具有相同引用计数的数据块则按照时间排序。每次淘汰队尾数据块。

开销:排序开销。

弊端:缓存颠簸。

3、LRU(最近最少使用替换算法)

思想:如果数据最近被访问过,那么将来被访问的几率也更高。

实现:使用一个栈,新页面或者命中的页面则将该页面移动到栈底,每次替换栈顶的缓存页面。

优点:LRU算法对热点数据命中率是很高的。

缺点:缓存颠簸

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值