当前位置: 首页 > news >正文

study notes: high performance linux server programming

1:linux网络API分为:socker地址API,socker基础API,网络信息API

  1,socker地址API:包含IP地址和端口(ip, port)。表示TCP通信的一端。

  2,socker基础API:创建/命名/监听socker,接收/发起链接,读写数据,获取地址信息,检测带外标记和读取/设置socker选项。sys/socket.h

  3,网络信息API:主机名和IP地址的转换,服务名和端口号的转换。netdb.h

2:socket和API的函数 和 相关知识。

  1,函数。

1 IP地址转换函数
  <arpa/inet.h>
  in_addr_t inet_addr( const char* strptr );  // 格式转换:十进制字符串IP -> 网络字节序(大端)IP
  int inet_aton( const char* cp, struct in_addr* inp );  // 格式转换:如上,但将数据保存到参数inp中
  char* inet_ntoa( struct in_addr in );  //  格式转换:网络字节序(大端)IP -> 十进制字符串IP
  int inet_pton( int af, const char* src, void* dst ); // 格式转换:字符串IP地址 -> 网络字节序(大端)整数IP
  const char* inet_ntop( int af, const void* src, char* dst, socklen_t cnt );  格式转换:网络字节序(大端)整数IP -> 字符串IP地址
  // inet_ntoa函数不可重入。
  // inet_ntop返回目标储存单元的地址。
2 创建socket
  <sys/types.h> <sys/socket.h>
  int socket( int domain, int type, int protocol );
  // 1 参数domain:使用的底层协议族。
  // 2 参数type:指定服务类型。SOCK_STREAM(TCP) SOCK_UGRAM(UDP)
  // 3 参数protocol:指定具体协议。一般默认为0.(前面参数已经确定了协议)
  // 4 返回:socket文件描述符。失败-1,设置errno
3 命名socket
  <sys/types.h> <sys/socket.h>
  int bind( int sockfd, const struct sockaddr* my_addr, socklen_t addrlen );
  // 1 参数addrlen:socket地址长度。
  // 2 返回:成功0,失败-1,设置errno
4 监听socket
  <sys/socket.h>
  int listen( int sockfd, int backlog );
  // 1 参数sockfd:指定被监听socket
  // 2 参数backlog:监听队列最大长度。典型值:5
5 接收链接。
  <sys/types.h> <sys/socket.h>
  int accept( int sockfd, struct sockaddr* addr, socklen_t* addrlen );
  // 1 参数sockfd:执行过listen的socket。
  // 2 参数addr:被接受链接的远程socket地址。
  // 3 参数addrlen:地址长度。
  // 4 返回:成功新的链接socket,失败-1设置errno
  // 5 accept只从监听队列取出连接,并不检测网络状态。
6 发起链接
  <sys/types.h> <sys/socket.h>
  int connect( int sockfd, const struct sockaddr *serv_addr, socklen_t addrlen );
  // 1 参数sockfd:由socket系统调用返回一个socket
  // 2 参数serv_addr:服务器监听的socket地址。
  // 3 返回:成功0,失败-1设置errno
7 关闭连接
  <unistd.h>
  int close( int fd );
  // 1 不会立即关闭,只是将引用减一。只有引用为0时才会真正关闭。
  <sys/socket.h>
  int shutdown( int sockfd, int howto );
  // 1 立即关闭连接。
  // 2 参数howto:决定函数的行为。SHUT_RD/WR/RDWR:可以分别控制读写关闭。
  // 3 返回:成功0,失败-1设errno
8 TCP数据读写。
  <sys/types.h> <sys/socket.h>
  ssize_t recv( int sockfd, void* buf, size_t len, int flags );  // 返回:实际读取到的数据长度。失败-1errno
  ssize_t send( int sockfd, const void* buf, size_t len, int flags );  // 返回:实际写入的数据长度。失败-1reeno
  // 1 参数buf,len:缓冲区的地址和大小。
  // 2 参数flags:数据收发额外控制。通常设0.
