Linux高性能服务器编程——书籍阅读笔记

目录

  • 前言
  • 正文
    • 第一章
      • 1. 零拷贝函数
      • 2. TCP/IP协议族
      • 3. OSPF
      • 4. ARP协议
      • 5. RARP
      • 6. ICMP协议
      • 7. TCP协议
      • 8. UDP协议
      • 9. 封装
    • 第四章 TCP/IP通信案例:访问Internet上的网络服务器
      • 1. Http 代理服务器
      • 3. Http请求方法
    • 第五章:Linux 网络编程基础API
      • 1. 小知识
      • 2. 字节序
      • 3. 通用Socket地址
      • 4. 点分十进制字符串和用网络字节序表示的IPV4地址之间的转换
      • 5. socket的创建
      • 6. 命名socket
      • 7. 监听socket
      • 8. 接受连接
      • 9. 发起连接
      • 10. 关闭连接
      • 11. TCP数据读写
      • 12. UDP数据读写
      • 13. 通用数据读写函数
    • 第六章:高级I/O函数
      • 1. pipe
      • 2. socketpair
      • 3. dup函数和dup2函数
      • 4. sendfile函数
      • 5. mmap函数
      • 6. splice 函数
      • 7. tee 函数
      • 8. fcntl函数
    • 第七章 Linux服务器程序规范
      • 1. 小知识
    • 第八章 高性能服务器程序框架
      • 一. 服务器模型
        • 1. C/S模型
        • 2. P2P模型
        • 3. 服务器基本模块的功能描述
      • 二、I/O模型
      • 三、两种高效的事件处理模式
        • 1. Reactor模式
        • 2. Reactor模式
  • 参考

前言

由于想学习一下Linux高性能服务器编程,所以选择了游双大佬的这本书,这本书在这一领域可以说是经典中的经典了,所以,拿来拜读一下,学习一些知识。

正文

第一章

1. 零拷贝函数

磁盘优化的技术:零拷贝,直接I/O,异步I/O。

1、DMA(Direct Memory Access)技术:简单理解就是在进行I/O设备和内存的数据传输的时候,数据搬运的工作全部交由DMA控制,CPU不在进行参与任何搬运的事情,这样CPU就可以去处理其他事情了。DMA的处理过程如下:
Linux高性能服务器编程——书籍阅读笔记_第1张图片
早期 DMA 只存在在主板上,如今由于 I/O 设备越来越多,数据传输的需求也不尽相同,所以每个 I/O 设备里面都有自己的 DMA 控制器。
2、一次系统调用必然会发生两次上下文切换:首先从用户态切换到内核态,当内核执行完任务后,再切换回用户态交由进程代码执行。
Linux高性能服务器编程——书籍阅读笔记_第2张图片
所以,我们之所以要使用零拷贝的原因就出现了,就是因为由缓存区拷贝到用户缓存区,再从用户缓存区拷贝到socket缓冲区,其实这个动作是无效的,所以就有了零拷贝函数。
3、 如何实现零拷贝?

a. mmap+write
b. sendfile
c.SG-DMA(The Scatter-Gather Direct Memory Access)技术

a. mmap() 替换 read() 系统调用函数

buf = mmap(file, len);
write(sockfd, buf, len);

mmap系统调用函数会直接把内核缓冲区里的数据[映射]到用户空间,这样,操作系统内核与用户空间就不需要再进行任何的数据拷贝操作。

Linux高性能服务器编程——书籍阅读笔记_第3张图片
通过使用 mmap() 来代替 read(), 可以减少一次数据拷贝的过程。这还不是最理想的零拷贝,因为仍然需要通过 CPU 把内核缓冲区的数据拷贝到 socket 缓冲区里,而且仍然需要 4 次上下文切换,因为系统调用还是 2 次。

b、sendfile
在 Linux 内核版本 2.1 中,提供了一个专门发送文件的系统调用函数 sendfile(),函数形式如下:

#include 
ssize_t sendfile(int out_fd, int in_fd, off_t *offset, size_t count);

它的前两个参数分别是目的端和源端的文件描述符,后面两个参数是源端的偏移量和复制数据的长度,返回值是实际复制数据的长度。
首先,它可以替代前面的 read() 和 write() 这两个系统调用,这样就可以减少一次系统调用,也就减少了 2 次上下文切换的开销。其次,该系统调用,可以直接把内核缓冲区里的数据拷贝到 socket 缓冲区里,不再拷贝到用户态,这样就只有 2 次上下文切换,和 3 次数据拷贝。如下图:
Linux高性能服务器编程——书籍阅读笔记_第4张图片
c. SG-DMA(The Scatter-Gather Direct Memory Access)技术
你可以在你的 Linux 系统通过下面这个命令,查看网卡是否支持 scatter-gather 特性:

