Linux下常见的面试题

学习嵌入式,已经有大半年了,也在博客上面看过一些笔试面试题,今天我想把自己看过的几个面试题做一些总结,也希望自己找工作的时候,回过头来复习。下面是我列举的几个笔试题!
1、系统调用与标准IO库函数有什么区别?

标准IO库函数 系统调用
在所有的ANSI C编译器中,c库函数都是相同的 各个操作系统的系统调用是不同的,这导致程序不可移植
它调用函数库中的一段程序(或函数) 它调用的是系统内核的服务
与用户程序相联系 在内核地址空间运行
它的运行时间属于“用户时间” 它的运行时间属于“系统时间”
属于过程调用,调用开销较小 需要在用户控件和内核上下文环境间切换,开销较大
在c函数libc中大约有300个函数 在unix中大约有90个系统调用
典型的c函数库:printf、fopen、fread、malloc 典型的系统调用write、open、read、sbrk、fork

2,什么是进程,什么是线程?进程与线程有什么区别?
①什么是一个进程?在操作系统原理使用这样的术语来描述的:正在运行的程序及其占用的资源(CPU、内存、系统资源等)叫做进程。
②在操作系统原理的术语中,线程是进程的一条执行路径。所有的线程都是在同一进程空间运行,这也意味着多条线程将共享该进程中的全部系统资源,如虚拟地址空间,文件描述符和信号处理等等。

进程与线程有什么区别:
(1)地址空间:线程是指进程内的一个执行单元,也是进程内的可调度实体;进程至少有一个线程;它们共享进程的地址空间;而进程有自己独立的地址空间;
(2)资源拥有:进程是资源分配和拥有的单位,同一个进程内的线程共享进程的资源
(3)线程是处理器调度的基本单位,但进程不是。
(4)二者均可并发执行.资源分配的单位是进程,处理机调度的单位是线程。