9 UDP数据读写。
  <sys/types.h> <sys/socket.h>
  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 );
  // 1 参数src/dest_addr:确定发送/接收端地址。(因UDP没有连接概念)
  // 2 这两个函数同样可以应用于TCP读写数据。
10 通用数据读写函数。
  <sys/socket.h>
  ssize_t recvmsg( int sockfd, struct msghdr* msg, int flags );
  ssize_t sendmsg( int sockfd, struct msghdr* msg, int flags );
  struct msghdr{
    void* msg_name;             // socket 地址
    socklen_t msg_namelen;    // 地址长度
    struct iovec* msg_iov;      // 分散的内存块
    int msg_iovlen;             // 分散内存块的数量
    void* msg_control;      // 辅助数据地址
    socklen_t msg_controllen;   // 辅助数据大小
    int msg_flags;              // 保存函数flag参数
  }
11 确定下一个数据是否时带外数据。
  <sys/socket.h>
  int sockatmark( int sockfd );  // 返回:带外1,否则0
12 获取地址信息。
  <sys/socket.h>
  int getsockname( int sockfd, struct sockaddr* address, socklen_t* address_len );  // 本端
  int getpeername( int sockfd, struct sockaddr* address, socklen_t* address_len );  // 远端
  // 1 返回:成功0,失败-1errno
13 获取/设置socket选项
  <sys/socket.h>
  int getsockopt( int sockfd, int level, int option_name, void* option_value, socklen_t* restrict option_len );
  int setsockopt( int sockfd, int level, int option_name, const void* option_value, socklen_t* restrict option_len );
  // 1 参数level:具体协议选项。
  // 2 返回:成功0,失败-1errno。

// 此后为网络信息API
14 获取主机完整信息。
  <netdb.h>
  struct hostent* gethostbyname( const char* name );  // 通过主机名称获取
  struct hostent* gethostbyaddr( const void* addr, size_t len, int type );  //通过IP地址获取
  // 1 参数type:IP地址的类型。
  // 2 返回:主机信息。
15 获取服务的完整信息。
  <netdb.h> 
  struct servent* getservbyname( const char* name, const char* proto );  // 通过名字获取
  struct servent* getservbyport( int port, const char* proto );  // 通过端口号获取
  // 1 参数proto:确定服务类型:UDP/TCP
16 获取主机和服务的综合函数。
  int getaddrinfo( const char* hostname, const char* service, const struct addrinfo* hints, struct addrinfo** result );
  int getnameinfo( const struct sockaddr* sockaddr, socklen_t addrlen, char* host, socklen_t hostlen, char* serv, socklen_t servlen, int flags );

 

   2,88-94在完成初版IM时,需要进行测试。

3:高级IO(高编已由函数省略)

  1,函数

1 两个文件描述符之间直接传递数据
  ssize_t sendfile( int out_fd, int in_fd, off_t* offset, size_t count );
  // 1 参数count:传输字节数
  // 2 返回:成功返回传输字节数,失败-1errno
  // 3 in_fd:必须时真实文件,out_fd:必须是socket
2 两个文件描述符之间移动数据。零copy操作
  ssize_t splice( int fd_in, loff_t* off_in, int fd_out, loff_t* off_out, size_t len, unsigned int flags );  // 具体需要再了解和使用。
3 两个管道文件描述符之间复制数据。零copy操作
  ssize_t tee( int fd_in, int fd_out, size_t len, unsigned int flags );

 

4:linux服务程序规范。

  1,函数。

1 和日志进程通信。
  <syslog.h>
  void syslog( int priority, const char* message );
  // 1 参数priority:设置值与日志级别按位与。
2 设置日志进程的输出方式。
  void openlog( const char* ident, int logopt, int facility );
  // 1 参数ident:添加到日志消息的日期和时间之后。
  // 2 参数facility:用来修改syslog函数中的默认值。
3 设置日志进程的掩码。(设置屏蔽信息)
  int setlogmask( int maskpri );
