基础知识总结

1.C++问题

  1. 声明和定义
  2. 申请二维数组
  3. 深拷贝和浅拷贝int **p=new int* [3];申请大小为3的指向整型指针的数组
  4. STL中的vector,list,set,map
  5. std::move的作用:将对象的状态或者所有权从一个对象转移到另一个对象,没有对象内存的拷贝。用途:像C++标准库vector::push_back操作,会对对象进行复制和内存拷贝,通过std::move,可以避免不必要的拷贝操作。

2.计算机网络

2.1 HTTP

  1. http和https的区别
    http是传输的是明文,不能传输敏感信息,如银行卡号,密码。端口号一般是80。
    htps是SSL+http协议,可以进行加密传输、身份认证。端口号一般是443。
  2. HTTP报文 参考
  • HTTP的报文格式: 起始行 头部字段 -空行 -消息正文
    注:其中起始行头部字段称为Header,消息正文称为body,Header和body之间一定要有空行隔开。
  • 请求行的格式:请求方法-空格-URL-空格 -版本号-回车换成,请求的头部使用key:value更详细的方式说明HTTP报文,我们就是要解析这样的http请求,并发送应答报文给客户端。
  1. HTTP状态码;1.信息,服务器收到请求,需要请求者继续执行。2.成功,操作被成功接收并处理。3.重定向,需要进一步操作以完成请求。4.客户端错误,请求包含语法错误或无法完成请求。5.服务器错误,服务器在处理请求的过程中发生了错误。

2.2 TCP

TCP/IP三次握手和四次挥手 参考
TCP三次握手1. 客户端向服务端发送seq=x,syn=1的报文,客户端进入SYN_SEND状态,2. 服务端向客户端发送ACK=1,ack=x+1,syn
=1,seq=y的报文,服务端进入SYN_RECIVED状态,3. 客户端给服务端发送ACK=1,ack=y+1,seq=x+1的报文,然后客户端和服务端进入ESTABLISED状态。
为什么要有要有第三次握手?:防止失效的报文突然又传到服务端,从而产生错误。 如果使用两次握手就建立了连接,假设有一种场景,客户端发送的请求连接报文因为网络延迟滞留在某个结点,客户端长时间收不到回应,就会认为服务端没有收到,就会重发,等到之前滞留的那个报文网络畅通了,到达服务端后,又建立连接了,这就导致了不必要的错误和资源的浪费。如果采用三次握手,那么失效的报文来了之后,服务端收到了那条失效报文并且回复了确认报文,但是客户端不会再确认了,由于服务端收不到确认,就不能建立连接。
TCP四次挥手 1. 客户端给服务端发送FIN=1,seq=x,客户端进入FIN_WAIT-1状态。2. 服务端给客户端发送ACK=1,ack=x+1,seq=y的消息报文。服务端进入CLOSE_WAIT状态,客户端进入FIN_WAIT-W状态。3. 服务端发送完报文后给客户端发送ACK=1,ack=x+1,FIN=1,seq=z的报文,进入LAST_ACK状态。4. 客户端给服务端发送ACK=1,ack=z+1,seq=x+1,客户端进入TIME_WAIT状态,等待2个MSL后进入CLOSED状态,服务端收到消息后进入CLOSED状态。
问题:为什么客户端最后还要等待2MSL(最长报文寿命)?
1.保证客户端发送的最后一个ACK报文到达服务器。 因为这个ACK报文可能会丢失,站在服务器的角度,已经发了FIN+ACK,然后应该受到ACK报文,如果没有收到,服务器就认为自己发的报文客户端没有收到,就会重新给客户端发送,客户端就能在这个2MSL时间段内收到这个重传报文,然后给出回应报文,并且会重启2MSL计时器。
2.防止类似于“三次握手”中提到了的“失效的报文”出现在本连接中。 客户但发送完最后一个报文后,在这个2MSL时间中,就可以保证关于本连接的所有报文段从网络中消失,这样新的连接中就不会旧的请求报文。
为什么建立连接的时候是三次握手,断开连接的时候是四次握手?
因为在建立连接受,服务端处在listen状态,收到客户端的SYN后,将SYN和ACK一起发给客户端了。
在断开连接的时候,服务端收到客户端的FIN报文后,只是表示客户不再发送数据了,但是自己的数据可能还没有发送完,需要等自己的数据发送完了再断开连接,就把FIN和ACK分开发了。如果服务端收到FIN报文之前,自己的数据已经发完了,其实也可以把FIN和ACK一起发,成为三次挥手。
如果建立连接后,客户端出现故障怎么办?
如果客户端出现状态,服务端肯定不能一直等着,白白浪费资源。TCP还设有一个保活计时器,服务端每次收到客户端的消息后就重置这个定时器,这个定时器的时间通常是两个小时,如果两个小时客户端还没有给服务端发送消息,服务端就会发送一个探测报文段,以后每隔75秒发送一次,一脸发送10个探测报文仍然没有反应,服务端就会认为客户端出了故障,接着关闭连接。

