1,数据链路层:ARP,RARP,使用物理地址寻址一台机器,MAC,驱动
网络层:ICMP,IP,IP协议使用逐跳方式确定通信路径
传输层:TCP,UDP,使用端到端通信,只关心通信的起始端和目的端,不在乎包的中转过程
应用层:ping,telent,OSPF,DNS

2,通过封装,上层协议使用下层协议提供的服务,每层协议在上层数据的基础上,加上自己的头部信息

3,帧,经过数据链路层封装的数据成为帧,帧是最终在物理网络上传送的字节序列

4,socket定义的这一组API提供两点功能:一是将应用程序数据从用户缓冲区中复制到TCP/UDP内核发送缓冲区,以交付内核来发送数据,如send,或者是从内核TCP/UDP接受缓冲区中复制数据到用户缓冲区,以读取数据。二是应用程序可以通过它们来修改内核中各层协议的头部信息或其他数据结构,从而精细地控制底层通信行为,如setsockopt设置IP数据报在网络上的存活时间。

5,用户空间:用户空间是常规进程所在区域,是非特权区域,比如该区域的代码不能直接访问硬件设备。
内核空间:内核空间是操作系统所在区域,有特别的权利:能与设备控制器通讯,控制用户区域进程的运行状态等等。
网卡中的缓冲区既不属于内核空间,也不属于用户空间。它属于硬件缓冲,允许网卡与操作系统之间有个缓冲;
内核缓冲区在内核空间,在内存中,用于内核程序,做为读自或写往硬件的数据缓冲区;
用户缓冲区在用户空间,在内存中,用于用户程序,做为读自或写往硬件的数据缓冲区;
为了加快数据的交互,可以将内核缓冲区映射到用户空间,这样,内核程序和用户程序就可以同时访问这一区间了。

6,IP协议为上层协议提供无状态,无连接,不可靠的服务
无状态:IP通信双方不同步传输数据的状态信息,IP数据报没有上下文关系
无连接:IP通信双方都不长久地维持对方的任何信息
不可靠:尽最大努力交付

7,tcpdump抓取数据报

8,ICMP重定向报文也能用于更新路由表

9,TCP相对于UDP,面向连接、字节流和可靠传输。双方先连接然后才读写,都必须为该连接分配必要的内核资源。TCP是全双工的,双方的读写可以通过一个连接进行。完成数据交换要断开连接以释放系统资源,有超时重传

10,基于流的数据没有边界限制,发送端执行的写操作次数和接收端执行的读操作次数之间没有任何数量关系。基于数据报的服务接收端必须以该长度为最小单位将其所有内容一次性读出,否则数据将被截断。

11,TCP头部的最后一个选项字段是可变长的可选信息,这部分最多包含40字节,因为TCP头部最长是60字节,其中还包括20字节的固定部分

12,半关闭状态,TCP连接是全双工的,所以它允许两个方向的数据传输被独立关闭,例,只接受不发送。服务器和客户端应用程序判断对方是否已经关闭连接的方法是:read系统调用返回0(收到结束报文段)

13,超时重传,1,2,4,8,16….五次重连均失败,放弃连接并通知应用程序

14,服务器状态转移过程:服务器通过listen系统调用进入listen状态,被动等待客户端连接。服务器一旦监听到某个连接请求(收到同步报文段),就将该连接放入内核等待队列,并向客户端发送带SYN标志的确认报文段,此时该连接处于SYN_RCVD状态,如果服务器成功地接收到客户端发送回的确认报文段,则该连接转移到ESTABLISHED状态。ESTABLISHED是双方能够进行双向数据传输的状态。
当客户端主动关闭连接时(通过close或shutdown系统调用向服务器发送结束报文段),服务器通过返回确认报文段使连接进入CLOSE_WAIT状态,等待服务器应用程序关闭连接。通常服务器检测到客户端关闭连接后,也会立即给客户端发送一个结束报文段来关闭连接,这将使连接转移到LAST_ACK状态,以等待客户端对结束报文段的最后一次确认,一旦确认完成,连接就彻底关闭了。