4 关闭日志功能。
  void closelog();
5 使进程称为后台进程:
  <unistd.h>
  int daemon( int nochdir, int noclose );
  // 1 参数nochdir:传0,则工作目录为根目录/。否则不变。
  // 2 参数noclose:传0,则被重定向为/dev/null。
  // 3 返回:成功0,失败-1errno。

 

  2,基本规范。

    1)一般以后台进程形式运行。后台进程又称为守护进程。

    2)通常有一套日志系统。至少能输出日志到文件。

    3)一般以某个专门的非root身份运行。

    4)通常时可配置的。

    5)启动时,会生成一个PID文件并存入/var/run目录中。

    6)通常需要考虑系统资源和限制。

  3,日志进程:rsyslogd。

    1)能够接收 用户/内核 的日志。

  4,ps命令可查看进程、进程组和会话之间的关系。

5:高性能服务器框架。

  1,函数。

  2,零散细节。

    1)客户连接请求时随即到达的异步事件,服务器需要使用某种IO模型来监听。

    2)服务器可以解构成三个主要模块:IO处理单元,逻辑单元,储存单元。

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

模块单个服务器程序服务器机群
IO处理单元处理客户连接,读写网络数据作为接入服务器,实现负载均衡
逻辑单元业务进程或线程逻辑服务器
网络储存单元本地数据库、文件或缓存。数据库服务器
请求队列各单元之间的通信方式。各服务器之间的永久TCP连接

    4)IO服用函数本身时阻塞的,它们能提高程序效率的原因:它们具有同时监听多个IO事件的能力。

    5)同步IO向应用程序通知的是IO就绪事件,异步IO向应用程序通知的是IO完成事件。

    6)服务器程序通常需要处理三类事件:IO事件,信号和定时事件。

    7)如果程序是计算密集型, 并发没有任何优势。但如果是IO密集型,并发就会有意义。因为IO速度比CPU缓慢。

    8)并发模式中,同步指程序完全按照代码序列顺序执行(用于处理客户逻辑),异步指程序执行需要由系统事件来驱动(用于处理IO)。

    9)主线程向工作线程发送scoket最简单的方式:往管道中写数据。

    10)池是一组资源的集合。这组资源在服务器启动之初就被完全创建好并初始化。常见的有:内存池,线程池,进程池和连接池。

    11)高性能服务器应该避免不必要的数据复制。(避免数据复制,共享内存是最块的方式,但不一定是最好的。

    12)并发程序必须考虑上下文切换的开销。

    13)尽量减少锁的开销。

  3,P2P模式:peer to peer。

    1)P2P模式时,主机之间很难相互发现。

  4,reactoer模式。

    1)它要求主线程(IO处理单元)只负责监听文件描述符是否有事件发生,有的话就立即将该事件通知工作线程(逻辑单元)。

    2)特点:主线程只确定有请求,工作线程针对具体事件类型针对处理。

  5,proactor模式:它将所有IO操作都交给主线程和内核来处理。工作线程仅仅负责业务逻辑。。

  6,半同步/半反应堆模式:主线程用来监听IO,并发送请求到请求队列。然后工作线程竞争抢夺处理权限。

    1)主线程和工作线程共享请求队列。需要锁来保证读写,消耗CPU

    2)每个工作线程同一时间只能处理一个客户请求。

  7,leader/follewer模式:leader监听scoket。当有新请求时,leader去处理请求,并从follower中推选新leader。

    1)需要包含几个组件:句柄集,线程集,事件处理器,具体事件处理器。

6:IO复用

  1,函数。

1 epoll系列:linux特有的IO复用函数。
  <sys/epoll.h>
  int epoll_create( int size ); // 创建一个描述符(标识事件表)
  int epoll_ctl( int epfd, int op, int fd, struct epoll_event *event ); // 对 事件表 进行操作(添加,修改,删除注册事件)。
  int epoll_wait( int epfd, struct epoll_event* events, int maxevents, int timeout ); // 等待事件。
