Linux后台研发面试题

本系列给出了在复习过程中一些C++后台相关面试题,回答内容按照笔者的知识点掌握,故有些问题回答较为简略

1、信号的生命周期

一个完整的信号生命周期可以用四个事件刻画:1)信号诞生;2)信号在进程中注册完毕;3)信号在进程中注销完毕;4)信号处理函数执行完毕。

信号诞生:某个事件发生,触发相应信号;

信号注册:Linux中分为非实时信号(signal函数注册)和实时信号(sigaction函数注册,可以支持信号带有参数,signal不支持),从kernel对非实时信号和实时信号的处理中进行描述。

信号注销:进程在执行信号处理函数之前,首先要把信号在进程中注销,对于实时信号如果还存在多个排队的信号,其在信号注销之后不会在信号集中删除该信号;

执行信号处理函数:执行函数。这里涉及到内核从kernel返回user处理信号的过程:信号在程序上下文在kernel中的时候到来,在返回user时信号被响应,此时运行环境依然为内核态,将信号处理

函数栈帧压入到内核态栈帧,此时弹栈后返回到用户层执行信号函数,执行完毕后弹栈又重新回到内核态,继续处理系统调用返回,重新返回到用户态运行(即信号处理的两次陷入内核)

2、信号的产生方式

(1)终端按键产生信号,比如ctrl c

(2)硬件异常产生信号,比如执行除以0的指令,进程访问非法内存地址

(3)软件产生信号,比如kill函数

3、信号的处理方式

(1)忽略信号;

(2)执行信号的默认处理动作;

(3)执行自定义信号处理函数

4、如何消除隐式转换

如果c++类的构造函数有一个参数,那么在编译的时候有一个缺省的转换操作:将该构造函数对应数据类型的数据转换为该类型对象。

比如:

class Test
{
public:
    Test(int num){}   //定义为explict Test(int num){},消除隐式转换,下述编译不能通过
}

Test obj = 10

上述代码中编译器将自动将整形转换为Test类对象,实际上等同于Test obj(10),这种是显示转换。如果有一个转换Test obj='a',实际上num将等于97,这种不符合本类的设计初衷。为了防止这种隐式转换,C++定义explicit关键字,被修饰的构造函数不能发生相应的隐式类型转换,只能以显示方式进行类型转换。

5、重载,重写和隐藏的区别

这个回答后续补充

6、volatile表示什么?有什么作用?

该部分内容解释,详见这里

7、malloc和new的区别,free和delete的区别

malloc和free是c语言的标准库函数,new和delete是c++的运算符,都可以用于动态申请或释放内存。对于内部数据类型的对象(int, char等),采用malloc和new都可以完成对对象内存的分配,同时函数返回所分配内存的首地址。但是对于非内部数据类型对象,采用malloc和free不能完成内存分配,对象在创建时需要执行构造函数,在释放时需要执行析构函数,需要new与delete运算符完成上述内存的分配。malloc与free,new和delete必须配对使用,避免内存泄露。

8、free一个数组时如何知道要释放多大的内存

在malloc一块内存地址时,现代编译器会把内存大小数值放在分配地址开始的位置,从而使得free该块内存时,可以知道需要释放多大的内存。

9、Linux内部提供了哪些调试宏

__FILE__:文件名;__DATE__:日期 __TIME__:时间 __LINE__:当前代码行 __FUNCTION__:所在函数名称

测试代码:

#include <stdio.h>  
  
