从今天起,豆芽有空也尽己所能,帮助一下大家。

面经来源:https://www.nowcoder.com/discuss/746975?source_id=discuss_experience_nctrack&channel=-1


1. C++多态

基类指针可以按照基类的方式来做事,也可以按照派生类的方式来做事,它有多种形态,或者说有多种表现方式,我们将这种现象称为多态(Polymorphism)。多态的实现机制为虚函数

虚函数的作用是允许在派生类中重新定义与基类同名的函数,并且可以通过基类指针或引用来访问基类和派生类的同名函数

方法是在基类中为同名函数添加关键字virtual


2. 内存结构

img

如上图,从低地址到高地址,一个程序由代码段、数据段、BSS段、堆栈段组成。

  1. 代码段:存放程序执行代码的一块内存区域。只读,不允许修改,代码段的头部还会包含一些只读的常量,如字符串常量字面值(注意:const变量虽然属于常量,但是本质还是变量,不存储于代码段)。

  2. 数据段data:存放程序中已初始化全局变量静态变量的一块内存区域。

  3. BSS 段:存放程序中未初始化全局变量静态变量的一块内存区域。

  4. 可执行程序在运行时又会多出两个区域:堆区栈区。

    堆区:动态申请内存用。堆从低地址向高地址增长。

    栈区:存储局部变量函数参数值。栈从高地址向低地址增长。是一块连续的空间。

  5. 最后还有一个文件映射区(共享区),位于堆和栈之间。


3. 静态成员函数可以直接访问非静态数据成员吗

不能。

当调用一个对象的非静态成员函数时,系统会把该对象的起始地址赋给成员函数的this指针。而静态成员函数不属于任何一个对象,因此C++规定静态成员函数没有this指针。既然它没有指向某一对象,也就无法对一个对象中的非静态成员进行访问。


4. socket编程了解吗

socket是一种特殊的文件。在Linux中,“一切皆文件”,都可以用“打开(open)——读写(write/read)——关闭(close)”的模式来操作。socket就是改模式的一个实现,并提供了一系列对应的函数接口。

我们以TCP为例,下图展示了其交互的过程:

img

图中展示的交互流程,具体如下所述 :

(1)服务器根据地址类型( ipv4, ipv6 )、 socket 类型、协议创建 socket。

(2)服务器为 socket 绑定 IP 地址和端口号。

(3)服务器 socket 监听端口号请求,随时准备接收客户端发来的连接,这时候服务器的socket 并没有被打开 。

(4)客户端创建 socket。

(5)客户端打开 socket,根据服务器 IP 地址和端口号试图连接服务器 socket。

(6)服务器 socket 接收到客户端 socket 请求,被动打开,开始接收客户端请求,直到客户端返回连接信息 。这时候 socket 进入阻塞状态,所谓阻塞即accept()方法一直到客户端返回连接信息后才返回,开始接收下一个客户端连接请求 。

(7)客户端连接成功,向服务器发送连接状态信息 。

(8)服务器 accept 方法返回,连接成功 。

(9)客户端向 socket 写入信息 。

(10)服务器读取信息 。

(11)客户端关闭 。

(12)服务器端关闭 。

涉及到的接口函数如图


5. TCP四次挥手的CLOSE_WAIT状态是在什么时候,出现大量CLOSE_WAIT会有什么影响

第二次挥手时。

大量CLOSE_WAIT影响服务器资源的释放,最后申请不了新的资源后,服务器宕机。


6. IO多路复用了解吗,select有什么问题,1024可以修改吗

IO多路复用:Linux用select/poll函数实现IO复用模型,这两个函数也会使进程阻塞,但是和阻塞IO所不同的是这两个函数可以同时阻塞多个IO操作。而且可以同时对多个读操作、写操作的IO函数进行检查。select/poll会监听所有的IO,直到有数据可读或可写时,才真正调用IO操作函数。

select,poll,epoll都是IO多路复用的机制,I/O多路复用就是通过一种机制,可以监视多个文件描述符(Linux中,一切皆文件,通过文件描述符来标识),一旦某个文件描述符就绪(一般是读就绪或者写就绪),能够通知应用程序进行相应的读写操作。

但select,poll,epoll本质上都是同步I/O,因为他们都需要在读写事件就绪后自己负责进行读写,也就是说这个读写过程是阻塞的,而异步I/O则无需用户自己进行读写,异步I/O的实现是内核负责把数据从内核拷贝到用户空间,用户直接使用数据,这个过程是非阻塞的

三者的原型如下所示:

int select(int nfds, fd_set *readfds,   
           fd_set *writefds, fd_set *exceptfds,   
           struct timeval *timeout  
          );  

int poll(struct pollfd *fds, nfds_t nfds,   
         int timeout  
        );  

int epoll_wait(int epfd, struct epoll_event *events,   
               int maxevents, int timeout  
              );  

总结下select的几大缺点:

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

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

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

理论上1024可以修改


7. 多线程有哪些锁,读写锁有什么特点

(1)互斥锁:mutex,保证在任何时刻,都只有一个线程访问该资源,当获取锁操作失败时,线程进入阻塞,等待锁释放。

(2)读写锁:rwlock,分为读锁和写锁,处于读操作时,可以运行多个线程同时读。但写时同一时刻只能有一个线程获得写锁。

互斥锁和读写锁的区别:

(a)读写锁区分读锁和写锁,而互斥锁不区分

(b)互斥锁同一时间只允许一个线程访问,无论读写;读写锁同一时间只允许一个线程写,但可以多个线程同时读。

(3)自旋锁:spinlock,在任何时刻只能有一个线程访问资源。但获取锁操作失败时,不会进入睡眠,而是原地自旋,直到锁被释放。这样节省了线程从睡眠到被唤醒的时间消耗,提高效率。

(4)条件锁:就是所谓的条件变量,某一个线程因为某个条件未满足时可以使用条件变量使该程序处于阻塞状态。一旦条件满足了,即可唤醒该线程(常和互斥锁配合使用)

(5)信号量




以上所有题的答案其实都来源于我的博客面经,欢迎大家围观:https://blog.nowcoder.net/jiangwenbo