1)说一说常用的 Linux 命令
-
ls:查看当前目录下的文件,添加参数-a,查看所有
-
pwd:查看当前目录路径
-
mkdir[name]:在当前目录创建一个名为name的文件夹
-
touch[name]:在当前目录创建一个名位name的文件
-
rm -rf [name]:删除名为name的文件、文件夹或压缩包等
-
ifconfig:查看IP地址和网络配置等
-
yum install :安装某工具
-
grep -c [关键字] [文件名]:统计文件中某关键字的出现次数;grep 强大的文本搜索工具
-
grep under 123.txt 在123.txt文件中搜索under字符串,大小写敏感,显示行; grep -n under 123.txt 在123.txt文件中搜索under字符串,大小写敏感,显示行及行号; grep -v under 123.txt 在123.txt文件中搜索under字符串,大小写敏感,显示没搜索到的行; grep -i under 123.txt 在123.txt文件中搜索under字符串,大小写敏感,显示行; grep -ni under 123.txt 在123.txt文件中搜索under字符串,大小写敏感,显示行及行号;
-
-
cd [路径]:切换到指定路径
-
find[name]:在当前目录下查找名为name的所有文件
-
vi [name]:使用编辑器vi打开一个文件
-
cat[name]:查看某个文件的内容
-
more[name]:百分比查看某个文件的内容
-
less[name]:分页查看某个文件的内容
-
spit:分割文件
-
echo “字符串”:在控制台输出一个字符串,可以使用“>"重定向到文件中
-
|:管道操作符,将前一个指令的输出作为后一个指令的输入
3)请你说说动态库静态库的区别和优缺点
编译程序的几个步骤:编译、汇编、链接、可执行文件
-
静态库:在链接时,将库的整个内容拷贝进去
-
优点:程序在运行时与函数库再无瓜葛,移植方便
-
缺点
-
浪费空间和资源
-
对程序的更新、部署和发布带来困难
例: 比如一个游戏的某一静态库更新了,所有使用这个静态库的应用程序都需要重新编译、发布给用户 对于开发者来说,可能就是几十M大小的改动,但对于玩家来说,可能会有几个G需要重新下载,全部更新
正是因为如此,才出现了动态库
-
-
-
动态库:在连接时,告知库里有什么东西,在哪里放着,实际运行的时候才去找;
-
优点
-
不同的应用程序如果依赖同一个动态库,那么动态库在内存中只存在一份拷贝,这样也就避免了静态库浪费空间和资源的问题
-
-
缺点:
-
运行时需要去找动态库,花费一定的时间
-
-
4)简述一下 GDB 常见的调试命令
-
编译时gcc -g test.c -o test.out,-g表示装入gdb编译的符号表
-
gdb test.out
-
b+行号/函数名,表示在多少行,或哪个函数时停住
-
info b:查看当前设置了哪些断点
-
continue(c):继续执行
-
next(n):单步跳过
-
step(s):单步调试
-
finish:跳出当前函数
-
print+变量名:查看某个变量的当前值
-
kill:终止正在调试的进程
-
info register:查看当前寄存器的值
-
p *array@len :打印数组,array为数组的起始地址,len为数组的长度
-
layout src:显示调试图形界面,或者用”ctrl+x,a“快捷键开启和关闭图形界面
-
layout asm:汇编调试图形界面,使用si进行汇编单步调试
5)说一说进程调度算法有哪些
-
FCFS,先来先服务,公平但不能优先响应较紧急的任务
-
SPF,短作业优先,思路是使进程的平均等待时间最短,可能会饿死长进程(非抢占)
-
抢占式变形:最短剩余时间优先调度算法
根据进程的执行的剩余时间排序,最短的优先调度
-
-
优先级调度算法,根据自定义方式,每隔一段时间计算一次进程优先级,进程调度时选择优先级最高的进程上台运行
-
时间片轮转调度算法:在FCFS的基础上,对每个进程的一次执行设定最长时间,超过这个时间,就必须进行进程调度,换其他进程上台执行,避免大作业长时间占用处理机
-
最高响应比优先调度
根据响应比调度,响应比=(等待时间+要求服务时间)/要求服务时间(等待时间越长、要求服务时间越小,优先级就越高)
-
多级反馈队列调度算法
为系统设置多条反馈队列,每条队列都有优先级,新任务加入1级队列(优先级最高),如果执行完1个时间片未完成,进程切换时,将该进程下放至下一级队列,降低优先级,只有当高优先级队列中的任务为空时,才能调度低优先级队列中的任务。
6)说一说什么是大端、小端,如何判断大端和小端
大端:高地址存低字节,低地址存高字节;符号位固定为第一个字符,容易判断正负,便于人类阅读
小端:低地址存低字节,高地址存高字节;强制类型转换数据不需要调整字节内容
判断大小端的办法:
union BigLittleDuan { /* data */ //共用体的实质是可以共享一段内存空间,b会占用a的前1个字节 int a; char b; }u; bool isBigorLittle(){ u.a=0x12345678; if(u.b==0x12)return 1; return 0; } bool isBigorLittle2(){ u.a=0x12345678; char* c = &u.b; if(*c==0x12)return 1; return 0; }
7)什么是孤儿进程,什么是僵尸进程,如何解决僵尸进程
孤儿进程:子进程在退出之前,父进程已经退出,子进程的资源回收工作交由1号进程完成,这样的子进程称为孤儿进程
僵尸进程:子进程在退出之后,父进程很长时间(处于某个死循环,或者不wait)没有收集子进程的信息并且没有回收其资源,由于父进程仍然存在,也不会交由1#进程来回收子进程的资源,导致子进程的资源长时间占据内存。如果系统中有大量的僵尸进程,可能会导致系统崩溃。
僵尸进程解决方式:使用信号处理wait函数,父进程调用wait等待子进程结束,并回收其资源,如果子进程一直未结束,父进程将睡在wait中,处于阻塞状态。
8)说一说进程通信的方式有哪些?
-
管道pipe:一种半双工通信方式,数据在同一时间只允许向一个方向流动,而且只能在有亲缘关系的进程之间使用(通常为父子进程)
-
命名管道FIFO:有名管道也是半双工的通信方式,但是允许无亲缘关系的进程间通信
-
消息队列MessageQueue:消息队列是保存在内核中的消息链表,每个消息体都是固定大小的存储块,不仅仅是像管道一样,只能传递无格式的字节流数据。
-
共享存储SharedMemory:某一段内存,由一个进程创建,但多个进程都可以访问,是最快的IPC方式
-
信号量Semaphore:一个计数器,用PV操作来控制进程的同步互斥
-
套接字Socket:跨网络通信,可以实现在不同主机上进程之间的通信;
-
信号signal:是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生
9)说一说进程有多少种状态,如何转换
最基本的三种状态:
执行、就绪和阻塞,就绪进程被载入内存并被处理机选中后转换为执行态,执行时如果时间片用完,转换到就绪态等待下一次调度,执行时因为等待IO事件发生而入睡进入阻塞态,调度其他进程上台执行,等待事件发生后,由现运行进程唤醒阻塞进程,进程转换为就绪态等待处理机调度。
如果加上创建和结束两个状态则为5个状态:
进程申请到PCB、分配所需的资源后,意味着生命的开始,刚创建的进程为就绪态进程,在执行完毕后释放内存,结束生命。
后来又增加了两个状态:
-
阻塞挂起,阻塞态的进程由于某种原因被挂起,可以等待IO事件,称为就绪挂起状态
-
就绪挂起,可能由就绪态或阻塞挂起态转换得来,需要被激活为就绪态后,才能被处理机调度
挂起状态无法被调度上台执行。
10)说一说 select 的原理以及缺点
用一个rset(位示图,某位为1表示该fd有数据到来,需要进行处理)记录当前到来需要处理的数据
缺点:
-
bitmap只有1024大小,可以调整,但仍有上限
-
bitmap每次循环时都需要初始化,不可重用
-
将rset从用户空间拷贝到内核需要较大的开销
-
并没有指出哪一个文件描述符里有数据,需要O(n)复杂度的再遍历
11)说一说 epoll 的原理
将有数据的fd重排,放到最最前端,并返回一个有数据的fd总数nfd
每一次while循环处理时,只需遍历nfd个fd,进行处理,也不需要切换到内核态
12)请你说说写时拷贝
在Linux系统中,fork()函数执行时生成一个与父进程相同的子进程(除pid号不同),子进程与父进程共享一段内存空间,如果子进程只进行读操作,不需要分配新的内存空间,这样就会提高创建子进程的速度,可以在第一时间执行exec函数开始它要完成的任务。
如果子进程有写操作,待到写操作到来时,触发页面内存的read-only读写权限异常,陷入内核,把这一份异常页面复制一份给子进程自己使用,节约内存空间。
缺点是,如果写操作涉及的页面很多,可能会有多次读写权限异常,会使前面提高子进程创建速度的过程得不偿失
13)请你说说线程的通信方式(同进程通信方式)
-
管道pipe:一种半双工通信方式,数据在同一时间只允许向一个方向流动,而且只能在有亲缘关系的进程之间使用(通常为父子进程)
-
命名管道FIFO:有名管道也是半双工的通信方式,但是允许无亲缘关系的进程间通信
-
消息队列MessageQueue:消息队列是保存在内核中的消息链表,每个消息体都是固定大小的存储块,不仅仅是像管道一样,只能传递无格式的字节流数据。
-
共享存储SharedMemory:某一段内存,由一个进程创建,但多个进程都可以访问,是最快的IPC方式
-
信号量Semaphore:一个计数器,用PV操作来控制进程的同步互斥
-
套接字Socket:跨网络通信,可以实现在不同主机上进程之间的通信;
-
信号signal:是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生
14)请你说说进程和线程的区别
进程:一个在内存中运行的应用程序,每个进程都有自己独立的一块内存,一个进程可以有多个线程,比如在Windows系统中,一个运行的.exe就是进程。
线程:进程中的一个执行任务(控制单元),负责当前进程中程序的执行,一个进程至少有一个线程,一个进程可以运行多个线程,多个线程可共享数据。
区别总结
-
根本区别:进程是操作系统资源分配的基本单元,而线程是处理器任务调度和执行的基本单位
-
资源开销:每个进程都有独立的代码和数据空间(程序上下文),程序之间的切换有较大开销,线程可以看作是轻量级进程,同一类线程共享代码和数据空间,每个线程都有自己独立的运行栈和PC,线程之间切换开销小
-
包含关系:一个进程内至少有1个线程,可以包含多个线程,每个线程执行进程的一个子任务
-
内存分配:同一进程的线程共享本进程的地址空间和资源,而进程之间的地址空间和资源是相互共享的
-
影响关系:一个进程崩溃后,在保护模式下不会对其他进程产生影响,但是一个线程崩溃后,一个进程都会崩溃,多进程要比多线程见状
-
执行过程:每个独立的进程有程序运行的入口,顺序执行序列和程序出口,但是线程不能独立执行,必须依存应用程序中,由应用程序提供多个线程执行控制,两者均可并发执行。
15)请你说说线程和协程的区别
协程是一种用户态的轻量级线程,协程的调度完全由用户控制(可以暂停),协程拥有自己的寄存器上下文和栈。恢复先前保存的寄存器上下文和栈,直接操作栈基本没有内核切换开销,可以不加锁访问全局变量,所以上下文的切换非常快。
区别:
-
一个线程可以多个写成,一个进程也可以单独拥有多个协程
-
线程进程都是同步机制,而协程是异步
-
协程能保留上一次调用时的状态,每次过程重入时,就相当于进入上一次调用的状态
-
线程是抢占式,而协程是非抢占式,需要用户自己释放使用权来切换到其他协程,因此同一时间其实只有一个协程拥有运行权,相当于单线程能力。
-
协程并不是取代线程,而且抽象于线程之上,线程是被分割的CPU资源,协程是组织好的代码流程,协程需要线程来承载运行,线程是协程的资源,但不会直接使用线程。而是通过执行器
-
线程是协程的资源,通过Interceptor来间接使用线程这个资源
16)请你介绍一下死锁,产生的必要条件,产生的原因,怎么预防死锁
死锁是不同进程间争抢临界资源时导致的无限等待的现象。
死锁产生的四个必要条件:
-
互斥条件:不同进程对临界资源的访问必须互斥
-
不可剥夺条件:进程占有的资源只能由自己释放
-
请求保持条件:进程占有一定资源后仍申请其他资源,在其他资源得不到分配时不会主动释放手里已经占有的资源
-
循环等待条件:不同进程间相互等待对方手里的资源
破坏上述四个条件之一即可预防死锁:
(1)互斥条件维护系统并发的安全,是并发程序的基础,一般不会破坏还会保护
(2)不可剥夺的条件也是系统的固有属性,不可破坏
(3)请求保持条件:要求进程在可以同时得到需要的所有资源时才进行资源分配,否则不分配。这种方式会降低资源的利用率,导致某种资源长时间不用
(4)循环等待条件:让进程请求资源时按照资源序号从小到大的顺序请求,不会形成请求环,就不会发生死锁。
17)请你说说分段和分页
分段:
作业的地址空间被划分为若干个段,每个段定义了一组逻辑信息。每个段都有自己的名字,从0开始编址,并采用一段连续的地址空间。段的长度由相应的逻辑信息组的长度决定,因而各段长度不等。整个地址空间分成多个段,逻辑地址=段号+段内地址
分页:
用户程序的地址空间被划分成若干固定大小的区域,称为“页”,相应的,物理内存被分为若干个块,块的大小和页的大小相等。可将用户程序的任一页放在内存的任意块中,实现了离散分配。为了能找到逻辑块号对应的物理块,系统为每个进程建立一张页表,用于记录逻辑块号和物理块号的映射关系。
区别:
-
都采用离散分配方式,由地址映射机构来实现地址的转换
-
分页存储管理方式是为了实现离散分配方法,提高内存利用率,页面大小固定
-
段的大小不固定,由用户编写的程序决定
18)请你说说互斥锁和自旋锁
都是对临界资源控制访问的一种方式,互斥锁属于sleep-waiting类型的锁,进程发现临界资源已经被占用后会放弃CPU,入睡等待临界资源解锁,而自旋锁不会放弃CPU,会一直请求该资源,直到该资源被释放,持续占用CPU发出请求,属于busy-waiting类型的锁。
区别在于,互斥锁使得后来的进程入睡,资源释放时唤醒,进行切换会有较大的开销;自旋锁不会切换进程,但会一直占用CPU,如果持锁时间比较长,会浪费CPU的执行时间。
19)请你说说共享内存
共享内存是操作系统在实际物理内存中开辟的一段内存
进程的地址空间中存在一个共享区,映射到该实际物理内存,不同进程通过不同的逻辑地址,访问相同的共享内存单元,实现通信。
因为在内存中通信,共享内存实现进程间通信是进程间通信最快的方式。
20)请你说一说虚拟内存与物理内存
一个进程新建立的时候,会建立自己的内存空间,4G大小(虚拟内存),此进程的数据、代码等拷到自己的进程空间,哪些数据在哪里,都由进程控制表记录,哪些地址有数据,哪些可读可写,都通过这个进程控制表记录。
每个进程已经分配的内存空间,都与对应的磁盘空间映射
(1)4G内存空间只是虚拟内存空间,每次访问内存空间的某个地址,都需要把地址翻译为实际物理内存地址。
(2)所有进程共享同一物理内存,每个进程只把自己目前需要的虚拟内存空间映射到物理内存上
(3)进程要知道哪些内存地址上的数据在物理内存上,哪些不在,还有在物理内存上的哪里,需要用页表来记录
(4)当进程访问某个虚拟地址,去看页表,如果发现对应的数据不再物理内存中,发生缺页异常,将需要的数据从磁盘上拷贝到内存中。
21)请你说说条件变量,
条件变量是用来等待线程而不是上锁的,条件变量通常和互斥锁一起使用。
主要是因为互斥锁的不足,只有两种状态:锁定和非锁定,而条件变量可以允许线程阻塞和等待另一个线程发送信号来弥补互斥锁的不足。
或:
条件变量是利用线程间共享的变量进行同步的一种机制,是在多线程程序中用来实现“等待->唤醒"逻辑的常用方法,用于维护一个条件,线程可以使用条件变量来等待某个条件为真,当条件不满足时,线程将自己加入等待队列,同时释放持有的互斥锁。当一个线程唤醒一个或多个线程时,此时条件不一定为真(虚假唤醒)
-
两个线程利用条件变量及互斥锁实现同步
-
一个线程利用条件变量实现等待,同时释放锁
-
一个线程获取锁后利用该条件变量唤醒等待的线程
22)请你介绍一下 I/O 多路复用
指单线程的进程按照某种方式同时处理多个客户端的请求的方法,常用的方法有select、poll和epoll