• 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,判断其可写时再写。

  • 相关阅读:
    jstl核心标签库
    乱码的解决
    eclipse 中 Servlet 模板代码(其实是代码提示模板)
    因为最近一直在和数据库打交道,所以做了几个小封装
    意外发现的大批量导入数据SqlBulkCopy类
    Http相关
    Tomcat相关
    对于反射中的invoke()方法的理解
    SqlCommandBuilder类是如何构建T-Sql语句
    模拟在内存中的数据库DataSet相关的类
  • 原文地址:https://www.cnblogs.com/scu-cjx/p/8600919.html
Copyright © 2020-2023  润新知