int main()  
{  
    printf("The file is %s.
",__FILE__);  
    printf( "The date is %s.
", __DATE__ );  
    printf( "The time is %s.
", __TIME__ );  
    printf( "This is line %d.
", __LINE__ );  
    printf( "This function is %s.
", __FUNCTION__ );     
      
    return 0;  
}

结果:

The file is macro.c.  
The date is Aug 24 2012.  
The time is 23:13:26.  
This is line 8.  
This function is main.

10、手写线程安全的单例模式

分为懒汉模式和饿汉模式,详见这里

11、引用和指针的区别

(1)引用是给另一个变量起的别名,所以引用不会额外分配内存空间。指针是一个实体,需要额外分配内存空间;

(2)引用在定义时必须进行初始化,并且之后不能够改变,指针在定义时不一定需要进行初始化,且指向的内容可以变化;

(3)有多级指针,但没有多级引用;

(4)指针和引用的自增运算结果不一样,指针自增指向下一个内存单元,引用自增是将遍历加1;

(5)sizeof引用得到的是引用变量的大小,sizeof指针得到的是指针变量的大小(32位为4字节,64位为8字节);

(6)引用访问一个变量是直接访问,指针访问变量是间接访问。

详见这里

12、pthread_cond_signal和pthread_cond_broadcast的区别

条件变量。前者用于唤醒一个等待条件的进程,后者用于唤醒所有等待条件的进程(惊群问题)。

13、如果用map删除了一个元素,迭代器还能用吗?为什么?怎么样做可以再接着用。

详见这里

14、TCP三次握手和四次挥手及各自的状态。

结合《Unix网络编程卷1》。

(一)三次握手:

客户端    服务端

SYN_SEND  

       SYN_RECV

ESTABLISH  ESTABLISH

(二)四次挥手:

客户端    服务端

FIN_WAIT1  

       CLOSE_WAIT

FIN_WAIT2  

       LAST_ACK

TIME_WAIT

CLOSED   CLOSED

15、TCP如果两次握手会出现什么问题

如果二次握手的ACK丢失,客户端不能判断当前是否已经建立连接。

16、TCP四次挥手为什么要有TIME_WAIT状态?

客户端在接收服务端最后一次消息发送后,回复消息,状态会变为TIME_WAIT,并等待2MSL(MSL为报文的最大存活时间),如果服务端没有接收到客户端最后的恢复消息,会重新发发送最后一次关闭数据,数据包来回时间为2MSL。

17、死锁的原因

(1)死锁的四个条件:

互斥条件:描述一个资源只能被一个进程所占用

占用和等待条件:已经占有资源的进程会由于请求其他进程占用的资源而导致等待

不可剥夺条件:一个进程需要请求被其他进程占用的资源时,不能抢占该资源,只能通过其他进程释放

循环等待条件:死锁发生时,系统中存在着进程与资源的环路。

(2)避免死锁的方法:银行家算法

(3)预防死锁的方法:破坏四个必要条件

破坏互斥条件:一切都使用假脱机技术

破坏占用和等待条件:在开始时请求所有资源

破坏不可剥夺条件:使资源可剥夺

破坏环路等待条件:给所有资源按升序编号,一个进程每次只能请求比当前资源序号高的资源。

18、为什么要字节对齐

详见这里

19、进程间通信的方式有哪些?线程间通信的方式有哪些?

进程间通信:信号,信号量,消息队列,管道(有名管道和无名管道),共享内存,socket

线程同步方式:互斥锁和读写锁,条件变量;信号量,临界区。

20、解决hash冲突的方法

详见这里

21、C++的内存分为几个部分

Linux进程虚拟地址空间。详见这里

22、如何得到一个结构体成员的偏移量?

类比于Linux内核进程current宏实现。详见这里

23、进程与线程的区别

(1)进程是资源分配和调度的独立单元,线程是CPU调度的基本单元;

(2)同一个进程中可以包括多个线程,并且线程是共享进程资源的(这里共享只包括,写时拷贝的内存页,文件描述符,信号),但是线程独有的(task_struct进程结构体,内核栈,寄存器)

(3)线程结束不会影响到同一进程中的其他线程,但进程结束会影响到所有线程;

(4)由于线程共享同一进程的数据资源,所以对于数据的访问需要保证同步与互斥问题。

24、对一个数组而言,delete a和delete []a有什么区别?

delete a只会删除这个数组的第一个元素,其后的元素内存不会被释放掉,从而导致内存泄露。delete []a将会释放所有元素内存

25、IO模型主要有哪些

(1)同步阻塞IO:即传统的IO模型;

(2)同步非阻塞IO:默认创建的socket都是阻塞的,非阻塞IO要求socket被设置为NONBLOCK

(3)IO多路复用:Reactor设计模式,常用的select和epoll

(4)异步IO:Proactor模式,也称为异步非阻塞IO。

同步和异步的概念描述的是用户线程与内核的交互方式:同步是指用户线程发起IO请求后需要等待或者轮询内核IO操作完成后才能继续执行;而异步是指用户线程发起IO请求后仍继续执行,当内核IO操作完成后会通知用户线程,或者调用用户线程注册的回调函数。

阻塞和非阻塞的概念描述的是用户线程调用内核IO操作的方式:阻塞是指IO操作需要彻底完成后才返回到用户空间;而非阻塞是指IO操作被调用后立即返回给用户一个状态值,无需等到IO操作彻底完成。

详见http://www.cnblogs.com/fanzhidongyzby/p/4098546.html

26、select,poll,epoll的区别

略。

27、TCP的nagle算法和延迟ACK,具有什么好处,但如果在一起使用会出现什么问题。

详见这里

28、epoll的LT模式和ET模式

详见这里

LT模式,如果有数据未处理完,会一直触发处理。ET模式,数据必须要一次性完成处理(采用while循环读入数据),否则下次不会被触发处理流程

一个面试问题:LT模式下,如果socket可写,将会一直触发可写,如何解决?

答:在需要写socket时,将其加入epoll的event,触发可写时进行写操作,写完后通过epoll_ctl将其移出event,从而避免重复触发可写。缺点在于:如果在发送数据量较少时,仍然需要两次epoll_ctl操作,会造成性能损耗。

优化方法:在需要写socket时,直接写socket,如果发生eagain或ewouldblock,再将其加入到epoll的event,判断其可写时再写。

原文地址:https://www.cnblogs.com/scu-cjx/p/8600919.html