TCP如何保证可靠传输

  • 分包排序。
  • 超时重传+接收端丢弃重复数据。
  • 校验和:如果接收端的校验和有误,丢弃这个报文段。
  • 利用滑动窗口机制进行流量控制,如果接收方来不及处理发送方的数据,就会提示发送方降低发送速率。
  • 拥塞控制:当网络拥塞时,减少数据的发送。
  • 停止等待协议:每发完一个分组,就等待对方确认,收到确认后再发下一个分组。

拥塞控制
TCP发送方维持一个拥塞窗口,窗口大小取决于网络的拥塞程度,发送方的发送窗口取拥塞窗口和接受方的接受窗口中的较小的那一个。
拥塞控制的四种算法:慢开始、拥塞避免、快重传和快恢复

  • 慢开始:发送方将数据缓慢的注入网络,由小到大增加拥塞窗口的数值。cwnd初始值为1,每经过一次往返,cwnd加倍。
  • 拥塞避免:让拥塞窗口缓慢增大,每往返一次,cwnd加1。
  • 快重传和快恢复:如果接收端接收到不按顺序的包,就会给发送发送一个重复确认,如果发送端接收到三个重复确认,就会立即重传丢失的包。单独包丢失时,FRR能最有效工作,多个包丢失的话就不能很有效的工作了。

URL请求
浏览器在和服务器建立一个TCP连接后是否会在HTTP请求完之后断开
答:这个不一定,在HTTP/1.0中,服务器响应完之后,会断开,但是也有一些服务器对connection:keep-alive的Header进行了支持。

TCP和UDP的区别

	-在连接方面:TCP是面向连接的,在通信前需要建立连接;UDP是面向无连接的,在通信前不需要建立连接。
	-在可靠行方面:TCP保证信息可靠交付;UDP是尽力交付,不能保证可靠交付。
	-在速度方面:TCP有确认机制,速度慢;UDP只是一直发,不管能不能到,速度快。
	-在连接对象数量方面:TCP只能是一对一;UDP可以一对一,一对多,多对一,多对多服务。
	-在发送QQ消息的时候,使用的是TCP协议;在视频通话的时候用的主要是UDP协议。

RST分节

RST表示复位,用来异常的关闭连接。发送RST包关闭连接时,不必等缓冲区都发出,直接丢弃缓冲区的包发送RST包。接收端收到RST包后,也不必发送ACK包来确认。出现RST包的情况:
	-服务器未打开,而客户端来连接。
	-请求超时:比如设置recv的超时时间为100ms,响应超过了100ms就认为接受超时了,就会发送RST拒绝进一步发送数据。
	-提前关闭:数据没读完就关闭连接,就会发送RST分节。
	-向一个关闭的socket上写数据会收到RST分节。比如说服务器关闭了socket,客户端还在向这个socket写数据,就会收到RST分节。