$ ethtool -k eth0 | grep scatter-gather
scatter-gather: on
#注意上面的ethtool可能要你自己进行安装:sudo apt install ethtool
# 注意上面的eth0是你的网卡的名称,你直接用ifconfig就可以查看到了。一般是有个e开头的,那个就是了。

于是,从 Linux 内核 2.4 版本开始起,对于支持网卡支持 SG-DMA 技术的情况下, sendfile() 系统调用的过程发生了点变化,具体过程如下:
第一步,通过 DMA 将磁盘上的数据拷贝到内核缓冲区里;
第二步,缓冲区描述符和数据长度传到 socket 缓冲区,这样网卡的 SG-DMA 控制器就可以直接将内核缓存中的数据拷贝到网卡的缓冲区里,此过程不需要将数据从操作系统内核缓冲区拷贝到 socket 缓冲区中,这样就减少了一次数据拷贝;

Linux高性能服务器编程——书籍阅读笔记_第5张图片
这就是所谓的零拷贝(Zero-copy)技术,因为我们没有在内存层面去拷贝数据,也就是说全程没有通过 CPU 来搬运数据,所有的数据都是通过 DMA 来进行传输的。
零拷贝技术的文件传输方式相比传统文件传输的方式,减少了 2 次上下文切换和数据拷贝次数,只需要 2 次上下文切换和数据拷贝次数,就可以完成文件的传输,而且 2 次的数据拷贝过程,都不需要通过 CPU,2 次都是由 DMA 来搬运。

所以,总体来看,零拷贝技术可以把文件传输的性能提高至少一倍以上。

  1. PageCache
    所以,针对大文件的传输,不应该使用 PageCache,也就是说不应该使用零拷贝技术,因为可能由于 PageCache 被大文件占据,而导致「热点」小文件无法利用到 PageCache,这样在高并发的环境下,会带来严重的性能问题。
    绕开 PageCache 的 I/O 叫直接 I/O,使用 PageCache 的 I/O 则叫缓存 I/O。通常,对于磁盘,异步 I/O 只支持直接 I/O。
    于是,在高并发的场景下,针对大文件的传输的方式,应该使用「异步 I/O + 直接 I/O」来替代零拷贝技术。

参考1:原来 8 张图,就可以搞懂「零拷贝」了

2. TCP/IP协议族

TCP/IP协议族是一个四层协议系统,自底向上分别是:数据链路层,网络层,传输层,应用层。

3. OSPF

OSPF(Open Shortest Path First,开放最短路径优先)是一种链路状态路由协议,无路由循环(全局拓扑),“开放”意味着非私有的。每一台路由器拥有整个拓扑结构,能根据网络拓扑信息独立地做出决策。OSPF采用SPF算法计算到达目的地的最短路径,所谓“链路”,即指路由器接口,所谓“状态”,即指描述接口以及其与邻居路由器之间的关系。管理距离AD值为110。

原文链接:https://blog.csdn.net/gongxifacai_believe/article/details/79530499

4. ARP协议

ARP 协议的全称是 Address Resolution Protocol(地址解析协议),它是一个通过用于实现从 IP 地址到 MAC 地址的映射,即询问目标 IP 对应的 MAC 地址 的一种协议。
Linux高性能服务器编程——书籍阅读笔记_第6张图片
由此,可以通过 ARP 从 IP 地址获取 MAC 地址,实现同一链路内的通信。

如果是不同链路怎么办呢?

这就要使用到 代理 ARP 了,通常 ARP 会被路由器隔离,但是采用代理 ARP (ARP Proxy) 的路由器可以将 ARP 请求转发给临近的网段。使多个网段中的节点像是在同一网段内通信。
ARP 高效运行的关键就是维护每个主机和路由器上的 ARP 缓存(或表)

ARP 攻击分类:

ARP 泛洪攻击
ARP 欺骗主机攻击
欺骗网关的攻击
中间人攻击
IP地址冲突攻击
原文链接:https://www.cnblogs.com/cxuanBlog/p/14265315.html

5. RARP

与 ARP 相对的,RARP(Reverse Address Resolution Protocol) 是将 ARP 反过来,从 MAC 地址定位 IP 地址的一种协议,将打印机服务器等小型嵌入式设备接入网络时会使用到。

6. ICMP协议

ICMP协议的功能主要有:

  1. 确认IP包是否成功到达目标地址
  2. 通知在发送过程中IP包被丢弃的原因.