15,客户端状态转移过程:客户端通过connect系统调用主动与服务器建立连接,然后给服务器发送一个同步报文段,使连接转移到SYN_SENT状态(如果connect连接的目标端口不存在或被处于TIME_WAIT状态的连接所占用或在超时时间内未收到服务器的确认报文段,则connect调用失败)。如果connect调用失败则返回初始的CLOSED状态,如果成功收到服务器的同步报文段和确认,则connect调用成功返回,连接转移至ESTABLISHED状态。
当客户端主动关闭时,它将向服务器发送一个结束报文段,同时连接进入FIN_WAIT_1状态。若收到确认报文段,连接转移至FIN_WAIT状态。如果服务器也关闭连接,则客户端给予确认并进入TIME_WAIT状态。

16,客户端连接在收到服务器结束报文段之后,没有直接进入CLOSED状态,而是转移到了TIME_WAIT状态,客户端要等待一段长为2MSL(报文段最大生存时间)的时间,才能完全关闭。

17,TIME_WAIT,可以可靠地终止TCP连接(重发的发完),保证让迟来的TCP报文段有足够是时间被识别并丢弃

18,socket选项SO_LINGER发送复位报文段,可以异常终止一个连接

19,使用交互数据对实时性要求高,如telnet,ssh;成块数据对传输效率要求高,如ftp

20,当传输大量大块数据的时候,发送方会连续发送多个TCP报文段,接收方也可以一次确认所有这些报文段

21,拥塞控制:慢启动(每次增加拥塞窗口,试探地指数增加,有一个门限,一般设为拥塞的一半),拥塞避免(拥塞窗口超过门限进入拥塞避免阶段,开始线性增加),快速重传(立刻重传),快速恢复(发送端收到连续三个重读的确认报文段,就认为拥塞发生了,然后启动快重传或者快恢复。快恢复是从拥塞窗口的一半恢复,并进行拥塞避免)

22,POST,客户端向服务器提交数据,这种方法影响服务器,服务器可能会根据收到的数据动态创建新的资源,也可能更新原有的资源。
PUT,上传资源,也会影响服务器

23,HTTP状态码:1xx信息;2xx成功;3xx重定向;4xx客户端错误;5xx服务器错误

24,小端字节序,主机字节序;大端字节序(高位字节后八位在低地址),网络字节序;

25,socket网络编程接口中表示socket地址的是结构体sockaddr
#include
struct sockaddr{
sa_family_t(地址族类型) sa_family;
char sa_data14;
}
image_1c91i7or6d9arip1jmt1ddv12nr9.png-85.6kB

image_1c91i8eth174g61v1k14154h1dcgm.png-109.9kB

所有专用socket地址类型的变量在实际使用时都需要转化为通用socket地址类型sockaddr(强制转换即可)
image_1c91ikb8j1ns8vb21qcr1p8ng4k13.png-70.2kB

26,网络上的两个程序通过一个双向的通信连接实现数据的交换,这个连接的一端称为一个socket。Socket又称”套接字”,应用程序通常通过”套接字”向网络发出请求或者应答网络请求。Socket是进程通讯的一种方式,即调用这个网络库的一些API函数实现分布在不同主机的相关进程之间的数据交换
函数原型(创建socket):socket在创建的时候默认是阻塞的(阻塞就是死等)
#include
#include
int socket(int domain, int type, int protocol);
domain:协议域,又称协议族(family)
type:指定Socket类型
protocol:指定协议

27,将一个socket与socket地址绑定称为给socket命名,服务器程序需要命名,这样客户端才知道如何连接它,客户端不需要命名,使用操作系统自动分配的socket
命名socket的系统调用 bind
int bind(int sockfd, const struct sockaddr* my_addr, socklen_t addrlen)
bind将my_addr所指的socket地址分配给未命名的sockfd文件描述符,addrlen参数指出该socket地址的长度