2

 

  2,零散细节。

    1)IO复用虽然能同时监听多个文件描述符,但它本身是阻塞的。

    2)epoll系列有两种模式:LT和ET。ET是相对高效的模式。

    3)ET模式的文件描述符都应该是非阻塞的。(如果是阻塞的,可能会一直阻塞下去)

    4)EPOLLONESHOT事件:使事件只能触发一次。若需要再次触发,需要重置。可以防止重复读写。

  3,select,poll,epoll的区别

系统调用selectpollepoll
事件集合用户通过三个参数分别传入可读,可写和异常事件。内核通过这些参数在线修改就绪事件。每次调用都会重读统一处理所有事件类型。需要一个事件集。用户通过pollfd.event传入事件。内核通过修改pollfd.event反馈就绪事件。内核通过事件表直接管理所有事件。无需反复传入事件。epoll_wait仅用来反馈就绪事件(不需要多余判断)
应用程序索引就绪文件描述符的时间复杂度nn

1

最大支持文件描述符数一般有最大值限制6553565535
工作模式LTLTLT/ET
内核实现和工作效率轮询方式检测就绪事件轮询回调方式检测就绪事件。

 

7:信号。

  1,函数

  2,零散细节。

    1)服务器程序必须处理(至少能忽略)一些常见信号。以避免异常终止。

    2)信号是一种异步事件:信号处理函数和程序的主循环是两条不同的执行路线。所以信号需要尽快完成。典型的解决方案:信号仅提供信号,具体处理尽量方在主循环中。

8:时间堆:将所有定时器超时时间最小的一个定时器的超时值作为心搏间隔。

9:高性能IO框架库。

  1,常见框架:ACE,ASIO,Libevent。

  2,linux服务器程序必须处理三类事件:IO,信号和定时事件。但需要考虑三个问题

    1)统一事件源。需要统一管理三类事件。一般方法为:利用IO复用系统调用来管理所有事件。

    2)可移植性。

    3)对并发编程的支持。

  3,IO框架库以库函数的形式,封装了较为底层的系统调用,给应用程序提供了一组更便于使用的接口。

  4,Libevent框架库。

    1)跨平台支持,拥有统一事件源,线程安全,基于Reactor模式。

    2)官方网站:libevent.org

  5,P240,需要执行代码确认

10:多线程编程。

  1,线程实现方式:完全在用户空间实现,完全由内核调度和双层调度。

    1)完全在用户空间实现优点:创建和调度线程无需内核的干预,速度很快。创建多各线程对系统性能没有太大影响。但无法在多CPU情况下使用。

    2)内核调度:和用户空间完全相反。

    3)双层调度:上两种模式的混合体。

11:进程池和线程池。

  1,线程池中的线程数量应该和CPU数差不多。

  2,进程之间(有关联进程)传递数据最简单的方法是管道。

12:服务器调制,调试和测试。

  1,linux平台的一个优秀特性是内核微调:可以通过修改文件的方式来调正内核参数。

  2,调试方法:tcpdump抓包看数据,或者gdb。

  3,linux对应用程序能打开的最大文件描述符数量有两个层次的限制:用户级限制和系统级限制。

  4,gdb调试多进程的两个简单方法:

    1)找到PID,然后使用attach添加到gdb中。

    2)set follow-fork mode(parent or child)

  5,gdb调试多线程的方法。

    1)info threads:显示当前可调试的所有线程。

    2)threadID:调试ID线程

    3)set scheduler-locking(off|on|step):off所有线程执行,on只有当前线程执行,step单步调试时,只有当前线程会执行。

  6,压力测试:IO复用,多线程,多进程并发编程等方法(不懂- -)。