ICMP(Internet Control Message Protocol)Internet控制报文协议。它是TCP/IP协议簇的一个子协议,用于在IP主机、路由器之间传递控制消息。控制消息是指网络通不通、主机是否可达、路由是否可用等网络本身的消息。这些控制消息虽然并不传输用户数据,但是对于用户数据的传递起着重要的作用.
ICMP协议使用的报文格式如图所示:
Linux高性能服务器编程——书籍阅读笔记_第7张图片
8位类型:一类是差错报文,这类报文用来回应网络错误,比如目标不可达和重定向。 一类是查询信息,ping就是使用ICMP报文来查看目标是否可达。

7. TCP协议

  1. 为应用层提供可靠的,面向连接的和基于流的服务。
  2. 使用超时重传,数据确认等方式确保数据包被正确的发送至目的端。

8. UDP协议

  1. 无连接,基于数据报的服务。
  2. 每个UDP数据报都有一个长度,接收端必须以该长度为最小单位将其所有内容一次性读出,否则数据将被截断。

9. 封装

Linux高性能服务器编程——书籍阅读笔记_第8张图片
TCP报文段
UDP数据报
IP数据报
以太网帧,令牌环帧

Linux高性能服务器编程——书籍阅读笔记_第9张图片
P27页开始跳跃,先跳跃到后面。

第四章 TCP/IP通信案例:访问Internet上的网络服务器

1. Http 代理服务器

a. 正向代理服务器
b. 反向代理服务器
c. 透明代理服务器

3. Http请求方法

Linux高性能服务器编程——书籍阅读笔记_第10张图片

第五章:Linux 网络编程基础API

1. 小知识

  1. 大端:高存低,低存高 小端:低存低,高存高 现代PC 主要使用小端。小端字节序又称为主机字节序
  2. 为了统一字节序的传输,发送端会把数据都转换成大端字节序,所以,大端字节序也叫做网络字节序。

2. 字节序

Linux提供如下4个函数来完成主机字节序和网络字节序之间的转换。
Linux高性能服务器编程——书籍阅读笔记_第11张图片
“host to network long”:即将长整型的主机字节序转化为网络字节序。

3. 通用Socket地址

  1. 表示socket地址的是结构体socketaddr:
    Linux高性能服务器编程——书籍阅读笔记_第12张图片
    sa_family_t:是地址族类型的变量。
    Linux高性能服务器编程——书籍阅读笔记_第13张图片

4. 点分十进制字符串和用网络字节序表示的IPV4地址之间的转换

#include 
in_addr_t inet_addr(const char* strptr);//点分十进制表示的IPV4地址转网络字节序表示的IPV4地址
int inet_aton(const char* cp,struct in_addr* inp);//完成和上面同样的功能
char* inet_ntoa(struct in_addr in);//将网络字节序表示的IPV4地址转为点分十进制表示的IPV4地址。

5. socket的创建

#include 
#include 
int socket(int domain,int type,int protocol);

domain:底层协议族 设为PF_INET H或PF_INET6
type:指服务类型,SOCK_STREAM(流服务),SOCK_UGRAM(数据报服务)
protocl:通常为默认值 0

成功时返回一个socket描述符,失败时则返回-1并设置errno.

6. 命名socket

int bind(int sockfd,const struct sockaddr* my_addr,socklen_t addrlen);//bind将my_addr所指的socket地址分配给未命名的sockfd文件描述符,addrlen则指出socket参数的长度。

7. 监听socket

int listen(int sockfd,int backlog);//sockfd指定被监听的socket,backlog参数提示内核监听队列的最大长度。

8. 接受连接

int accept(int sockfd,,const struct sockaddr* my_addr,socklen_t addrlen);//	sockfd是执行过Listen系统调用的监听socket.addr用来获取被接受连接的远程socket地址,该地址长度由addrlen决定。

9. 发起连接

int connect(int sockfd,,const struct sockaddr* serv_addr,socklen_t addrlen);//sockfd由系统调用返回一个socket,serv_addr参数是服务器监听的socket地址,addrlen则代表这个地址的长度。

10. 关闭连接

int close(int fd);//fd参数代表待关闭的socket.

close系统调用并非立即关闭一个连接,而是将fd的引用计数减一。只有当fd的引用计数为0时,才真正关闭连接。

若要无论如何都要关闭该连接,则使用shutdown

int shutdown(int sockfd,int howto);//socketfd参数是待关闭的socket,howto决定了socket的行为。

Linux高性能服务器编程——书籍阅读笔记_第14张图片

11. TCP数据读写

用于TCP流数据读写的系统调用