28,监听socket listen
int listen(int sockfd,int backlog);
sockfd参数指定被鉴定的socket,backlog表示监听队列的最大长度

29,接受连接
int accept(int sockfd,struct sockaddr addr,socklen_t addrlen)
sockfd是listen监听的socket,addr获取被接受连接的远端socket地址,addrlen表示socket地址的长度

30,发起连接
int connect(int sockfd,const struct sockaddr* serv_addr,socklen_t addrlen)
sockfd由socket系统调用返回一个socket,serv_addr是服务器监听的socket地址,addrlen是地址的长度

31,关闭连接
int close(int fd)
fd是待关闭的socket

32,TCP数据读写中:recv读取sockfd上的数据,成功返回读取到的数据的长度,它可能小于我们期望的长度,所以要多调用几次才能读取到完整的数据。recv返回0就意味着通信对方已经关闭了连接,出错返回-1
send往sockfd上写入数据,成功返回写入的数据的长度,失败返回-1并设置errno

33,UDP数据读写中:recvfrom读取sockfd上的数据,因为UDP没有连接,所以我们每次读取都需要获取发送端的socket地址。sendto往sockfd上写入数据。recvfrom/sendto也可以用于面向连接的数据读写,只需要把最后参数设为NULL,忽略发送端和接收端的socket地址

34,recvmsg/sendmsg通用的数据读写系统调用,不仅能用于TCP数据流还能用于UDP数据报

35,socket地址两要素,IP地址和端口号

36,pipe函数可用于创建一个管道,以实现进程间通信
int pipe(int fd2)
两个文件描述符fd[0]和fd1分别构成管道的两端,往fd1写入的数据可以从fd[0]读出,并且0只能用于读出,1只能用于写入

37,readv函数和writev函数将数据从文件描述符读到分散的内存块中,分散读;
writev函数将多块分散的内存一并写入到文件描述符中,集中写。

38,sendfile在两个文件描述符之间直接传递数据(完全在内核中操作),从而避免了内核缓冲区和用户缓冲区之间的数据拷贝,效率很高,被称为零拷贝
splice在两个文件描述符之间移动数据,也是零拷贝操作
tee在两个管到文件描述符之间复制数据,也是零拷贝操作

39,mmap函数用于申请一段内存空间,成功返回指向目标内存区域的指针,munmap则释放有mmap创建的这段内存空间,成功返回0,失败返回-1并设置errno

40,真实用户ID(UID),有效用户ID(EUID),真实组ID(GID),有效组ID(EDID)

41,https://www.cnblogs.com/JohnABC/p/4079669.html进程组方便管理,辈分最高的那个的ID作为组号
进程必定属于一个进程组,也只能属于一个进程组
每个进程除了有一个进程ID之外,还属于一个进程组,那什么是进程组呢?
  顾名思义,进程组就是一个或多个进程的集合。这些进程并不是孤立的,他们彼此之间或者存在父子、兄弟关系,或者在功能上有相近的联系。每个进程都有父进程,而所有的进程以init进程为根,形成一个树状结构
  
42,一些有关联的进程组将会组成一个会话(session)
由于Linux是多用户多任务的分时系统,所以必须要支持多个用户同时使用一个操作系统。当一个用户登录一次系统就形成一次会话 。一个会话可包含多个进程组,但只能有一个前台进程组。每个会话都有一个会话首领(leader),即创建会话的进程
image_1c91r981fil5piengc4tr1dhp1g.png-77kB

43,执行ps命令可查看进程。进程组和会话之间的关系,PID进程的身份标识符

44,getrlimit读取和设置linux的系统资源限制

45,服务器三个模块:I/O处理模块,逻辑单元,存储单元

46,CS服务器模型
image_1c91sobtiscgj5recop3b1fhv1t.png-423.9kB
image_1c91sp81jchq17vmkvj1c3brbv2a.png-223.9kB

