title: 常见面试题之操作系统
categories:
- [操作系统]
tags: - [面试题]
date: 2021/05/12
作者:hackett
微信公众号:加班猿
1 请你说一下过程与线程的概念,以及为什么要有过程线程,其中有什么区别,他们各自又是怎么同步的
基本概念:
过程是对运行时程序的封装,是零碎进行资源调度和调配的的根本单位,实现了操作系统的并发;
线程是过程的子工作,是 CPU 调度和分派的根本单位,用于保障程序的实时性,实现过程外部的并发;线程是操作系统可辨认的最小执行和调度单位。
区别:
1.一个线程只能属于一个过程,而一个过程能够有多个线程,但至多有一个线程。线程依赖于过程而存在。
2.过程在执行过程中领有独立的内存单元,而多个线程共享过程的内存。(资源分配给过程,同一过程的所有线程共享该过程的所有资源。同一过程中的多个线程共享代码段(代码和常量),数据段(全局变量和动态变量),扩大段(堆存储)。然而每个线程领有本人的栈段,栈段又叫运行时段,用来寄存所有局部变量和长期变量。)
3. 过程是资源分配的最小单位,线程是 CPU 调度的最小单位;
4. 零碎开销:因为在创立或吊销过程时,零碎都要为之调配或回收资源,如内存空间、I/o 设施等。因而,操作系统所付出的开销将显著地大于在创立或吊销线程时的开销。相似地,在进行过程切换时,波及到整个以后过程 CPU 环境的保留以及新被调度运行的过程的 CPU 环境的设置。而线程切换只须保留和设置大量寄存器的内容,并不波及存储器治理方面的操作。可见,过程切换的开销也远大于线程切换的开销。
5. 通信:因为同一过程中的多个线程具备雷同的地址空间,以致它们之间的同步和通信的实现,也变得比拟容易。过程间通信 IPC,线程间能够间接读写过程数据段(如全局变量)来进行通信——须要进程同步和互斥伎俩的辅助,以保证数据的一致性。在有的零碎中,线程的切换、同步和通信都毋庸操作系统内核的干涉
6. 过程编程调试简略可靠性高,然而创立销毁开销大;线程正相反,开销小,切换速度快,然而编程调试绝对简单。
7. 过程间不会相互影响;线程一个线程挂掉将导致整个过程挂掉
8. 过程适应于多核、多机散布;线程实用于多核
过程间通信:
- 管道(是一个内核缓冲区,过程以先进先出从缓冲区存取数据)
- 命名管道 FIFO(对应一个磁盘索引节点,任何过程都能够拜访)
- 音讯队列(对每个音讯指定特定的数据类型,能够依据自定义条件接管特定类型的音讯)
- 共享内存(效率高,间接读写内存,不须要数据的拷贝)
- 信号量(原子操作,传递数据须要联合共享内存)
- 套接字 socket(用于不同机器间的过程通信)
- 信号(用于告诉接管过程的某个事件曾经产生)
2 请问 MySQL 的端口号是多少,如何批改这个端口号
查看端口号:
应用命令 show global variables like ‘port’; 查看端口号,mysql 的默认端口是 3306
批改端口号:
批改端口号:编辑 /etc/my.cnf 文件
3 请你说一说有了过程,为什么还要有线程?
线程产生的起因:
过程能够使多个程序能并发执行,以进步资源的利用率和零碎的吞吐量;然而其具备一些毛病:
过程在同一时间只能干一件事
过程在执行的过程中如果阻塞,整个过程就会挂起,即便过程中有些工作不依赖于期待的资源,依然不会执行。
和过程相比,线程的劣势如下:
从资源上来讲,线程是一种十分 ” 勤俭 ” 的多任务操作形式。在 linux 零碎下,启动一个新的过程必须调配给它独立的地址空间,建设泛滥的数据表来保护它的代码段、堆栈段和数据段,这是一种 ” 低廉 ” 的多任务工作形式。
从切换效率上来讲,运行于一个过程中的多个线程,它们之间应用雷同的地址空间,而且线程间彼此切换所需工夫也远远小于过程间切换所须要的工夫。据统计,一个过程的开销大概是一个线程开销的 30 倍左右。
从通信机制上来讲,线程间不便的通信机制。对不同过程来说,它们具备独立的数据空间,要进行数据的传递只能通过过程间通信的形式进行,这种形式不仅费时,而且很不不便。线程则不然,因为同一进城下的线程之间奉献数据空间,所以一个线程的数据能够间接为其余线程所用,这不仅快捷,而且不便。
除以上长处外,多线程程序作为一种多任务、并发的工作形式,还有如下长处:
1、使多 CPU 零碎更加无效。操作系统会保障当线程数不大于 CPU 数目时,不同的线程运行于不同的 CPU 上。
2、改善程序结构。一个既长又简单的过程能够思考分为多个线程,成为几个独立或半独立的运行局部,这样的程序才会利于了解和批改。
4 请问单核机器上写多线程程序,是否须要思考加锁,为什么?
在单核机器上写多线程程序,依然须要线程锁。因为线程锁通常用来实现线程的同步和通信。在单核机器上的多线程程序,依然存在线程同步的问题
5 请你说一说线程间的同步形式,最好说出具体的零碎调用
信号量
信号量是一种非凡的变量,可 用于线程同步。它只取天然数值,并且只反对两种操作:
P(SV): 如果信号量 SV 大于 0,将它减一;如果 SV 值为 0,则挂起该线程。
V(SV):如果有其余过程因为期待 SV 而挂起,则唤醒,而后将 SV+1;否则间接将 SV+1。
其零碎调用为:
sem_wait(sem_t *sem):以原子操作的形式将信号量减 1,如果信号量值为 0,则 sem_wait 将被阻塞,直到这个信号量具备非 0 值。
sem_post(sem_t *sem):以原子操作将信号量值 +1。当信号量大于 0 时,其余正在调用 sem_wait 期待信号量的线程将被唤醒。
互斥量
互斥量又称互斥锁,次要用于线程互斥,不能保障按序拜访,能够和条件锁一起实现同步。当进入临界区 时,须要取得互斥锁并且加锁;当来到临界区时,须要对互斥锁解锁,以唤醒其余期待该互斥锁的线程。其次要的零碎调用如下:
pthread_mutex_init: 初始化互斥锁
pthread_mutex_destroy:销毁互斥锁
pthread_mutex_lock:以原子操作的形式给一个互斥锁加锁,如果指标互斥锁曾经被上锁,pthread_mutex_lock 调用将阻塞,直到该互斥锁的占有者将其解锁。
pthread_mutex_unlock: 以一个原子操作的形式给一个互斥锁解锁。
条件变量
条件变量,又称条件锁,用于在线程之间同步共享数据的值。条件变量提供一种线程间通信机制:当某个共享数据达到某个值时,唤醒期待这个共享数据的一个 / 多个线程。即,当某个共享变量等于某个值时,调用 signal/broadcast。此时操作共享变量时须要加锁。其次要的零碎调用如下:
pthread_cond_init: 初始化条件变量
pthread_cond_destroy:销毁条件变量
pthread_cond_signal:唤醒一个期待指标条件变量的线程。哪个线程被唤醒取决于调度策略和优先级。
pthread_cond_wait:期待指标条件变量。须要一个加锁的互斥锁确保操作的原子性。该函数中在进入 wait 状态前首先进行解锁,而后接管到信号后会再加锁,保障该线程对共享资源正确拜访。
6 游戏服务器应该为每个用户开拓一个线程还是一个过程,为什么?
游戏服务器应该为每个用户开拓一个过程。因为 同一过程间的线程会相互影响 , 一个线程死掉会影响其余线程,从而导致过程解体
7 请你说一说操作系统中的构造体对齐,字节对齐
1、起因:
1)平台起因(移植起因):不是所有的硬件平台都能拜访任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异样。
2)性能起因:数据结构(尤其是栈)应该尽可能地在天然边界上对齐。起因在于,为了拜访未对齐的内存,处理器须要作两次内存拜访;而对齐的内存拜访仅须要一次拜访。
2、规定:
1)数据成员对齐规定:构造 (struct)(或联结(union)) 的数据成员,第一个数据成员放在 offset 为 0 的中央,当前每个数据成员的对齐依照 #pragma pack 指定的数值和这个数据成员本身长度中,比拟小的那个进行。
2)构造 (或联结) 的整体对齐规定:在数据成员实现各自对齐之后,构造 (或联结) 自身也要进行对齐,对齐将依照 #pragma pack 指定的数值和构造 (或联结) 最大数据成员长度中,比拟小的那个进行。
3)构造体作为成员:如果一个构造里有某些构造体成员,则构造体成员要从其外部最大元素大小的整数倍地址开始存储。
3、定义构造体对齐:
能够通过预编译命令 #pragma pack(n),n=1,2,4,8,16 来扭转这一系数,其中的 n 就是指定的“对齐系数”。
8 请你答复一下动态变量什么时候初始化
动态变量存储在虚拟地址空间的数据段和 bss 段
C 语言中其在代码执行之前初始化,属于编译期初始化。
C++ 中因为引入对象,对象生成必须调用构造函数,因而 C ++ 规定全局或部分动态对象当且仅当对象首次用到时进行结构
9 请你说一说用户态和内核态区别
用户态和内核态是操作系统的两种运行级别,两者最大的区别就是特权级不同。用户态领有最低的特权级,内核态领有较高的特权级。运行在用户态的程序不能间接拜访操作系统内核数据结构和程序。内核态和用户态之间的转换形式次要包含:零碎调用,异样和中断
10 如何设计 server,使得可能接管多个客户端的申请
多线程,线程池,io 复用
11 死循环 + 来连贯时新建线程的办法效率有点低,怎么改良?
提前创立好一个线程池,用 生产者消费者模型,创立一个工作队列,队列作为临界资源,有了新连贯,就挂在到工作队列上,队列为空所有线程睡眠。改良死循环:应用 select epoll 这样的技术
12 怎么唤醒被阻塞的 socket 线程?
给阻塞时候短少的资源
13 怎么确定以后线程是忙碌还是阻塞?
应用 ps 命令查看
14 请问就绪状态的过程在期待什么?
被调度应用 cpu 的运行权
15 两个过程拜访临界区资源,会不会呈现都取得自旋锁的状况?
单核 cpu,并且开了抢占能够造成这种状况。
16 windows 音讯机制晓得吗,请说一说
当用户有操作 (鼠标,键盘等) 时,零碎会将这些工夫转化为音讯。每个关上的过程零碎都为其保护了一个音讯队列,零碎会将这些音讯放到过程的 音讯队列 中,而应用程序会循环从音讯队列中取出来音讯,实现对应的操作
17 说一说 C ++ 你用到的锁?
生产者消费者问题利用互斥锁和条件变量能够很容易解决,条件变量这里起到了代替信号量的作用
18 请你说一说 C ++ 内存溢出和内存透露
1、内存溢出
指程序申请内存时,没有足够的内存供申请者应用。内存溢出就是你要的内存空间超过了零碎理论调配给你的空间,此时零碎相当于没法满足你的需要,就会报内存溢出的谬误
内存溢出起因:
内存中加载的数据量过于宏大,如一次从数据库取出过多数据
汇合类中有对对象的援用,应用完后未清空,使得不能回收
代码中存在死循环或循环产生过多反复的对象实体
应用的第三方软件中的 BUG
启动参数内存值设定的过小
2、内存透露
内存透露是指因为忽略或谬误造成了程序未能开释掉不再应用的内存的状况。内存透露并非指内存在物理上的隐没,而是应用程序调配某段内存后,因为设计谬误,失去了对该段内存的管制,因此造成了内存的节约。
内存透露的分类:
1、堆内存透露(Heap leak)。对内存指的是程序运行中依据须要调配通过 malloc,realloc new 等从堆中调配的一块内存,再是实现后必须通过调用对应的 free 或者 delete 删掉。如果程序的设计的谬误导致这部分内存没有被开释,那么尔后这块内存将不会被应用,就会产生 Heap Leak。
2、系统资源泄露(Resource Leak)。次要指程序应用零碎调配的资源比方 Bitmap,handle ,SOCKET 等没有应用相应的函数开释掉,导致系统资源的节约,重大可导致系统效力升高,零碎运行不稳固。
3、没有将基类的析构函数定义为虚函数。当基类指针指向子类对象时,如果基类的析构函数不是 virtual,那么子类的析构函数将不会被调用,子类的资源没有正确是开释,因而造成内存泄露。
19 请你来说一说协程?
1、概念:
协程,又称微线程,纤程,英文名 Coroutine。协程看上去也是子程序,但执行过程中,在子程序外部可中断,而后转而执行别的子程序,在适当的时候再返回来接着执行
2)协程和线程区别
那和多线程比,协程最大的劣势就是协程极高的执行效率。因为子程序切换不是线程切换,而是由程序本身管制,因而,没有线程切换的开销,和多线程比,线程数量越多,协程的性能劣势就越显著。
第二大劣势就是不须要多线程的锁机制,因为只有一个线程,也不存在同时写变量抵触,在协程中管制共享资源不加锁,只须要判断状态就好了,所以执行效率比多线程高很多。
3)其余
在协程上利用多核 CPU 呢——多过程 + 协程,既充分利用多核,又充分发挥协程的高效率,可取得极高的性能。
Python 对协程的反对还十分无限,用在 generator 中的 yield 能够肯定水平上实现协程。尽管反对不齐全,但曾经能够施展相当大的威力了。
20 请你说一下僵尸过程?
1)失常过程
失常状况下,子过程是通过父过程创立的,子过程再创立新的过程。
2)孤儿过程
一个父过程退出,而它的一个或多个子过程还在运行,那么那些子过程将成为孤儿过程。孤儿过程将被 init 过程 (过程号为 1) 所收养,并由 init 过程对它们实现状态收集工作
3)僵尸过程
一个过程应用 fork 创立子过程,如果子过程退出,而父过程并没有调用 wait 或 waitpid 获取子过程的状态信息,那么子过程的过程描述符依然保留在零碎中。这种过程称之为僵尸过程。
内部毁灭:
通过 kill 发送 SIGTERM 或者 SIGKILL 信号毁灭产生僵尸过程的过程,它产生的僵死过程就变成了孤儿过程,这些孤儿过程会被 init 过程接管,init 过程会 wait()这些孤儿过程,开释它们占用的零碎过程表中的资源
外部解决:
1、子过程退出时向父过程发送 SIGCHILD 信号,父过程解决 SIGCHILD 信号。在信号处理函数中调用 wait 进行解决僵尸过程。
2、fork 两次,原理是将子过程成为孤儿过程,从而其的父过程变为 init 过程,通过 init 过程能够解决僵尸过程。
21 请你来介绍一下 5 种 IO 模型
1. 阻塞 IO: 调用者调用了某个函数,期待这个函数返回,期间什么也不做,不停的去查看这个函数有没有返回,必须等这个函数返回能力进行下一步动作
2. 非阻塞 IO: 非阻塞期待,每隔一段时间就去检测 IO 事件是否就绪。没有就绪就能够做其余事。
3. 信号驱动 IO: 信号驱动 IO:linux 用套接口进行信号驱动 IO,装置一个信号处理函数,过程持续运行并不阻塞,当 IO 工夫就绪,过程收到 SIGIO 信号。而后解决 IO 事件。
4.IO 复用 / 多路转接 IO:linux 用 select/poll 函数实现 IO 复用模型,这两个函数也会使过程阻塞,然而和阻塞 IO 所不同的是这两个函数能够同时阻塞多个 IO 操作。而且能够同时对多个读操作、写操作的 IO 函数进行检测。晓得有数据可读或可写时,才真正调用 IO 操作函数
5. 异步 IO:linux 中,能够调用 aio_read 函数通知内核形容字缓冲区指针和缓冲区的大小、文件偏移及告诉的形式,而后立刻返回,当内核将数据拷贝到缓冲区后,再告诉应用程序。
22 请问 server 端监听端口,但还没有客户端连贯进来,此时过程处于什么状态?
这个须要看服务端的编程模型,如果如上一个问题的答复形容的这样,则处于阻塞状态,如果应用了 epoll,select 等这样的 io 复用状况下,处于运行状态
23 请问 C ++ 怎么实现线程池?
设置一个生产者消费者队列,作为临界资源
24 Linux 下怎么失去一个文件的 100 到 200 行
sed -n ‘100,200p’ inputfile
awk ‘NR>=100&&NR<=200{print}’ inputfile
head -200 inputfile|tail -100
25 请你来说一下 awk 的应用
1)作用:
款式扫描和解决语言。它容许创立简短的程序,这些程序读取输出文件、为数据排序、解决数据、对输出执行计算以及生成报表,还有有数其余的性能。
2)用法:
awk [-F field-separator] ‘commands’ input-file(s)
3)内置变量
ARGC | 命令行参数个数 |
---|---|
ARGV | 命令行参数排列 |
ENVIRON | 反对队列中零碎环境变量的应用 |
FILENAME | awk 浏览的文件名 |
FNR | 浏览文件的记录数 |
FS | 设置输出域分隔符,等价于命令行 - F 选项 |
NF | 浏览记录的域的个数 |
NR | 已读的记录数 |
OFS | 输入域分隔符 |
ORS | 输入记录分隔符 |
RS | 管制记录分隔符 |
如果你感觉文章还不错,记得 ”点赞关注“
关注我的微信公众号【加班猿】能够获取更多内容