3,父进程在创建子进程之后,哪些东西共享,哪些东西不共享?
子进程自父进程继承到:
1、进程的资格(真实(real)/有效(effective)/已保存(saved) 用户号(UIDs)和组号(GIDs))
2、环境(environment)变量
3、堆栈
4、内存
5、打开文件的描述符
6、执行时关闭(close-on-exec) 标志
7、信号(signal)控制设定
8、nice值
9、进程调度类别(scheduler class)
10、进程组号
11、对话期ID(Session ID)
12、当前工作目录
13、根目录 (根目录不一定是“/”,它可由chroot函数改变)
14、掩模( 文件方式创建屏蔽字(file mode creation mask (umask) )
15、资源限制
16、控制终端

子进程所独有:
1、进程号
2、不同的父进程号
3、自己的文件描述符和目录流的拷贝
4、子进程不继承父进程的进程,正文(text), 数据和其它锁定内存(memory locks)
8、在tms结构中的系统时间
9、资源使用(resource utilizations)设定为0
10、阻塞信号集初始化为空集(译者注:原文此处不明确, 译文根据fork函数手册页稍做修改)
11、不继承由timer_create函数创建的计时器
12、不继承异步输入和输出
13、父进程设置的锁

4、什么是死锁?死锁产生的原因?死锁的产生有哪些条件?怎么解决死锁?
①死锁:两个或两个以上的进程或线程在执行过程中,相互等待资源而产生的一种僵持状态,如果没有外力的干预将一直持续这个状态。举个例子来说:A君和B君住在一起,但是家里只有一个碗一双筷子,A君打算先拿了筷子再拿碗然后吃饭,而B君则是想先拿碗再拿筷子然后吃饭,A君拿了筷子、B君拿了碗,两个人都很饿都等着对方给自己没拿到的东西才能吃饭,两个人又不肯让出自己手中拿到的东西,就这样赌气一直等一直等,结果两个人都饿扁了,这就是死锁。

②系统资源不足、相互竞争资源、请求资源顺序不当。

互斥:某种资源一次只允许一个进程访问,即该资源一旦分配给某个进程,其他进程就不能再访问,直到该进程访问结束(碗和筷子都是只能一个人拿)。
占有并等待:一个进程本身占有资源(一种或多种),同时还有资源未得到满足,正在等待其他进程释放该资源。(A君拿着筷子饥肠辘辘的等着B君把碗给他)。
不可抢占:别人已经占有了某项资源,你不能因为自己也需要该资源,就去把别人的资源抢过来。
**循环等待:**存在一个进程链,使得每个进程都占有下一个进程所需的至少一种资源。
④产生死锁需要四个条件,那么,只要这四个条件中至少有一个条件得不到满足,就不可能发生死锁了。由于互斥条件是非共享资源所必须的,不仅不能改变,还应加以保证,所以,主要是破坏产生死锁的其他三个条件:

a、破坏“占有并等待”条件
方法1:所有的进程在开始运行之前,必须一次性地申请其在整个运行过程中所需要的全部资源。(第二天A君一回来就迅速同时拿了碗和筷)

优点:简单易实施且安全。

缺点:因为某项资源不满足,进程无法启动,而其他已经满足了的资源也不会得到利用,严重降低了资源的利用率,造成资源浪费。使进程经常发生饥饿现象。

方法2:该方法是对第一种方法的改进,允许进程只获得运行初期需要的资源,便开始运行,在运行过程中逐步释放掉分配到的已经使用完毕的资源,然后再去请求新的资源。这样的话,资源的利用率会得到提高,也会减少进程的饥饿问题。
b、破坏“不可抢占”条件
当一个已经持有了一些资源的进程在提出新的资源请求没有得到满足时,它必须释放已经保持的所有资源,待以后需要使用的时候再重新申请。这就意味着进程已占有的资源会被短暂地释放或者说是被抢占了。该种方法实现起来比较复杂,且代价也比较大。释放已经保持的资源很有可能会导致进程之前的工作实效等,反复的申请和释放资源会导致进程的执行被无限的推迟,这不仅会延长进程的周转周期,还会影响系统的吞吐量。(A君心想既然没拿到碗,就给筷子给他先吃饭吧)
c、破坏“循环等待”条件

可以通过定义资源类型的线性顺序来预防,可将每个资源编号,当一个进程占有编号为i的资源时,那么它下一次申请资源只能申请编号大于i的资源。(A君和B君约定好了谁先抢到筷子谁就可以拿碗吃饭)

5、什么是写实拷贝技术(COW)

fork函数的功能就是创建一个进程,调用fork的进程称为父进程,新生成的进程称为子进程。
在生成子进程的时候用到了写实拷贝技术:不进行对父进程的数据段,栈和堆的完全复制,这些区域由父、子进程共享内核将这些资源访问权限改写成只读,当父、子进程任何一方要对其进行修改时,再进行复制操作,并且只复制虚拟地址空间下的资源所在的那”一页”。(操作系统为每一个程序维护一个页表,因此程序加载的时候,不要求在主存上连续,)

6,select、poll、epoll怎么工作的?他们有什么区别?
一、1、 select()函数允许进程指示内核等待多个事件(文件描述符)中的任何一个发生,并只在有一个或多个事件发生或经历一段指定时间后才唤醒它,然后接下来判断究竟是哪个文件描述符发生了事件并进行相应的处理。
2、我们可以从内核和select的关系来看:(1)传向select的参数告诉内核:
①我们所关心的描述符。
②对于每个描述符我们所关心的条件(是否读一个给定的描述符?是否想写一个给定的描述符?是否关心一个描述符的异常条件?)。
③希望等待多长时间 (可以永远等待,等待一个固定量时间,或完全不等待)。
(2)从select返回时,内核告诉我们:
①已准备好的描述符的数量。
②哪一个描述符已准备好读、写或异常条件。

二、poll是Linux中的字符设备驱动中的一个函数,poll本质上和select没有区别,它将用户传入的数组拷贝到内核空间,然后查询每个fd对应的设备状态,如果设备就绪则在设备等待队列中加入一项并继续遍历,如果遍历完所有fd后没有发现就绪设备,则挂起当前进程,直到设备就绪或者主动超时,被唤醒后它又要再次遍历fd。这个过程经历了多次无谓的遍历。

三、epoll的设计和实现与select完全不同。epoll通过在Linux内核中申请一个简易的文件系统,把原先的select/poll调用分成了3
个部分:

  1. 调用epoll_create()建立一个epoll对象(在epoll文件系统中为这个句柄对象分配资源)
  2. 调用epoll_ctl向epoll对象中添加这100万个连接的套接字
  3. 调用epoll_wait收集发生的事件的连接

四、select、poll、epoll 区别总结:
1、支持一个进程所能打开的最大连接数

select

单个进程所能打开的最大连接数有FD_SETSIZE宏定义,其大小是32个整数的大小(在32位的机器上,大小就是3232,同理64位机器上FD_SETSIZE为3264),当然我们可以对进行修改,然后重新编译内核,但是性能可能会受到影响,这需要进一步的测试。

poll

poll本质上和select没有区别,但是它没有最大连接数的限制,原因是它是基于链表来存储的

epoll

虽然连接数有上限,但是很大,1G内存的机器上可以打开10万左右的连接,2G内存的机器可以打开20万左右的连接。
2、FD剧增后带来的IO效率问题

select

因为每次调用时都会对连接进行线性遍历,所以随着FD的增加会造成遍历速度慢的“线性下降性能问题”。

poll

同上

epoll

因为epoll内核中实现是根据每个fd上的callback函数来实现的,只有活跃的socket才会主动调用callback,所以在活跃socket较少的情况下,使用epoll没有前面两者的线性下降的性能问题,但是所有socket都很活跃的情况下,可能会有性能问题。

3、 消息传递方式

select

内核需要将消息传递到用户空间,都需要内核拷贝动作

poll

同上

epoll

epoll通过内核和用户空间共享一块内存来实现的。

总结:

综上,在选择select,poll,epoll时要根据具体的使用场合以及这三种方式的自身特点。

1、表面上看epoll的性能最好,但是在连接数少并且连接都十分活跃的情况下,select和poll的性能可能比epoll好,毕竟epoll的通知机制需要很多函数回调。

2、select低效是因为每次它都需要轮询。但低效也是相对的,视情况而定,也可通过良好的设计改善
参考文章https://www.cnblogs.com/aspirant/p/9166944.html
epoll的优点:

1、没有最大并发连接的限制,能打开的FD的上限远大于1024(1G的内存上能监听约10万个端口);
2、效率提升,不是轮询的方式,不会随着FD数目的增加效率下降。只有活跃可用的FD才会调用callback函数;
即Epoll最大的优点就在于它只管你“活跃”的连接,而跟连接总数无关,因此在实际的网络环境中,Epoll的效率就会远远高于select和poll。

select的几大缺点:

(1)每次调用select,都需要把fd集合从用户态拷贝到内核态,这个开销在fd很多时会很大

(2)同时每次调用select都需要在内核遍历传递进来的所有fd,这个开销在fd很多时也很大

(3)select支持的文件描述符数量太小了,默认是1024

select poll 和epoll的总结:

(1)select,poll实现需要自己不断轮询所有fd集合,直到设备就绪,期间可能要睡眠和唤醒多次交替。而epoll其实也需要调用epoll_wait不断轮询就绪链表,期间也可能多次睡眠和唤醒交替,但是它是设备就绪时,调用回调函数,把就绪fd放入就绪链表中,并唤醒在epoll_wait中进入睡眠的进程。虽然都要睡眠和交替,但是select和poll在“醒着”的时候要遍历整个fd集合,而epoll在“醒着”的时候只要判断一下就绪链表是否为空就行了,这节省了大量的CPU时间。这就是回调机制带来的性能提升。

(2)select,poll每次调用都要把fd集合从用户态往内核态拷贝一次,并且要把current往设备等待队列中挂一次,而epoll只要一次拷贝,而且把current往等待队列上挂也只挂一次(在epoll_wait的开始,注意这里的等待队列并不是设备等待队列,只是一个epoll内部定义的等待队列)。这也能节省不少的开销。
7、进程间通信有哪些方法?你用过哪些,并举例怎么使用的。
进程间通信的方法:

  1. 信号(sinal): 信号是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生;
  2. 管道(pipe)::管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系(通常是指父子进程关系)的进程间使用;
  3. 命名管道FIFO:是半双工的通信方式,但是它允许无亲缘关系进程间的通信;
  4. 命名socket或UNIX域socket:socket也是一种进程间通信机制,与其他通信机制不同的是,它可用于不同进程间的进程通信;
  5. 信号量(semaphore):信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段;
  6. 共享存储(shard memory):共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的 IPC 方式,它是针对其他进程间通信方式运行效率低而专门设计的。它往往与其他通信机制,如信号两,配合使用,来实现进程间的同步和通信;
  7. 消息队列(message queue)::消息队列是由消息的链表,存放在内核中并由消息队列标识符标识。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点;

你可能感兴趣的:(面试)