47,P2P服务器模型。点对点,每台机器在消耗服务的同时也给别人提供服务。先在用发现服务器提供查找
image_1c91titlr19mo27g19861lbns472n.png-63kB

48,服务器编程框架
image_1c91tvh7k4141itpqir4sj1vmq34.png-222.8kB
image_1c91ups4610r1kk015p59kojn53h.png-463.7kB

49,可能被阻塞的系统调用包括accept,send,recv和connect,阻塞的话errno通常被设为期望阻塞云云

50,同步I/O向应用程序通知的是I/O就绪事件,要求用户代码自行执行I/O操作,异步I/O通知的是I/O完成事件,由内核来执行I/O操作。
image_1c931e3s01ivr1gdh1m1b81htk63u.png-104.4kB

51,服务器程序通常需要处理三类事件:I/O事件、信号及定时时间。

52,同步I/O模型常用于实现Reactor模式请求队列
Reactor要求主线程(I/O处理单元)只负责监听文件描述上是否有事件发生,有的话就立即将该事件通知工作线程(逻辑单元),除此之外,主线程不做任何工作,其他如读写数据,接受新连接和处理客户请求都在工作线程中完成
image_1c9331cf2cb6j7pdklhpj167e4b.png-321.6kB
异步I/O模型则用于实现Proactor模式
Proactor将所有I/O操作都交给主线程和内核来处理,工作线程只负责业务逻辑
image_1c9331qpo1bbrtfm8rerdnvmf4o.png-430.7kB

53,在I/O模型中,同步和异步区别的是内核向应用程序通知的是何种I/O事件,是就绪时间还是完成事件,以及该由谁来完成I/O读写,是应用程序还是内核
在并发模式中,同步指的是程序完全按照代码序列的顺序执行,异步指的是程序的执行需要由系统事件来驱动,常见的系统事件包括中断、信号等。

54,两种并发编程模式:半同步/半异步模式领导者/追随者模式
半同步/半异步模式:按照同步方式运行的线程称为同步线程,按照异步方式运行的线程称为异步线程。显然异步线程的执行效率高,实时性强,是很多嵌入式程序采用的模型。但异步方式难于调试和扩展,不适合于大量的并发,而同步线程则相反,它虽然效率和实时性较差,但是逻辑简单。两者结合就是半同步/半异步模式。其中,同步线程用于处理客户逻辑,异步线程用于处理I/O事件
领导者/追随者模式:多个工作线程轮流获得事件源集合,轮流监听、分发并处理事件的一种模式,在任意时间点,程序都仅有一个领导者线程,它负责监听I/O事件,而其他线程都是追随者,他们休眠在线程池中等待成为新的领导者。当前的领导者检测到I/O事件,首先从线程池中推选出新的领导者线程,然后处理I/O事件。此时新的领导者等待新的I/O,二者实现了并发。

55,逻辑单元内部的一种高效编程方法:有限状态机

56,提高服务器性能:池,数据复制,上下文切换和锁
是一组资源的集合,这组资源在服务器启动之前就被完全创建好并初始化,这称为静态资源分配。直接从池中获取资源比动态分配快的多
数据复制,高性能服务器应该避免不必要的数据复制,尤其是当数据复制发生在用户代码和内核之间的时候(从内核缓冲区复制到应用程序缓冲区)
上下文切换和锁,上下文切换问题,即进程切换或线程切换导致的系统开销。即使是I/O密集型的服务器也不应该使用过多的工作线程或工作进程,否则进程间线程间的切换将占用大量CPU时间。
并发程序需要考虑的另外一个问题是共享资源的加锁保护。锁通常被认为是导致服务器效率低下的一个因素,因为它引入的代码不仅不处理任何业务逻辑,而且需要访问内核资源。如果必须使用,可以考虑减小锁的力度,比如使用读写锁

57,I/O复用使得程序能够同时监听多个文件描述符,这对提高程序的性能至关重要
linux下实现I/O复用的系统调用主要有select、poll和epoll

