由于想学习一下Linux高性能服务器编程,所以选择了游双大佬的这本书,这本书在这一领域可以说是经典中的经典了,所以,拿来拜读一下,学习一些知识。
磁盘优化的技术:零拷贝,直接I/O,异步I/O。
1、DMA(Direct Memory Access)技术:简单理解就是在进行I/O设备和内存的数据传输的时候,数据搬运的工作全部交由DMA控制,CPU不在进行参与任何搬运的事情,这样CPU就可以去处理其他事情了。DMA的处理过程如下:
早期 DMA 只存在在主板上,如今由于 I/O 设备越来越多,数据传输的需求也不尽相同,所以每个 I/O 设备里面都有自己的 DMA 控制器。
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系统调用函数会直接把内核缓冲区里的数据[映射]到用户空间,这样,操作系统内核与用户空间就不需要再进行任何的数据拷贝操作。
通过使用 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 次数据拷贝。如下图:
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 缓冲区中,这样就减少了一次数据拷贝;
这就是所谓的零拷贝(Zero-copy)技术,因为我们没有在内存层面去拷贝数据,也就是说全程没有通过 CPU 来搬运数据,所有的数据都是通过 DMA 来进行传输的。
零拷贝技术的文件传输方式相比传统文件传输的方式,减少了 2 次上下文切换和数据拷贝次数,只需要 2 次上下文切换和数据拷贝次数,就可以完成文件的传输,而且 2 次的数据拷贝过程,都不需要通过 CPU,2 次都是由 DMA 来搬运。
所以,总体来看,零拷贝技术可以把文件传输的性能提高至少一倍以上。
参考1:原来 8 张图,就可以搞懂「零拷贝」了
TCP/IP协议族是一个四层协议系统,自底向上分别是:数据链路层,网络层,传输层,应用层。
OSPF(Open Shortest Path First,开放最短路径优先)是一种链路状态路由协议,无路由循环(全局拓扑),“开放”意味着非私有的。每一台路由器拥有整个拓扑结构,能根据网络拓扑信息独立地做出决策。OSPF采用SPF算法计算到达目的地的最短路径,所谓“链路”,即指路由器接口,所谓“状态”,即指描述接口以及其与邻居路由器之间的关系。管理距离AD值为110。
原文链接:https://blog.csdn.net/gongxifacai_believe/article/details/79530499
ARP 协议的全称是 Address Resolution Protocol(地址解析协议),它是一个通过用于实现从 IP 地址到 MAC 地址的映射,即询问目标 IP 对应的 MAC 地址 的一种协议。
由此,可以通过 ARP 从 IP 地址获取 MAC 地址,实现同一链路内的通信。
如果是不同链路怎么办呢?
这就要使用到 代理 ARP 了,通常 ARP 会被路由器隔离,但是采用代理 ARP (ARP Proxy) 的路由器可以将 ARP 请求转发给临近的网段。使多个网段中的节点像是在同一网段内通信。
ARP 高效运行的关键就是维护每个主机和路由器上的 ARP 缓存(或表)
ARP 攻击分类:
ARP 泛洪攻击
ARP 欺骗主机攻击
欺骗网关的攻击
中间人攻击
IP地址冲突攻击
原文链接:https://www.cnblogs.com/cxuanBlog/p/14265315.html
与 ARP 相对的,RARP(Reverse Address Resolution Protocol) 是将 ARP 反过来,从 MAC 地址定位 IP 地址的一种协议,将打印机服务器等小型嵌入式设备接入网络时会使用到。
ICMP协议的功能主要有:
ICMP(Internet Control Message Protocol)Internet控制报文协议。它是TCP/IP协议簇的一个子协议,用于在IP主机、路由器之间传递控制消息。控制消息是指网络通不通、主机是否可达、路由是否可用等网络本身的消息。这些控制消息虽然并不传输用户数据,但是对于用户数据的传递起着重要的作用.
ICMP协议使用的报文格式如图所示:
8位类型:一类是差错报文,这类报文用来回应网络错误,比如目标不可达和重定向。 一类是查询信息,ping就是使用ICMP报文来查看目标是否可达。
a. 正向代理服务器
b. 反向代理服务器
c. 透明代理服务器
Linux提供如下4个函数来完成主机字节序和网络字节序之间的转换。
“host to network long”:即将长整型的主机字节序转化为网络字节序。
#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地址。
#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.
int bind(int sockfd,const struct sockaddr* my_addr,socklen_t addrlen);//bind将my_addr所指的socket地址分配给未命名的sockfd文件描述符,addrlen则指出socket参数的长度。
int listen(int sockfd,int backlog);//sockfd指定被监听的socket,backlog参数提示内核监听队列的最大长度。
int accept(int sockfd,,const struct sockaddr* my_addr,socklen_t addrlen);// sockfd是执行过Listen系统调用的监听socket.addr用来获取被接受连接的远程socket地址,该地址长度由addrlen决定。
int connect(int sockfd,,const struct sockaddr* serv_addr,socklen_t addrlen);//sockfd由系统调用返回一个socket,serv_addr参数是服务器监听的socket地址,addrlen则代表这个地址的长度。
int close(int fd);//fd参数代表待关闭的socket.
close系统调用并非立即关闭一个连接,而是将fd的引用计数减一。只有当fd的引用计数为0时,才真正关闭连接。
若要无论如何都要关闭该连接,则使用shutdown
int shutdown(int sockfd,int howto);//socketfd参数是待关闭的socket,howto决定了socket的行为。
用于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);
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);
ssize_t recvmsg(int sockfd,struct msghdr* msg,int flags);
ssize_t sendmsg(int sockfd,struct msghdf* msg,int flags);
定义:
#include
int pipe(int fd[2]);
A.成功时,将打开的一对文件描述符值填入其参数指向的数组。若失败,则返回-1,并设置erron.
B. fd[0]只能用于从管道中读出数据,fd[1]只能用于将数据写入管道中。
C. read和write都是阻塞的。
作用:能够方便的创建双向管道
int socketpair(int domain,int type,int protocol,int fd[2]);
domain只能使用AF_UNIX
成功返回0,否则返回-1并设置erron.
作用:将标准输入重定向到一个文件中。
int dup(int file_descriptor);
int dup2(int file_descriptor_one,int file_descriptor_two);
作用:可在两个文件描述符之间传递数据(完全在内核中操作), 称为零拷贝
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.
splice函数用于在两个文件描述符之间移动数据,也是零拷贝操作。
ssize_t splice (int fd_in,loff_t* off_in,int fd_out,loff_t* off_out,size_t len,unsigned int flags);
tee函数在两个管道文件描述符之间复制数据,也是零拷贝操作。
#include
ssize_t tee(int fd_in,int fd_out,size_t len,unsigned int flags);
fcntl函数,提供了对文件描述符的各种控制操作。
- I/O处理单元 处理客户连接,读写网络数据 作为接入服务器,实现负载均衡
- 逻辑单元 业务进程或逻辑 逻辑服务器
- 网络存储单元 本地数据库、文件或缓存 数据库服务器
- 请求队列 各单元之间的通信方式 各服务器之间的永久TCP连接
Proactor模式将所有的I/O操作都交由主线程和内核来处理,工作线程仅仅负责业务逻辑。