13:常用工具。

  1,tcpdump:经典的网络抓包工具

  2,lsof:列出当前系统打开的文件描述符的工具。

  3,nc:用来快速构建网络连接。

    1)服务器方式运行时:监听某个端口并接收客户端连接。

    2)客户端方式运行时:可以向服务器发起连接并收发数据。

    3)所以可以用来调试客户端和服务器。

  4,strace:测试服务器性能的工具。

    1)跟踪程序运行过程中执行的系统调用和接收到的信号,并将系统调用名、参数、返回值及信号名数处到标准输出或指定文件。

  5,netstat:网络信息统计工具。

    1)可以打印本地网卡接口上的全部连接、路由表信息、网卡接口信息。

  6,ifstat:interface statistics。简单的网络流量检测工具。

  7,mpstat:multi-processor statistics。能实时监测多处理器系统上的每个CPU的使用情况。

转载于:https://www.cnblogs.com/zheng39562/p/4275227.html

相关文章:

  • 阿里云修改CentOS Linux服务器的主机名
  • JavaScript-4.6鼠标事件监听,获取鼠标坐标window.event---ShinePans
  • 清华差生10年奋斗经历 读大学的意义好处 人就是你越尊重别人,别人越尊重你 优秀是一种习惯,懒惰是一种惯性。人和人的差别又是就是因为每天积累差了一点点...
  • 使用MSSQL,连接oracle,对oracle数据进行操作
  • Html5+Css3 Banner Animation 多方位移动特效
  • 内存loaddll和shellcode 是不是都差不多呢?
  • 分享:我用一天时间开发的 新年送祝福 微信手机网站(可在线体验附图)(要代码的留下邮箱)...
  • java基础篇---HTTP协议
  • 【BZOJ】【2157】旅游
  • XmlSerializer
  • 网站推广--Html关键词代码解说
  • 根域名数据库地址
  • intent 几种用法
  • JS-DOM操作应用高级(二)
  • 新建ios项目,运行时一闪即逝,并未显示出画的界面,以及分辨率自适应问题...
  • 【162天】黑马程序员27天视频学习笔记【Day02-上】
  • AWS实战 - 利用IAM对S3做访问控制
  • Java知识点总结(JavaIO-打印流)
  • Joomla 2.x, 3.x useful code cheatsheet
  • JS函数式编程 数组部分风格 ES6版
  • js数组之filter
  • JS学习笔记——闭包
  • PyCharm搭建GO开发环境(GO语言学习第1课)
  • python docx文档转html页面
  • rc-form之最单纯情况
  • SegmentFault 技术周刊 Vol.27 - Git 学习宝典:程序员走江湖必备
  • spring boot下thymeleaf全局静态变量配置
  • 反思总结然后整装待发
  • 关于List、List?、ListObject的区别
  • 赢得Docker挑战最佳实践
  • 摩拜创始人胡玮炜也彻底离开了,共享单车行业还有未来吗? ...
  • 通过调用文摘列表API获取文摘
  • ​Python 3 新特性:类型注解
  • # 数据结构
  • #define MODIFY_REG(REG, CLEARMASK, SETMASK)
  • #NOIP 2014# day.2 T2 寻找道路
  • (¥1011)-(一千零一拾一元整)输出
  • (52)只出现一次的数字III
  • (Redis使用系列) SpringBoot 中对应2.0.x版本的Redis配置 一
  • (附源码)ssm户外用品商城 毕业设计 112346
  • (十一)JAVA springboot ssm b2b2c多用户商城系统源码:服务网关Zuul高级篇
  • (四)鸿鹄云架构一服务注册中心
  • (一)appium-desktop定位元素原理
  • (转)Scala的“=”符号简介
  • (最简单,详细,直接上手)uniapp/vue中英文多语言切换
  • .helper勒索病毒的最新威胁:如何恢复您的数据?
  • .NET CORE 第一节 创建基本的 asp.net core
  • .NET core 自定义过滤器 Filter 实现webapi RestFul 统一接口数据返回格式
  • .NET 设计一套高性能的弱事件机制
  • .NET 使用 JustAssembly 比较两个不同版本程序集的 API 变化
  • .Net6 Api Swagger配置
  • /proc/stat文件详解(翻译)
  • :=
  • @RequestMapping处理请求异常
  • [ACTF2020 新生赛]Include