58,select系统调用的用途是:在一段指定时间内,监听用户感兴趣的文件描述符上的可读、可写和异常等事件。
image_1c93fccdu19a6mbdq2dfgucm9.png-172.3kB

59,poll系统调用和select类似,也是在指定时间内轮询一定数量的文件描述符,以测试其中是否有就绪者

60,epoll
image_1c93gieb51kddsuvgve1aki13up13.png-217.5kB
epoll系列系统调用的主要接口是epoll_wait函数,它在一段超时时间内等待一组文件描述符上的事件

61,三组I/O复用函数的比较
image_1c93h89m7o4b121g1ctv7e1gue20.png-145.7kB
image_1c93hmhk110qa1c5o1acr1ed5lvt3d.png-280kB

62,工作模式,LT(电平触发,默认)ET(边沿触发)

63,可以同时处理TCP请求和UDP请求,回射服务器

64,xinetd超级服务,因特网服务,同时管理多个自服务,即监听多个端口

65,信号是由用户、系统或者进程发送给目标进程的信息,以通知目标进程某个状态的改变或系统异常

66,kill函数,一个进程给其他进程发送信号的API
image_1c93jaadnppf1255dtm81q1h1v3q.png-96.3kB

67,信号函数:信号处理函数signal,成功时返回一个函数指针

68,网络编程相关信号
SIGHUP,当挂起进程的控制终端时,SIGHUP信号将被触发。被用来强制服务器重读配置文件
SIGPIPE,往一个读关闭的管道或socket连接中写数据将引发SIGPIPE信号,收到该信号的默认行为是结束进程
SIGURG,通知应用程序带外数据到达

69,多传输层有带外数据的概念,它有时也称为经加速数据。其想法是一个连接的某端发生了重要的事情,而且该端希望迅速通告其对端。这里“迅速”意味着这种通知应该在已排队等待发送的任何“普通”(有时称为“带内”)数据之前发送。也就是说,带外数据被认为具有比普通数据更高的优先级。带外数据并不需要在客户和服务器之间再使用一个连接,而是被映射到已有的连接中。
不幸的是,一旦超越普通概念光临现实世界,我们发现几乎每个传输层都各自有不同的带外数据实现。而UDP作为一个极端的例子,没有实现带外数据

70,linux三种定时方法
image_1c93l7378asf1vld3lv1lh2168n47.png-34.8kB

71,image_1c93lh3fbeh1utjnunfd116sb4k.png-336kB

72,定时器通常至少要包含两个成员:一个超时时间(相对时间或者绝对时间)和一个任务回调函数。
image_1c93m97ig1e621j4eci61tiq1ind5h.png-144.8kB

73,高性能定时器,时间轮和时间堆
基于排序链表的定时器存在一个问题:添加定时器的效率偏低。下面我们要讨论的时间轮解决了这个问题
时间轮
image_1c93ojs281veo18qub1q2eu1vce5u.png-157.8kB
可见,对时间轮而言,添加一个定时器的时间复杂度是O(1),删除一个定时器的时间复杂度也是O(1),执行一个定时器的时间复杂度是O(n)。但实际上执行一个定时器任务的效率要比O(n)好得多,因为时间轮将所有的定时器散列到了不同的链表上。时间轮的槽越多,等价于散列表的入口(entry)越多,从而每条链表上的定时器数量越少。当使用多个轮子来实现时间轮时,执行一个定时器任务的时间复杂度将接近O(1)
时间堆最小堆,小顶堆
前面讨论的定时方案都是以固定的频率调用心搏函数tick,并在其中依次检测到期的定时器,然后执行到期定时器上的回调函数。设计定时器的另外一种思路是:将所有定时器中超时时间最小的一个定时器的超时值作为心搏间隔。这样,一旦心搏函数tick被调用,超时时间最小的定时器必然到期,我们就可以在tick函数中处理该定时器。然后,再次从剩余的定时器中找出超时时间最小的一个,并将这段最小时间设置为下一次心搏间隔。如此反复,就实现了较为精确的定时。
实际上,我们只需要对数组中的第[(N-1)/2]~0个元素执行下虑操作,即可确保该数组构成一个最小堆。这是因为对包含N个元素的完全二叉树而言,它具有[(N-1)/2]个非叶子节点,这些非叶子节点正是该完全二叉树的第0~[(N-1)/2]个节点。我们只要确保这些非叶子节点构成的子树都具有堆序性质,整个树就具有堆序性质
对时间堆而言,添加一个定时器的时间复杂度是O(lgn),删除一个定时器的时间复杂度是O(1),执行一个定时器的时间复杂度是O(1)。因此,时间堆的效率是很高的

