老毛面试宝典:
1.进程和线程的异同
(1)进程是对运行时程序的封装,是系统进行资源调度的基本单位,实现了操作系统的并发,线程是进程的子任务,是CPU进行资源调度和分配的基本单位。
(2)一个进程可以有多个线程,但至少有一个线程,线程依赖于进程而存在。
(3)进程在运行时拥有独立的内存单元,而同一个进程的线程共享进程的数据段、代码段、扩展段,但每个线程都有自己独立的栈段,用于存放所有局部变量和临时变量。
(4)系统开销:进程创建和销毁开销大。
(5)通信:进程同步比较复杂(管道、消息队列、信号量、信号、共享内存、套接字),而多个线程由于有相同的地址空间,因此通信比较简单(临界区、互斥量、信号量、信号(事件))。
(6)进程调试简单、可靠性高,但创建销毁开销大;线程创建销毁开销小,但编程调试复杂。
(7)进程之间不会相互影响,但一个线程挂掉将使其他线程也挂掉。
(8)进程适用于多核多机分布,而线程适用于多核。
2.为什么要有线程?
进程可以使多个程序可以并发运行,提高资源的利用率和系统吞吐量,但进程在运行过程中如果阻塞,整个进程就会挂起,即使有些工作并不依赖于等待的资源,仍然不会执行。
因此操作系统引入了比进程粒度更小的线程,作为并发执行的基本单位,它有如下优点:
(1)资源上,多个线程有相同的地址空间,节约内存。
(2)切换效率:有相同地址空间,切换效率相当于进程的30倍。
(3)通信机制:线程共享数据段、代码段、扩展段,通信简单。
(4)使多CPU更加有效;改善程序结构。
3.TCP
标志控制
URG:紧急标志
ACK:确认标志
PSH:推标志
RST:复位标志
SYN:同步标志
FIN:结束标志
TCP三次握手
客户端:SYN=1,seq=x ->服务端;
服务端:SYN=1,ACK=1,seq=y,ack=x+1 ->客户端;
客户端:ACK=1,ack=y+1 ->服务端;
为什么A还要发送一次确认呢?可以二次握手吗?
答:主要为了防止已失效的连接请求报文段突然又传送到了B,因而产生错误。如A发出连接请求,但因连接请求报文丢失而未收到确认,于是A再重传一次连接请求。后来收到了确认,建立了连接。数据传输完毕后,就释放了连接,A工发出了两个连接请求报文段,其中第一个丢失,第二个到达了B,但是第一个丢失的报文段只是在某些网络结点长时间滞留了,延误到连接释放以后的某个时间才到达B,此时B误认为A又发出一次新的连接请求,于是就向A发出确认报文段,同意建立连接,不采用三次握手,只要B发出确认,就建立新的连接了,此时A不理睬B的确认且不发送数据,则B一致等待A发送数据,浪费资源。
为什么Server端易受到SYN攻击?
服务器端的资源分配是在二次握手时分配的,而客户端的资源是在完成三次握手时分配的,所以服务器容易受到SYN洪泛攻击,SYN攻击就是Client在短时间内伪造大量不存在的IP地址,并向Server不断地发送SYN包,Server则回复确认包,并等待Client确认,由于源地址不存在,因此Server需要不断重发直至超时,这些伪造的SYN包将长时间占用未连接队列,导致正常的SYN请求因为队列满而被丢弃,从而引起网络拥塞甚至系统瘫痪。
防范SYN攻击措施:降低主机的等待时间使主机尽快的释放半连接的占用,短时间受到某IP的重复SYN则丢弃后续请求。
TCP四次挥手
客户端:FIN=1,seq=x ->服务端;
服务端:ACK=1,seq=y,ack=x+1 ->客户端;
此时,服务端处于CLOSE_WAIT。
服务端:FIN=1,seq=w ->客户端;
客户端:ACK=1, ack=w+1 ->服务端;
此时,客户端处于TIME_WAIT。
CLOSE_WAIT:
产生的原因:
某种情况下客户端关闭了连接,但是我方忙于读写,没有关闭连接
解决方法:
思想:检查出客户端已经关闭的连接,关闭他
之所以会出现这种问题,肯定是服务器端的连接释放的代码存在问题
1.当服务器读写失败时,可以选择关闭连接
2.定期向连接发送询问数据,检查收到的回复数据包(Heart-Beat线程发送指定格式的心跳数据包)
3.修改keep-live参数(超时时间,tcp检查间隔时间:keeplive探测包发送的间隔,tcp检查次数:如果对方不予应答,探测包发送的次数)
TIME-WAIT状态必须等待2MSL的时间:
MSL最长报文段寿命Maximum Segment Lifetime,MSL=2
1)保证A发送的最后一个ACK报文段能够到达B。2)防止“已失效的连接请求报文段”出现在本连接中。【1)这个ACK报文段有可能丢失,使得处于LAST-ACK状态的B收不到对已发送的FIN+ACK报文段的确认,B超时重传FIN+ACK报文段,而A能在2MSL时间内收到这个重传的FIN+ACK报文段,接着A重传一次确认,重新启动2MSL计时器,最后A和B都进入到CLOSED状态,若A在TIME-WAIT状态不等待一段时间,而是发送完ACK报文段后立即释放连接,则无法收到B重传的FIN+ACK报文段,所以不会再发送一次确认报文段,则B无法正常进入到CLOSED状态。2)A在发送完最后一个ACK报文段后,再经过2MSL,就可以使本连接持续的时间内所产生的所有报文段都从网络中消失,使下一个新的连接中不会出现这种旧的连接请求报文段。】
为什么连接的时候是三次握手,关闭的时候却是四次握手?
答:因为当Server端收到Client端的SYN连接请求报文后,可以直接发送SYN+ACK报文。其中ACK报文是用来应答的,SYN报文是用来同步的。但是关闭连接时,当Server端收到FIN报文时,很可能并不会立即关闭SOCKET,所以只能先回复一个ACK报文,告诉Client端,"你发的FIN报文我收到了"。只有等到我Server端所有的报文都发送完了,我才能发送FIN报文,因此不能一起发送。故需要四步握手。
4.http和https有什么区别,加密算法用了什么?
(1)http是以明文传输数据,而https采用TLS加密,具有更高安全性
(2)http端口号是80,https端口号是443
(3)https在三次握手之后,还需要进行SSL的handshake,握手时延增加
(4)https需要认证证书
https:安全、确保数据正确发送。
缺点:延时高、成本高。
4.1HTTP1.0和HTTP1.1的主要区别
1,HTTP/1.0协议使用非持久连接,即在非持久连接下,一个tcp连接只传输一个Web对象,;
2,HTTP/1.1默认使用持久连接(然而,HTTP/1.1协议的客户机和服务器可以配置成使用非持久连接)。
在持久连接下,不必为每个Web对象的传送建立一个新的连接,一个连接中可以传输多个对象!
5.http返回码:
1XX:指示信息,已成功接收,继续处理;
2XX:成功,请求已被成功接收、理解、接受;
3XX:重定向,要完成请求需要进行其他操作;
4XX:客户端错误,语法有问题或请求无法实现;
5XX:服务端错误,服务端未能实现合法请求。
200 ok:客户端请求成功。
6.锁的类型,作用。
读写锁:读锁可以被多个线程访问,但写锁被某线程访问后其他线程被阻塞。
互斥锁:锁机制是同一时刻只允许一个线程执行一个关键部分的代码。
自旋锁:自旋锁与互斥量功能一样,唯一一点不同的就是互斥量阻塞后休眠让出cpu,而自旋锁阻塞后不会让出cpu,会一直忙等待,直到得到锁。
条件变量:利用线程间共享全局变量进行同步的一种机制。
7.分段和分页的区别。
页是信息的物理单位,段是逻辑单位
页的大小固定且由系统决定,段是长度不固定
分页的作业地址空间是一维的,分段为二维的,标识一个地址时,既要段号又要段地址。
8.OSI七层:应用层、表示层、会话层、传输层、网络层、数据链路层、物理层。
TCP/IP协议:
应用层:应用程序间沟通的层,Telnet、SMTP、HTTP、FTP
传输层:负责主机中两个进程之间的通信,提供端到端(end-to-end)的传输,TCP、UDP
网络层:为分组交换网上的不同主机提供通信,负责点到点(point-to-point)的传输,IP、ICMP
数据链路层:负责接收IP数据包并通过网络发送之,或者从网络上接收物理帧,抽出IP数据报,交给IP层,ARP(地址解析)协议和RARP(逆地址解析)协议
9.define 和const的区别
(1)就起作用的阶段而言: #define是在编译的预处理阶段起作用,而const是在 编译、运行的时候起作用。
(2)就起作用的方式而言: #define只是简单的字符串替换,没有类型检查。而const有对应的数据类型,是要进行判断的,可以避免一些低级的错误。
(3)就存储方式而言:#define只是进行展开,有多少地方使用,就替换多少次,它定义的宏常量在内存中有若干个备份;const定义的只读变量在程序运行过程中只有一份备份。
(4)从代码调试的方便程度而言: const常量可以进行调试的,define是不能进行调试的,因为在预编译阶段就已经替换掉了。
10.malloc(),free()和new,delete的区别
(1)申请内存位置:malloc是在堆上申请内存,new是在自由存储器申请内存,自由存储器可以是堆或者静态存储区,取决于new在哪里为对象分配内存。
(2)返回值类型:malloc返回值是void*,而new返回的对象类型的指针,保证了类型安全。
(3)异常返回值:malloc异常返回为NULL,而new失败抛出异常。
(4)new不需要人为分配大小,而malloc必须指定大小。
(5)new/delete会调用构造函数、析构函数,而malloc、free不会调用构造函数、析构函数。
(6)new可调用malloc,malloc不能调用new
(7)new/delete可重载,malloc/free不可重载
11.从输入url到显示网页经历的过程
(1)根据域名,进行DNS域名解析;
(2)得到解析的IP地址,建立TCP连接;
(3)向IP地址,发送HTTP请求;
(4)服务器处理请求;
(5)返回响应结果;
(6)关闭TCP连接;
(7)浏览器解析HTML;
(8)浏览器布局渲染。
12.IP分片的大小及原因。
IP分片是网络上传输IP报文的一种技术手段。IP协议在传输数据包时,将数据报文分为若干分片进行传输,并在目标系统中进行重组,这一过程称为分片。
原因: 数据链路层具有最大传输单元MTU这个特性,它限制了数据帧的最大长度。通常要传输的IP报文的大小超过最大传输单位MTU时就会产生IP分片情况,IP分片经常发生在网络环境当中。
13.DNS作用:将网址/域名解析成 IP 地址。
14.extern关键字作用
extern 表明该变量在别的地方已经定义过了,在这里要使用那个变量。
15.进程运行状态:
创建、就绪、执行、阻塞、终止
16.僵尸进程危害
在每个进程退出的时候,内核释放该进程所有的资源,包括打开的文件,占用的内存等。但是仍然为其保留一定的信息(包括进程号,退出状态,运行时间s等)。直到父进程通过wait / waitpid来取时才释放. 但这样就导致了问题,如果进程不调用wait / waitpid的话,那么保留的那段信息就不会释放,其进程号就会一直被占用,但是系统所能使用的进程号是有限的,如果大量的产生僵尸进程,将因为没有可用的进程号而导致系统不能产生新的进程. 此即为僵尸进程的危害,应当避免。
僵尸进程的避免
(1)父进程通过wait和waitpid等函数等待子进程结束,这会导致父进程挂起。
(2) 如果父进程很忙,那么可以用signal函数为SIGCHLD安装handler,因为子进程结束后, 父进程会收到该信号,可以在handler中调用wait回收。
(3) 如果父进程不关心子进程什么时候结束,那么可以用signal(SIGCHLD,SIG_IGN) 通知内核,自己对子进程的结束不感兴趣,那么子进程结束后,内核会回收, 并不再给父进程发送信号。
(4) 还有一些技巧,就是fork两次,父进程fork一个子进程,然后继续工作,子进程fork一 个孙进程后退出,那么孙进程被init接管,孙进程结束后,init会回收。不过子进程的回收还要自己做。
17.如何解决死锁:
死锁条件:
互斥、请求和保持等待、不可剥夺、环路等待。
请求和保持等待->一次性分配资源;
不可剥夺资源->可剥夺资源;
环路等待->资源有序分配。
18.用户态和内核态。
当一个进程在执行用户自己的代码时处于用户运行态(用户态),此时特权级最低,为3级,是普通的用户进程运行的特权级,大部分用户直接面对的程序都是运行在用户态。Ring3状态不能访问Ring0的地址空间,包括代码和数据;当一个进程因为系统调用陷入内核代码中执行时处于内核运行态(内核态),此时特权级最高,为0级。执行的内核代码会使用当前进程的内核栈,每个进程都有自己的内核栈。
19.C++特性,与C的区别:
C是面向过程的语言,c++面向对象;
C++抽象数据类型,实现了封装、继承、多态。
C效率高、可移植性强。
文件后缀。
C++支持函数重载。
返回值:C默认返回int,C++若无返回值,必须指定为void。
- 智能指针
shared_pt 基于引用计数的智能指针, 会统计当前有多少个对象同时拥有该内部指针; 当引用计数降为0时, 自动释放.主要用于多线程。
weak_ptr 一种弱引用, 指向shared_ptr所管理的对象; weak_ptr只引用, 不计数.是为了解决循环引用。
unique_ptr 独占语义的智能指针, 资源只能唯一的被一个unique_ptr所占有, 当其离开作用域时自动析构.
共享型智能指针通过维护一个对资源的引用计数来实现多个智能指针对象使用同一资源,每当资源上新增一个智能指针对象的时候,引用计数就加1,智能指针对象析构的时候,引用计数减1,当引用计数减为0的时候,释放资源。
21.请问你有没有基于做过socket的开发?具体网络层的操作该怎么做?(其实也是问网络编程的基本步骤)
服务端:socket-bind-listen-accept
客户端:socket-connect
22.请问server端监听端口,但还没有客户端连接进来,此时进程处于什么状态?
这个需要看服务端的编程模型,如果如上一个问题的回答描述的这样,则处于阻塞状态,如果使用了epoll,select等这样的io复用情况下,处于运行状态
23.请你讲述一下Socket编程的send() recv() accept() socket()函数?
send函数用来向TCP连接的另一端发送数据。客户程序一般用send函数向服务器发送请求,而服务器则通常用send函数来向客户程序发送应答,send的作用是将要发送的数据拷贝到缓冲区,协议负责传输。
recv函数用来从TCP连接的另一端接收数据,当应用程序调用recv函数时,recv先等待s的发送缓冲中的数据被协议传送完毕,然后从缓冲区中读取接收到的内容给应用层。
accept函数用了接收一个连接,内核维护了半连接队列和一个已完成连接队列,当队列为空的时候,accept函数阻塞,不为空的时候accept函数从上边取下来一个已完成连接,返回一个文件描述符。
24.请你说一下http协议会话结束标志怎么截出来?
看tcp连接是否有断开的四部挥手阶段。
25.指针和引用的区别:
指针和引用的本质:指针是存放内存地址的一种变量,特殊的地方就在它存放的是内存地址。引用是别名。
声明时:指针可以暂时不初始化,但引用必须在声明时就初始化,且不能初始化为空。引用只能初始化一次,之后不可变,指针可变。
使用:指针需要解引用,引用无需解引用。
sizeof:,指针得到的是指针本身的大小,引用得到的是所指向变量的大小。
指针和引用的自增运算意义不同。
26.C++ 11
(1)关键词auto:自动类型推断。
(2)for()循环范围:使用引用循环变量各值。
(3)初始化列表。
(4)新增nullptr。
(5)智能指针。
27.深拷贝和浅拷贝的区别
1.浅拷贝: 将原对象或原数组的引用直接赋给新对象,新数组,新对象/数组只是原对象的一个引用。
2.深拷贝: 创建一个新的对象和数组,将原对象的各项属性的“值”(数组的所有元素)拷贝过来,是“值”。
28.拷贝构造函数是一种特殊的构造函数,其形参为本类的对象引用。
普通构造函数和复制构造函数的区别
(1) 在形式上
类名(形参表列); //普通构造函数的声明,如Box(int h,int ,int en);
类名(类名& 对象名); //复制构造函数的声明,如Box(Box &b);
(2) 在建立对象时,实参类型不同
系统会根据实参的类型决定调用普通构造函数或复制构造函数。
如 Box box1(12,15,16); //实参为整数,调用普通构造函数
Box box2(box1); //实参是对象名,调用复制构造函数
(3) 在什么情况下被调用
普通构造函数在程序中建立对象时被调用。 复制构造函数在用已有对象复制一个新对象时被调用,在以下3 种情况下需要克隆对象:
① 程序中需要新建立一个对象,并用另一个同类的对象对它初始化:
box2=box1//box2(box1)
② 当函数的参数为类的对象时。
③ 函数的返回值是类的对象。
29.const和static的区别:
(1)const的意义是定义时初始化,不能被改变,static内存只分配一次,下次调用时仍是上次的值;
(2)static修饰变量,将其存在静态变量区,下次调用仍保持之前的值;修饰成员函数,无this指针,只能访问类的static成员变量。
(3)const修饰成员变量,代表只读,不能改变;修饰成员函数,是常函数,不能修改类的成员变量。