ssize_t recv(int sockfd,void *buf,size_t len,int flags);
ssize_t send(int sockfd,const void *buf,size_t len,int flags);

12. UDP数据读写

ssize_t recvfrom(int sockfd,void *buf,size_t len,int flags,struct sockaddr* src_addr,socklen_t* addrlen);
ssize_t sendto(int sockfd,const void *buf,size_t len,int flags,const struct sockaddr* dest_addr,socklen_t* addrlen);

13. 通用数据读写函数

ssize_t recvmsg(int sockfd,struct msghdr* msg,int flags);
ssize_t sendmsg(int sockfd,struct msghdf* msg,int flags);

第六章:高级I/O函数

1. pipe

定义

#include 
int pipe(int fd[2]);

A.成功时,将打开的一对文件描述符值填入其参数指向的数组。若失败,则返回-1,并设置erron.
B. fd[0]只能用于从管道中读出数据,fd[1]只能用于将数据写入管道中。
C. read和write都是阻塞的。

2. socketpair

作用:能够方便的创建双向管道

int socketpair(int domain,int type,int protocol,int fd[2]);

domain只能使用AF_UNIX
成功返回0,否则返回-1并设置erron.

3. dup函数和dup2函数

作用:将标准输入重定向到一个文件中。

int dup(int file_descriptor);
int dup2(int file_descriptor_one,int file_descriptor_two);

4. sendfile函数

作用:可在两个文件描述符之间传递数据(完全在内核中操作), 称为零拷贝

5. mmap函数

mmap函数用于申请一段内存空间,可将这段内存空间当做进行间通信的共享空间。
munmap函数则释放由mmap创建的这段内存空间。

#include 
void *mmap(void *start,size_t length,int prot,int flags,int fd,off_t offset);
int munmap(void *start,size_t length);

在这里插入图片描述
的访问权限。

mmap函数成功时,返回目标内存区域的指针,失败时则返回MAP_FAILED并设置erron.

6. splice 函数

splice函数用于在两个文件描述符之间移动数据,也是零拷贝操作。

ssize_t splice (int fd_in,loff_t* off_in,int fd_out,loff_t* off_out,size_t len,unsigned int flags);

7. tee 函数

tee函数在两个管道文件描述符之间复制数据,也是零拷贝操作。

#include 
ssize_t tee(int fd_in,int fd_out,size_t len,unsigned int flags);

8. fcntl函数

fcntl函数,提供了对文件描述符的各种控制操作。

第七章 Linux服务器程序规范

1. 小知识

  1. 守护进程的父进程通常是init进程。
  2. Linux提供了一个守护进程来处理系统日志——syslogd.

第八章 高性能服务器程序框架

一. 服务器模型

1. C/S模型

Linux高性能服务器编程——书籍阅读笔记_第15张图片

2. P2P模型

云计算机群可以看做是P2P模型的一个典范。
Linux高性能服务器编程——书籍阅读笔记_第16张图片

3. 服务器基本模块的功能描述

  1. I/O处理单元 处理客户连接,读写网络数据 作为接入服务器,实现负载均衡
  2. 逻辑单元 业务进程或逻辑 逻辑服务器
  3. 网络存储单元 本地数据库、文件或缓存 数据库服务器
  4. 请求队列 各单元之间的通信方式 各服务器之间的永久TCP连接

二、I/O模型

  1. 阻塞的文件描述符被称为阻塞I/O。非阻塞的文件描述符被称为非阻塞I/O.
  2. socket的基础API中,可能被阻塞的系统调用包括accept,send,recv,connect.
  3. 理论上来说:阻塞I/O,I/O复用和信号驱动I/O都是同步I/O模型。
  4. 异步I/O的读写操作 总是立即返回,而不论I/O是否堵塞,因为真正的读写操作已经交由内核操作了。
  5. 同步I/O通知的是I/O就绪事件,异步I/O通知的是I/O完成事件。
    Linux高性能服务器编程——书籍阅读笔记_第17张图片

三、两种高效的事件处理模式

  1. 通常I/O需要处理三类事件:I/O事件,信号及定时事件。
  2. Reactor和Proactor.两种事件处理模式。同步I/O模型通常用于处理Proactor模式,异步I/O模型通常用于处理Reactor模式。

1. Reactor模式

介绍
在这里插入图片描述
模式
Linux高性能服务器编程——书籍阅读笔记_第18张图片

2. Reactor模式

Proactor模式将所有的I/O操作都交由主线程和内核来处理,工作线程仅仅负责业务逻辑。

参考

  1. Linux高性能服务器编程-游双

你可能感兴趣的:(书籍,Linux高性能服务器编程)