2.3输入url,显示主页的过程

   - DNS解析
   - TCP链接
   - 发送HTTP请求
   - 服务器返回HTTP报文
   - 浏览器渲染界面
   - 连接结束
  1. dns如何解析出ip
	1.查找:浏览器自身-操作系统(hosts)-本地域名服务器(LDNS)-根域名服务器
	2.根域名服务器返回给本地域名服务器LDNS一个主域名服务器的IP地址(gTLD Server如.com,.cn.org等)
	3.本地域名服务器LDNS给返回的主域名服务器gTLD发送请求
	4.主域名服务器根据所给的的域名查找并返回这个域名对应的Name Server,Name Server就是网站注册的域名服务器
	5.Name Server根据映射关系找到目标ip,返回给LDNS
	6.LDNS缓存这个域名和对应的ip地址
	7.LDN把解析的结果返回给用户,用户根据TTL值缓存到本地系统中,解析域名结束
  1. 两台主机之间如何进行数据传输
	以QQ为例
	(1)应用层封装,首先应用对消息进行封装:app+消息。识别应用程序。
	(2)传输层封装:TCP报头+应用数据。
	(3)网络层封装:IP报头+应用数据。
	TCP报文包括序号确认号、控制位、校验和等,用来保证可靠传输。
	TCP报文的源端口号和目的端口号,与源ip地址和目的ip地址唯一确定一条TCP连接。
	(4)数据链路层:以太网首部+应用数据+以太网尾部。
	(5)数据到达主机接口,通过网线被传送到另一个主机的接口,然后再一层层的把报头去掉,最后数据到达应用层。

3.操作系统

  1. 进程和线程
    进程:资源分配的最小单位,也就是一个运行的程序
    线程:程序执行的最小单位,也就是程序的一个单元执行流
    线程安全:多个线程对类或者类的方法,同一个文件描述符,或者网络套接字操作的时候按照我们想的一样去做。
  2. 孤儿进程
    一个父进程已经被终止的进程被称为孤儿进程,那么父进程的id就是1,即init进程id。
  3. 保证线程安全的方法
    竞争和原子操作、同步和锁、可重入、防过度化
  • 竞争和原子操作:linux系统可以提供了一些常用操作的原子指令,确保了线程安全。
  • 同步和锁:在程序里面加互斥锁、读写锁、条件变量、屏障来保证线程安全。
  • 可重入:保证函数可重入可以保证线程安全。
  • 防过度优化:可以加volatile,告诉编译器对访问该变量的代码不进行优化,提供对特殊地址的稳定访问。volatile修饰的关键字,系统总是从他所在的内存中读取关键字。如果不加volatile,不同的线程有可能从内存中读取数据,有可能在寄存器中读取数据。
  1. 同步和互斥
    互斥是通过竞争独占一个资源,彼此之间不知道对方的存在,执行是乱序的。比如说消费者的各个线程就是互斥,可以用互斥锁实现。
    同步时建立在互斥之上,协调多个线程完成同一任务,彼此之间知道对方的存在,执行是有序的。比如说生产者和消费者之间就是同步,可以用互斥锁加条件变量实现。
  2. 死锁和避免死锁
    ①死锁的概念:如果试图对一个互斥量加锁两次,或者一个以上的互斥量相互访问,导致进程无法向前运行,这就是死锁。
    ②避免死锁:方法1:可以通过仔细控制互斥量的加锁顺序来避免死锁的发生,如果所有线程在对B加锁之前锁住A,就不会发生死锁,可能发生死锁只会在一个线程以相反的顺序对互斥量进行加锁。方法2:可以释放占有的锁,过一段时间再试,这种情况下可以使用pthread_mutex_tryunlock接口避免死锁。方法3:使用pthread_mutex_timedlock设置需要允许阻塞的时间,超时就返回EIMEDOUT,并且解开自己的锁。
  3. 互斥锁,mutex
    ①概念:互斥,线程独自占有,其他线程想占有时阻塞
    ②函数:
  4. 文件系统
pthread_mutex mu=PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_init();//两种初始化方式
pthread_mutex_destroy();
pthread_mutex_lock();
pthread_mutex_unlock();
pthread_mutex_trylock();
  1. 读写锁,也叫共享互斥锁,rwlock
    ①概念:读写锁有三种模式:读状态加锁,写状态加锁,不加锁。写模式的读写锁允许一个线程占用;读模式的读写锁允许多个线程占用。
    ②工作方式:当读写锁是写加锁状态,所有对这个锁进行加锁的状态都会阻塞;当读写锁是读加锁状态,所有对这个锁进行读加锁状态的线程都可以访问,所有写加锁状态的线程都会阻塞,直到所有线程都是放他们的读加锁。
    ③函数:
pthread_rwlock_init();//初始化
pthread_rwlock_destroy();//销毁
pthread_rwlock_rdlock();//读加锁
pthread_rwlock_wrlock();//写加锁
pthread_rwlock_unlock();//解锁
pthread_rwlock_tryrdlock();//尝试加读锁,加不上返回错误
pthread_rwlock_trywrlock();//尝试加写锁,加不上返回错误
pthread_rwlock_timerdlock();//超时返回ETIMEDOUT
pthread_rwlock_timewrlock();//超时返回ETIMEDOUT
  1. 自旋锁
    ①概念:自旋锁和互斥量类似,但是不是通过休眠使进程阻塞,而是在获得锁之前一直自忙等(自旋)操作。
    ②用处:锁被持有的时间很短,而且线程并不希望在重新调度上花费太多成本。
  2. 记录所,应该说是字节范围锁
    可以锁住文件的一个区域或者整个文件,保证一个进程读或者修改文件时,阻止其他进程修改同一文件区。多个进程写加锁和读加锁时互斥的,单个进程新锁会替换旧锁。
    建议性锁:建议不阻止;强制性锁:阻止。
  3. I/O多路转接、多路复用
    先构造一张有文件描述符的列表,然后调用一个函数,在有准备好的I/O操作时,函数返回,并且告诉进程哪些文件描述符已准备好可以进行I/O操作。
  4. 条件变量,
    ①概念:互斥量必须在锁定之后才能改变条件
    ②函数:
pthread_cond_t cont=PTHREAD_COND_INITIALIZER;
pthread_cond_init();//两种初始化方式
pthread_cond_destroy();
pthread_cond_wait(cond,mutex);
pthread_cond_timewait(cond,mutex,timesp);
//wait函数的作用:释放mutex,等待cond的信号

pthread_cond_singal(&cond);//唤醒一个进程
pthread_cond_broadcast(&cond);

③条件变量存在的意义:如果直接使用mutex,除了生产者、消费者之间需要竞争互斥量之外,消费者之间也需要竞争互斥量,如果队列中没有数据,那么消费者之间竞争互斥锁是没有意义的。有了条件变量机制后,只有生产者完成生产吗,才会引起消费者之间的竞争,提高了程序效率。

  1. 信号
    ·是一种软件中断。Ctrl+C产生中断信号SIGINT。
    ·kil命令是向内核发送终止信号。

  2. 屏障
    ①概念屏障是用户协调多个线程并行工作的同步机制。屏障允许某个线程等待,知道所有线程都到达某一点,然后从该点执行。pthread_join就是一种屏障,允许一个线程等待,知道另一个线程退出。
    ②函数

pthread_barrier_init(barrier,attr,count);//初始化屏障,可以指定count,表明在允许所有线程继续工作之前,必须到达屏障的线程数目
pthread_barrier_destory(&barrier);//销毁屏障
pthread_barrier_wait(&barrier);
//表明线程已经完成工作,准备等所有的其他线程赶上来。
//调用pthread_barrier_wait()的线程在屏障技术(pthread_barrier_init时设定)未满足条件时,会进入休眠状态,如果该线程是最后一个调用pthread_barrier_wait()的线程,就满足了屏障计数,所有的线程都会被唤醒。
//对于任意一个线程,pthread_barrier_wait的返回结果是PTHREAD_BARRIER_SERIAL_THREAD,其他线程的返回值是0,这就使得一个线程可以作为主线程,处理其他线程的工作结果。
  1. 使用线程的过程
pthread_t tid;
pthread_create(tid,NULL,fun,arg);
void* fun(void* arg);
pthread_join(tid,NULL);
  1. 散布读(readv)和聚集写(writev)
    用于在一次函数调用中读、写多个非连续缓冲区。