74,Linux服务器程序必须处理的三类事件:I/O事件、信号和定时事件。在处理这三类事件时我们通常需要考虑如下三个问题:统一事件源,可移植性,对并发编程的支持

75,高性能I/O框架库Libevent

76,多进程编程,包括:
复制进程映像的fork系统调用和替换进程映像的exec系列系统调用
僵尸进程以及如何避免僵尸进程
进程间通信(IPC)最简单的方式:管道
3种System V进程间通信方式:信号量、消息队列和共享内存
在进程间传递文件描述符的通用方法:通过UNIX本地域socket传递特殊的辅助数据(msg_control和msg_controllen成员用于辅助数据的传送)

77,fork系统调用
image_1c93sdnes1mcg8or1crl8vd1dd36o.png-121.4kB

78,exec系列系统调用
image_1c93sl5jo1gio1pta10ngqaa17ei75.png-115.1kB

79,处理僵尸进程
父进程不等待子进程结束,子进程将成为僵尸进程
对于多进程程序而言,父进程一般需要跟踪子进程的退出状态。因此,当子进程结束运行时,内核不会立即释放该进程的进程表表项,以满足父进程后续对该子进程退出信息的查询(如果父进程还在运行)。在子进程结束运行之后,父进程读取其退出状态之前,我们称该子进程处于僵尸态。另外一种使子进程进入僵尸态的情况是:父进程结束或者异常终止,而子进程继续运行。此时子进程的PPID将被操作系统设置为1,即init进程。init进程接管了该子进程,并等待它结束。在父进程退出之后,子进程退出之前,该子进程处于僵尸态
由此可见,无论哪种情况,如果父进程没有正确地处理子进程的返回信息,子进程都将停留在僵尸态,并占据着内核资源。这是绝对不能容许的,毕竟内核资源有限

80,要在事件已经发生的情况下执行非阻塞调用才能提高程序的效率

81,管道pipe,父进程和子进程间通信的常用手段。管道能在父子进程间传递数据,利用的是fork调用之后两个管道文件扫描符fd[0]和fd1都保持打开。一对这样的文件描述符只能保证父、子进程间一个方向的数据传输,父进程和子进程必须有一个关闭fd[0],另一个关闭fd1

82,信号量PV原语,使用二进制信号量同步两个进程
image_1c94c0af56irh6qvqppst15jv7i.png-140.8kB
semget系统调用创建一个新的信号量集
semop系统调用改变信号量的值,即执行P、V操作
semctl系统调用允许调用者对信号量进行直接控制
semget的调用者可以给其key参数传递一个特殊的键值IPC_PRIVATE(其值为0),这样无论该信号量是否已经存在,semget都将创建一个新的信号量。使用该键值创建的信号量并非像它的名字声称的那样是进程私有的。其他进程,尤其是子进程,也有方法来访问这个信号量

83,共享内存是最高效的进程间通信机制,包括4个系统调用:shmget、shmat、shmdt和shmctl。
共享内存是最高效的IPC机制,因为它不涉及进程之间的任何数据传输。这种高效率带来的问题是,我们必须用其他辅助手段来同步进程对共享内存的访问,否则会产生竞态条件。因此,共享内存通常和其他进程间通信方式一起使用。

84,线程同步的三种方法:互斥锁,条件变量和信号量