Linux高性能服务器编程(笔记3)

        pipe(int fd[2)其中fd[1]是写端,fd[0]是读端,两端都是阻塞式的,即read读空管道会阻塞write写满管道会阻塞,当写端文件描述符fd[1]计数减为0时read返回0,当读端fd[0]计数减为0时write失败引发SIGPIPE信号。管道内部数据是字节流类似与TCP但是TCP数据窗口大小取决于对方和网络,而pipe本身容量有限制默认65536B可以使用fcntl修改管道容量
        双向管道:socketpair(int domain,int type,int protocol,int fd[2])//domain=AF_UNIX只能本地使用,两端都可读可写
dup(int fd)//dup2(int fd1,int fd2)将复制文件描述符fd1指向的文件给新的描述符,dup2返回一个不小于fd2的描述符,失败返回-1,并不继承原文件描述符的属性如close-on-exec和non-blocking
        CGI:将标准输出的文件描述符赋值给socket这样服务端的标准输出将会直接发送到与客户端连接对应的socket,先关闭STDOUT_FILENO然后立即dup(返回当前最小的可用文件描述符)
        strerror(errno)//输出errno
        readv(int fd,const struct ivoec* vector,int count)//writev()//readv将从fd读到的数据分散写到vector指定的内存中(分散读),writev将vector指定的分散内存写到fd中(集中写)
        INADDR_ANY,这个宏表示本地的任意IP地址,因为服务器可能有多个网卡,每个网卡也可能绑定多个IP地址,这样设置可以在所有的IP地址上监听,直到与某个客户端建立了连接时才确定下来到底用哪个IP地址,该宏可以用初始化socket地址结构的IP地址
        sendfile(int out_fd,int in_fd,off_t* offset,size_t count)//将in_fd内的数据直接拷贝到out_fd中(完全在内核中操作避免了用户空间的切换称为零拷贝),count为拷贝的字节数,offset是in_fd读入流的起始位置为NULL则位流默认起始位置,成功返回传输的字节数。in_fd必须是真实的文件描述符不能是socket或管道,out_fd必须是个socket,此函数专门为网络上传输文件设计的
        mmap(void *start,size_t length,int prot,int flags,int fd,off_t offset)//将指定的文件fd内容映射到一块内存中,start为指定内存的起始地址为NULL时系统自动分配地址,length是内存段长度,prot设置该段内存的访问权限,flags控制内存内容被修改后程序的行为(是否要同步修改映射文件等),fd是被映射的文件描述符,offset设置从文件的何处开始映射,成功则返回目标内存指针否则返回-1
        munmap(void* start,size_t length)释放mmap创建的内存
        splice(int fd_in,loff_t* off_in,int fd_out,loff_t* off_out,size_t len,unsigned int flags)//用于在两个文件描述符间移动数据零拷贝,若fd_in是管道则off_in必须是NULL,若fd_in不是管道描述符但是off_in为NULL则输入数据流从当前位置读入,若off_in不是NULL则指定具体的流偏移位置,flags控制数据如何流动,其中fd_in和fd_out中至少有一个是管道描述符,成功返回移动的字节数
        tee(int fd_in,int fd_out,size_t len,unsigned int flags)//在两个管道描述符间复制数据零拷贝,源文件描述符上的数据仍然可以用于后续的读操作
        fcntl(int fd,int cmd)//控制文件描述符属性和行为
        服务器编程框架:I/O处理单元->请求队列->逻辑单元->请求队列->网络存储单元;I/O处理单元是服务器管理客户连接的模块,等待客户连接,接收客户数据,将服务器响应数据返回给客户端但是数据的收发不一定在这里进行可能在逻辑单元中,I/O处理单元充当接入服务器;逻辑单元通常是一个zhenai进程或线程,分析处理用户数据,将结果送给I/O处理单元或客户端,充当逻辑服务器;请求队列是各单元之间通信方式的抽象;网络存储单元可以是数据库、缓存、文件
阻塞IO是指执行的系统调用可能因为无法立即完成而被操作系统挂起直到等待的事件发生为止,socket中阻塞的有accept、send、recv、connect。非阻塞IO执行系统调用总是立即返回而不管事件是否发生。根据errno区分事件没有发生和出错的情况,非阻塞IO通常要和其它IO机制一起使用如IO复用和SIGIO信号提高程序效率
        IO复用:应用程序通过IO复用函数向内核注册一组事件内核通过IO复用函数把其中就绪的事件通知应用程序,常用的复用函数select、poll、epoll_wait,IO复用函数本身是阻塞的但是它能同时监听多个IO事件
        SIGIO信号:SIGIO和SIGURG信号必须与某个文件描述符关联,当被管理的文件描述符可读或可写时系统触发SIGIO信号,当socket上有带外数据时系统触发SIGURG信号,将信号和文件描述符关联的方法是使用fcntl函数为目标文件描述符指定宿主进程或进程组那么指定的宿主进程或进程组将捕获这两个信号。将文件描述符指定宿主进程当文件描述符上有事件发生时SIGIO信号的处理函数将被触发也就可以在该信号处理函数中对文件描述符执行非阻塞IO操作
阻塞IO、IO复用、信号驱动IO都是同步模型:IO读写操作都是在IO事件发生之后由应用程序完成,要去用户代码自己执行IO操作;异步IO用户告诉内黑读写缓冲区的位置然后立即返回而数据的实际读写是由内核完成的。同步IO向应用程序通知的是IO就绪事件,异步IO向应用程序通知的是IO完成事件
        Reactor模式:主线程往epoll内核注册事件并调用epoll_wait等待事件发生,当事件发生时epoll_wait通知主线程,主线程将事件放入请求队列,此时工作线程被唤醒。注意主线程只负责监听文件描述符上是否有事件发生。
        Proactor模式: 主线程调用aio_read/aio_write向内核注册事件并告诉读/写缓冲区位置,主线程处理其它逻辑,当缓冲区读/写好后,内核向应用程序发送一个信号,应用程序将预先定义好的信号处理函数。
        可以利用同步IO模型模拟proactor,将数据封装好后再通知阻塞的工作线程
        IO的同步异步指内核向应用程序通知的何种IO事件以及该由谁来IO读写,但在并发模式中同步是指程序完全按照指定代码顺序执行,异步指程序执行按照事件驱动完成。
        高效的并发模式:半同步/半异步模式:同步线程处理客户逻辑,异步线程处理IO事件;半同步/半反应堆模式:异步线程只有一个是主线程只负责监听socket上的事件发生时将连接插入请求队列然后睡眠在请求队列上的工作线程通过竞态获得任务的接管权(reactor模式工作线程自己和客户端交互);高效的半同步/半异步模式:主线程只管监听socket接受新连接后将连接派发给工作线程该连接的所有后续工作交给该工作线程,主线程和工作线程间同步管道传递连接,都维持自己的事件循环即各自独立监听不同的事件,因此每个线程都是异步模式;领导者/追随者模式:每个工作线程都有机会监听分发事件,在任意一个时间点程序仅有一个领导者线程负责监听IO事件,其余线程为追随者,当领导者检测到IO事件从线程池选出新的领导者然后去处理IO事件
        select(int nfds,...,timeout)在指定一段时间timeout内监听用户感兴趣的文件描述符上的可读、可写、异常事件若timeout为NULL则无限期等待,宏         FD_SET(int fd,fd_set* fdset)设置fdset位fd,FD_ISSET(int fd,fd_set* fdset)测试位fd是否被设置
        poll(struct pollfd* fds,nfds,timeout)和色了传统差不多在timeout内等待nfds个pollfd结构体的事件(fds指向一个结构体数组)
        epoll把用户关心的文件描述符的事件放在内核的一个事件表中,无须要select和poll那样每次调用都要重复传入文件或事件集,需要一个额外的文件描述符来唯一标识事件表epoll_create(size)提示一个size大小的事件表,epoll_ctl()操作事件表注册修改删除事件,epoll_wait()在超时时间内等待一组文件描述符上的事件。不会复用文件描述符集合来传递结果而迫使开发者每次等待事件之前都必须重新准备要被侦听的文件描述符集合,另一点原因就是获取事件的时候,它无须遍历整个被侦听的描述符集,只要遍历那些被内核IO事件异步唤醒而加入Ready队列的描述符集合就行了.LT模式(默认)当epoll_wait检测到其上有事件发生并将此事件通知应用程序后应用程序可以不立即处理当下一次应用程序调用epoll_wait还会通知此事件。ET模式是epoll_wait检测到事件发生通知应用程序且应用程序立即处理该事件后续不再通知。在非阻塞模式下读写系统调用返回-1不一定是出错可能是EAGAIN和EWOULDBLOCK表示资源暂不可用那么这种情况在ET模式下表示本次读写全部完毕(ET模式下由于一次性触发必须读写一次性完毕)epoll可以再次触发该描述符上的事件了
        缺省状态下的套接字都是阻塞方式的,这意味着一个套接口的调用不能立即完成时,进程将进入睡眠状态,并等待操作完成。对于某些应用,需要及时可控的客户响应,而阻塞的方式可能会导致一个较长的时间段内,连接没有响应。造成套接字阻塞的操作主要有recv, send, accept, connect. 那么针对非阻塞socket需要特殊的机制,以非阻塞connect为例:非阻塞模式有3种用途1.三次握手同时做其他的处理。connect要花一个往返时间完成,从几毫秒的局域网到几百毫秒或几秒的广域网。这段时间可能有一些其他的处理要执行,比如数据准备,预处理等。2.用这种技术建立多个连接。这在web浏览器中很普遍.3.由于程序用select等待连接完成,可以设置一个select等待时间限制,从而缩短connect超时时间。在一个TCP套接口设置为非阻塞后,调用connect,connect会在系统提供的errno变量中返回一个EINRPOCESS错误,此时TCP的三路握手继续进行,之后可以用select函数检查这个连接是否建立成功。
        处理非阻塞connect的步骤:第一步:创建socket,返回套接口描述符。第二步:调用fcntl把套接口描述符设置成非阻塞。第三步:调用connect开始建立连接。第四步:判断连接是否成功建立:A:如果connect返回0,表示连接简称成功(服务器可客户端在同一台机器上时就有可能发生这种情况);B:调用select来等待连接建立成功完成,如果select返回0,则表示建立连接超时,返回超时错误给用户,同时关闭连接,以防止三路握手操作继续进行下去;如果select返回大于0的值,则需要检查套接口描述符是否可读或可写,如果套接口描述符可读或可写,则可以通过调用getsockopt来得到套接口上待处理的错误(SO_ERROR),如果连接建立成功,这个错误值将是0,如果建立连接时遇到错误,则这个值是连接错误所对应的errno值(比如:ECONNREFUSED,ETIMEDOUT等)."读取套接口上的错误"是遇到的第一个可移植性问题;如果出现问题,getsockopt源自Berkeley的实现是返回0,等待处理的错误在变量errno中返回;但是Solaris会让getsockopt返回-1,errno置为待处理的错误;我们对这两种情况都要处理;
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值