函数
#include<sys/uio.h>
ssize_t readv(int fd,const struct iovec *iov,int iovent);
ssize_t writev(int fd,cosnt struct iovec *iov,int iovent);
struct iovec{
void *iov_base;//缓冲区的开始地址
size_t iov_len;//缓冲区的长度
}
  1. 函数readn和writen
    本质:调用read和write直至读写了n个字节。

  2. 守护进程
    守护进程是一种生存期长的进程,通常执行系统的日常事务。比如说系统守护进程:syslogd,login,网络守护进程sendmail,httpd等。

  3. 你理解的操作系统
    控制计算机硬件的一个软件

  4. 用户态和内核态

  • 区别:内核态运行操作系统程序,操作硬件;用户态运行用户程序。
  • 指令划分:特权指令是只能操作系统使用,用户程序不能使用的指令,如I/O、内存清零;非特权指令是用户程序可以使用的指令,如控制转移,算术运算。
  • 特权级别:R0,R1,R2和R3。R0相当于内核态,R3相当于用户态。
  • 转换后:用户态->内核态,通过中断、异常、陷入机制(访管指令);内核态->用户态,通过设置程序状态字PSW。
  • 联系:一个进程在执行用户自己的代码的时候处于用户态。如果执行文件的读写操作、网络数据的收发,这些系统调用会调用内核的代码,就会处于内核态。
  • 用户态切换到内核态的三种方式:1.系统调用,这是用户态主动切换到内核态的方式,比如说fork()执行了个创建新进程的系统调用,malloc执行了sbrk的系统调用。2.异常,CPU在执行用户态下的程序是,发生了一些一场,会触发当前运行进程到处理此异常的内核相关进程中,比如说缺页异常。3.外围设备的中断。
  1. 什么是系统调用
    系统调用是应用程序和系统间的接口。比如说malloc调用了sbrk来申请内存,printf会调用write系统调用以输出一个字符串。
  2. malloc和free的原理
    malloc先遍历堆中的内存是否可用,如果可用并且大小符合就直接用,如果不能用的话就向操作系统中申请新的内存。
    free把内存归还给操作系统。
  3. 系统调用和库函数的区别:系统调用是由内核实现的,库函数是从用户角度实现的,有时候库函数会调用系统调用。
  4. 进程和线程区别
  • 进程是资源分配的最小单位,线程是cpu调度的最小单位。
  • 线程必须在进程先运行;一个进程可以包含多个进程。
  • 不同进程之间很难共享数据;同一进程下的不同线程很容易共享数据,当线程共享一个内存时,可以加互斥锁。
  1. 什么是协程

  2. 进程间通信方式

  3. 阻塞I/O、非阻塞I/O
    阻塞I/O:每个用户一个线程,线程会等待用户I/O操作,I/O操作完之后才会继续执行。
    非阻塞I/O:一个线程服务多个用户,线程给用户注册一个回调函数,用户完成I/O操作后执行回调函数。

  4. 对于I/O密集型程序,最佳线程数=cpu核数*(1/cpu利用率)=cpu核数*(1+i/o耗时/cpu耗时)

  5. 操作系统由哪些构成
    操作系统由内核、驱动程序、接口库、文件系统、外围组成。其中最重要的内核由进程调度、内存管理、网络接口、虚拟文件系统、进程间通讯五部分组成。

  6. 进程的状态、切换、调度

  • 状态
    • 就绪:进程具备运行条件,等待系统分配处理器就可以运行。
    • 执行:进程占有处理器正在运行。
    • 阻塞:进程不具备运行条件,正在等待某个事件完成,比如说等待互斥量解锁。
  • 状态的切换:
    • 就绪->运行:就绪状态的进程,当进程调度程序分配了处理器后,就变为运行状态。
    • 执行->就绪:处于执行状态的进程,由于分配给他的时间片用完了,必须让出处理器,就变为就绪状态了。
    • 执行->阻塞:处于执行状态的进程因为等待某个事件发生无法继续执行,就变成阻塞状态。
    • 阻塞->就绪:阻塞状态的进程,若其等待的时间已经发生,于是进程由阻塞状态变为就绪状态。
  1. 操作系统中的进程调度算法
  • 先来先服务调度算法
  • 短作业优先调度算法
  • 高优先权优先调度算法
  • 高响应比优先调度算法
  • 时间片轮转法
  • 多级反馈队列调度算法:①设置多个就绪队列,第一个优先级最后,然后优先级一次降低。②有新进程来了将新进程放在第一个就绪队列的末尾,如果正在执行的进程能执行完,就撤离系统,如果执行不完就放在下一个就绪队列的末尾。③如果前面的就绪队列为空,并且有新进程来了,那么就将正在这执行的进程放在这个就绪队列的末尾,去执行新的进程。
  1. 进程间通信方式(共享内存、管道、消息)
  2. 线程的实现方式(一对一、多对一等)
  3. 互斥与同步(信号量、管程、锁)
  4. 并发经典的问题:读者写者、哲学家就餐问题
  5. 为什么需要虚拟内存,MMU 具体如何做地址转换的
  6. 内存为什么分段、分页
  7. 页面置换算法
  8. 文件系统是如何组织的
  9. 虚拟内存
  • 虚拟内存
    参考
    • 现代处理器的寻址方式:程序虚拟寻址。CPU生成的虚拟地址在传送给主存之前被翻译成一个物理地址。地址翻译需要CPI硬件和操作系统共同管理,CPU上的叫做内存管理单元,利用查询表翻译虚拟地址,查询表由操作系统管理。
    • 程序和虚拟内存的关系:因为一个程序是逐段执行的,将需要执行的程序从硬盘中复制到内存中,其他不运行的程序保存在硬盘里。经过有目的的换入换出,处理器就可以运行一个大于实际物理内存的程序。也就是说处理器好像拥有了一个大于实际屋内空间内存的内存空间,这个储存空间叫做虚拟内存,真正的内存空间被称为实际物理内存空间。
    • 映射:由于存在两个地址,因此一个应用程序从编写到执行,需要进行两次映射。第一次映射是到虚拟内存空间;第二次映射是到物理内存空间,第二次映射是有硬件和软件共同完成的,硬件部分是储存管理单元,软件部分是操作系统的内存管理模块。
    • 虚拟地址/物理地址表格:用来映射虚拟地址和物理地址,操作系统的内存管理模块建立,以虚拟地址位索引,记录了程序段所占用的物理地址,是硬件的存储管理单元将虚拟地址转换为实际物理地址的依据。
    • 技术关键:程序映射表
  • linux的虚拟内存技术
    • 页和页框:linux将虚拟空间分为若干个大小相等的存储分区,成为页;然后按页的大小划分为若干块。物理内存中的块叫做页框。页与页块是linux实现虚拟内存技术的基础。页和页框的带下一般是4kB,根据系统和应用的不同,也和页框页可能会变化。高位段是页和页框的编码,低位段时页和页框的偏移量。
    • 在将一个页映射到某个页框上的同时必须将页码和对应的页框码填入映射记录表中。
    • 进程和虚拟空间:每个进程都有自己的4G空间,新进程建立的时候会建立自己的内存空间,进程的数据、代码等会从磁盘拷贝到自己的进程中间,每个进程分配的内存空间都会与对应的磁盘空间映射。每个进程的4G空间实际上时虚拟内存空间,每次访问内存空间的地址都需要将地址翻译为实际物理内存地址。
    • 页表:所有进程共享同意物理内存,每个进程只把自己目前所需的虚拟内存空间映射到物理内存上。进程需要知道那些内存地址上的数据在物理内存上,哪些不在,在物理内存上的哪里,因此需要页表来记录。页表中的每一个表项分为两部分,第一部分记录此页是否在物理内存上,第二部分记录物理内存页的地址。
    • 缺页异常:当进程访问某个虚拟地址时会去查看页表,如果发现对应的数据不在物理内存上则出现缺页异常。缺页异常的处理过程是将进程所需要的数据从磁盘拷贝到物理内存中,如果内存已满没有空余位置,就回去找一个页进行覆盖,如果覆盖过的页曾经被修改过,需要将此页写回磁盘。
  • 优点:利用碎片,使用库文件的代码,可以只储存一份这样的代码
  • 进程加载时,内核只是位进程创建了虚拟内存的布局,具体是初始化进程控制表中内存相关的链表,不直接拷贝到物理内存,只建立虚拟内存和物理内存的映射,等运行相关程序时,才会通过缺页异常来拷贝数据,进程运行过程中需要动态分配内存,比如malloc也只是分配了虚拟内存,也就是虚拟内存对应的也白哦项的相关设置,进程真正访问到此数据时才引发缺页异常。
  1. 虚拟文件系统
  • 引论:虚拟文件系统屏蔽了不同文件系统的操作差异和实现细节,提供了统一的实现框架和操作接口,降低了操作文件和接入新文件系统的难度。
  • 虚拟文件系统是内核的一个软件层,给用户的程序提供文件系统接口;同时它也提供了内核中一个抽象的功能,允许不同的文件系统共存。借用虚拟文件系统可以是应用open、read、write这些系统调用操作文件。
  • 比如说ret=write(fd,buf,len);首先会调用虚拟文件系统的通用系统调用sys_write();接着sys_write()会根据fd找到所在文件系统提供的写操作函数op_write(),最后调用文件系统的写操作函数op_write()。
  • 四种对象类型:①超级块对象:代表一个已安装的文件系统;②索引节点对象:代表具体文件;③目录项对象,代表哦一个目录项,是文件路径的一个组成部分;④文件对象:代表进程打开的文件。
  1. exit和_exit
    exit先执行一些清理,饭后返回内核:_exit直接返回内核。
  2. 为什么有了close之后,还是用shutdown呢?
  • 首先,只有最后一个活动引用关闭时,close才释放网络端点。这意味着如果复制一个套接字,要直到关闭了最后一个引用他的文件描述符才释放这个套接字。而shutdown允许一个套接字处于不活动状态,和引用它的文件描述符数目无关。
  • 其次,游客可以很方便地关闭一个套接字传输中的一个方向。例如,如果想让所通信端的进程能够确定数据传输何时结束,可以关闭该套接字的写端然后通过该套接字的读端仍可以继续接受数据。
  1. 进程标识:网络地址:端口号
  2. 字节序:处理器架构特征,用来指示像整数这样的大数据类型内部是如何排序的。分为:大端字节序:就是高位放在低地址上;小端字节序,低位放在低地址上。TCP/IP协议栈是使用的大端字节序。
  3. 指数补偿:在客户端connect服务端的时候,如果出现错误,进程会休眠一段时间,这个时间是指数级别的增加,直到到达一个阈值,关闭连接的套接字。
  4. IO模型:同步IO,异步IO,阻塞IO,非阻塞IO(以下都使用linux下的recv作为例子) 参考知识
  • 阻塞IO模型:使用recv的默认参数一直等数据,直到拷贝到用户空间。这种IO模型是同步的。 like:水龙头没有水,A同学需要等水装满杯子才能去做其他的事情。

  • 非阻塞IO模型:使用recv,改变flags,不管有没有获取到数据都返回。这种IO模型也是同步的。 水龙头没水,B同学等一会儿再过来看。这种情况下离开了装水现场(回到了用户空间)。

  • IO复用模型:调用recv前调用select或者epoll,网络数据到达内核后,内后告诉用户进程。期间阻塞与select或者epoll,没有阻塞于recv,也可以叫做非阻塞IO,同步IO。 C同学告诉阿姨看水龙头,水龙头有水了,阿姨告诉C同学,C同学去接水。

  • 信号驱动IO模型:通过调用sigaction注册信号函数,等内核数据准备好的时候系统中断当前程序,执行信号函数。D同学让舍管阿姨等有水的时候取通知他接水(注册信号函数),D同学得知有水了,去接水(执行信号函数)。

  • 异步IO模型:调用aio_read,让内核等数据准备好,并且复制到用户进程空间后执行实现制定好的函数。E同学让舍管阿姨将杯子装满水后通知他,整个过程E同学都可以做别的事情,这是真正的异步IO。参考异步IO实现

  • 总结:在这里插入图片描述

4 C++

  1. strlen和sizeof的区别
  • strlen是库函数;sizeof是运算符,在编译时就知道大小。strlen取决于字符串的长短,sizeof取决于分配空间的大小。
    char *s;是指针,sizeof()结果是4,因为指针的大小是5
    char s[];是字符串,sizeof()结果取决于存储字符的数量,即需要分配的空间大小

5 数据结构

  1. 解决哈希表冲突
  • 开放寻址法
    • a.线性探测法:插入数据时如果冲突就往后找空闲的位置放;寻找数据时,依次往后对比,如果遍历到空闲位置还没有找到,就说明不在集合里。删除后标记一下deleted。
    • b.二次探测:二次探测的步长标为原来的平方。
    • c.双重散列:使用一组散列函数,知道找到空闲位置为止。
  • 链表法:每个桶对应一个链表,插入的时候直接查到表头,插入的实践复杂度时O(1),查找删除的时间复杂度时O(k),k表示链表的节点数。如果哈希冲突比较多的话可以利用红黑树代替链表。

6 算法

  1. 判断数组是否有三个递增元素(不要求连续)

其他链接

知乎_腾讯
github_综合
面经+学习路线

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值