欢迎光临散文网 会员登陆 & 注册

Linux基础(五)——项目实战与总结

2023-06-17 16:38 作者:UCLmsc  | 我要投稿

一、阻塞和非阻塞、同步和异步

阻塞和非阻塞通常用来描述代码在等待某些操作完成时的行为:
阻塞:当代码执行一个需要等待某些操作完成的任务时,它会一直等待,直到操作完成后才继续执行下一步操作。
非阻塞:当代码执行一个需要等待某些操作完成的任务时,它会立即返回并继续执行下一步操作,而不是等待操作完成。
同步和异步通常用来描述代码执行的方式:
同步:当代码执行一个任务时,它会一直等待该任务完成后才继续执行下一步操作。
异步:当代码执行一个任务时,它会继续执行下一步操作,而不是等待任务完成。在任务完成后,代码会得到通知,然后再执行相应的操作。同步阻塞、同步非阻塞、异步阻塞、异步非阻塞是四种常见的代码执行方式,它们描述了代码在等待任务完成时的行为以及执行方式。

同步阻塞:代码执行一个任务时,会一直等待任务完成后才继续执行下一步操作。在等待任务完成期间,代码会被阻塞,不能执行其他操作。
同步非阻塞:代码执行一个任务时,会立即返回并继续执行下一步操作,而不是等待任务完成。但是,在等待任务完成期间,代码会不断轮询任务状态,直到任务完成。
异步阻塞:代码执行一个任务时,不会等待任务完成,而是继续执行下一步操作。但是,在任务完成之前,代码会被阻塞,不能执行其他操作。
异步非阻塞:代码执行一个任务时,不会等待任务完成,而是继续执行下一步操作。在等待任务完成期间,代码会进行其他操作。当任务完成时,代码会得到通知,并执行相应的操作。
这四种执行方式都有其适用场景和优缺点。例如,同步阻塞适用于对执行顺序和结果准确性要求较高的场景,但是可能会导致性能问题和资源浪费;异步非阻塞适用于对性能要求较高的场景,但是可能会导致代码复杂度增加。开发人员应根据实际情况选择适合的执行方式。


二、Unix、Linux上的五种IO模型

阻塞(Blocking)IO模型。这种模型是最简单的IO模型,也是默认的IO模型。

在阻塞IO模型中,当应用程序调用一个IO操作(如读取数据)时,如果没有数据可读,则应用程序会被阻塞,直到有数据可读或发生错误。在这个过程中,应用程序无法执行其他操作,直到IO操作完成。

阻塞IO模型的优点是简单易懂,容易实现和使用。但是,它的缺点也很明显:由于应用程序在IO操作期间被阻塞,因此它无法同时处理其他任务,造成了资源浪费和性能瓶颈。

对于网络编程来说,阻塞IO模型也有其局限性。当一个应用程序在等待数据时,它会一直等待,直到有数据可读或发生错误。这可能会导致应用程序的响应时间变慢,尤其是在高并发的情况下。

因此,在高并发、高性能的网络编程中,通常需要使用其他更高级的IO模型,如IO多路复用、非阻塞IO、异步IO等,以提高系统的性能和响应能力。
非阻塞(Non-blocking)IO模型。这种模型是在阻塞IO模型的基础上发展而来的一种IO模型。

在非阻塞IO模型中,当应用程序调用一个IO操作时,如果没有数据可读,则应用程序不会被阻塞,而是立即返回一个错误码(如EAGAIN或EWOULDBLOCK),表示当前没有数据可读。应用程序可以继续执行其他任务,直到后续再次尝试IO操作。

在这个过程中,应用程序需要不断地轮询IO状态,以确定是否有数据可读或写入。虽然非阻塞IO模型可以避免阻塞,提高应用程序的响应能力,但它需要应用程序不断地轮询IO状态,造成CPU资源浪费。

对于网络编程来说,非阻塞IO模型的应用场景较为有限。当应用程序需要同时处理多个连接时,使用非阻塞IO模型时,应用程序需要不断轮询每个连接的IO状态,这可能会导致系统的性能下降。因此,在高并发、高性能的网络编程中,通常需要使用其他更高级的IO模型,如IO多路复用、异步IO等。
IO复用(IO Multiplexing)模型。这种模型使用IO多路复用技术,可以同时处理多个IO操作,从而提高系统的性能和响应能力。

在IO复用模型中,应用程序可以将多个IO操作注册到一个IO多路复用器中,例如select、poll或epoll。IO多路复用器会不断轮询这些IO操作的状态,当有数据可读或可写时,IO多路复用器会通知应用程序进行操作。

使用IO复用模型时,应用程序可以同时处理多个IO操作,而不必为每个IO操作创建一个独立的线程或进程。这可以减少线程或进程的数量,从而减少系统开销,提高系统的吞吐量和响应能力。

IO复用模型的缺点是,应用程序需要不断轮询IO状态,造成CPU资源浪费。此外,由于IO多路复用器需要维护多个IO操作,因此在高并发的情况下,IO多路复用器可能会成为瓶颈,导致系统的性能下降。

总体来说,IO复用模型适用于需要同时处理多个IO操作、但IO操作之间的关系比较简单的场景,例如网络服务器、文件服务器等。在高并发、高性能的网络编程中,IO复用模型是一个比较常用的IO模型。
信号驱动(Signal-driven)IO模型。这种模型使用信号机制来通知应用程序IO操作已经完成,从而避免了阻塞和轮询IO状态的问题。

在信号驱动IO模型中,应用程序首先要通过signal函数将一个信号(如SIGIO)与一个文件描述符关联起来。当IO操作完成时,操作系统会向应用程序发送一个SIGIO信号,应用程序可以通过信号处理函数来处理这个信号,从而完成IO操作。

使用信号驱动IO模型时,应用程序可以在等待IO操作完成时进行其他操作,而不必被阻塞。信号驱动IO模型可以提高应用程序的响应能力,同时避免了轮询IO状态的CPU资源浪费。

但是,信号驱动IO模型也有其缺点。由于信号是异步的,因此应用程序需要使用信号处理函数来处理信号,而信号处理函数可能会影响应用程序的可维护性和可读性。此外,在高并发、高性能的网络编程中,信号驱动IO模型可能会出现一些问题,例如信号处理函数的竞争条件等。

总体来说,信号驱动IO模型适用于需要避免阻塞和轮询IO状态的场景,例如实时媒体流、网络游戏等。在高并发、高性能的网络编程中,信号驱动IO模型通常使用较少,而更常用的是IO复用模型和异步IO模型。

异步(Asynchronous)IO模型。这种模型使用异步IO技术,可以在IO操作完成之前继续执行其他操作,从而提高系统的性能和响应能力。

在异步IO模型中,应用程序可以通过aio_read和aio_write等异步IO函数发起IO操作,然后立即返回。当IO操作完成时,操作系统会通知应用程序,应用程序可以在回调函数中处理数据。

使用异步IO模型时,应用程序可以在等待IO操作完成时继续执行其他操作,而不必被阻塞或轮询IO状态。异步IO模型可以提高应用程序的响应能力和吞吐量,同时减少CPU资源的浪费。

但是,异步IO模型也有其缺点。异步IO模型的实现比较复杂,需要使用操作系统提供的异步IO接口,例如POSIX的aio系列函数或Windows的IO Completion Ports。此外,在不同的操作系统和平台上,异步IO接口的实现也可能不同,需要进行不同的适配。

总体来说,异步IO模型适用于需要处理大量IO操作和高并发的场景,例如网络服务器、数据库等。在高并发、高性能的网络编程中,异步IO模型是一个比较常用的IO模型。
Asynchronous I/O control block,简称AIO控制块,是在异步IO模型中使用的一种数据结构,用于描述异步IO操作的参数和状态。

在Unix、Linux系统中,AIO控制块的结构体类型为struct aiocb,定义在<aio.h>头文件中。该结构体包含了以下字段:

void *aio_buf:指向IO数据缓冲区的指针。
size_t aio_nbytes:IO操作的数据长度。
off_t aio_offset:IO操作在文件中的偏移量。
int aio_fildes:文件描述符。
int aio_lio_opcode:IO操作类型,取值为LIO_READ或LIO_WRITE。
int aio_reqprio:IO操作的优先级。
struct sigevent aio_sigevent:异步IO完成后的通知方式,可以是信号、线程或进程。
int aio_return:IO操作完成后的返回值。
int aio_errno:IO操作完成时的错误码。
int aio_offset:IO操作完成时的偏移量。
int aio_nbytes:IO操作完成时的数据长度。
使用AIO控制块时,应用程序可以通过aio_read和aio_write等异步IO函数发起IO操作,并传递一个AIO控制块作为参数。在IO操作完成后,操作系统会更新AIO控制块的状态,应用程序可以通过读取AIO控制块的字段来获取IO操作的结果。

需要注意的是,AIO控制块只适用于异步IO模型,不能用于其他IO模型。在使用AIO控制块时,应用程序需要使用操作系统提供的异步IO接口,例如POSIX的aio系列函数或Windows的IO Completion Ports。

三、Web服务器简介及HTTP协议

Web服务器是一种用于提供Web服务的服务器软件,它可以接收来自客户端(例如Web浏览器)的HTTP请求,处理请求并向客户端返回HTTP响应。Web服务器通常运行在Web应用程序的后台,并负责处理来自多个客户端的请求。

Web服务器可以提供各种不同类型的Web服务,例如静态网页、动态网页、文件下载、视频流媒体等。Web服务器通常会提供一些可配置的选项,例如虚拟主机、访问控制、数据缓存等,以满足不同应用程序的需求。

Web服务器通常采用多线程或多进程的方式来处理客户端请求,以提高并发性能。另外,随着Web应用程序的复杂性不断增加,一些Web服务器也开始支持异步IO模型和多路复用技术,以进一步提高性能和响应能力。

常见的Web服务器软件包括Apache、Nginx、Microsoft IIS、Lighttpd等。这些Web服务器都有不同的优缺点和适用场景,应用程序需要根据自己的需求选择合适的Web服务器。

四、服务器编程基本框架和两种高效的事件处理模式


服务器编程基本框架

服务器编程的基本框架通常由以下几个组成部分:

网络通信模块:负责与客户端建立连接、接收和发送数据,常用的网络通信协议包括TCP/IP、HTTP等。

业务逻辑模块:负责处理客户端发送过来的请求,根据请求内容进行相应的处理,并将处理结果返回给客户端。

数据存储模块:负责将服务器收到的数据进行存储,常用的数据存储方式包括关系型数据库和非关系型数据库等。

安全认证模块:负责对客户端请求进行身份验证、权限控制等操作,以确保服务器数据的安全性。

日志记录模块:负责记录服务器的运行日志,包括请求处理情况、异常信息等。

以上模块可以根据实际需求进行扩展和精简,具体实现方式也可以根据编程语言和框架的不同而有所不同。
Reactor模式是一种常用的事件驱动编程模式,它是一种高效的I/O多路复用技术实现方式,通常应用于服务器端的网络编程中。

在Reactor模式中,程序通过一个事件循环来监听多个I/O事件,当有事件发生时,会根据不同的事件类型来调用相应的回调函数进行处理。这种模式的核心是事件循环,它不断地轮询所有注册的I/O事件,一旦有事件发生,就会触发相应的处理函数。在事件循环中,Reactor会使用一个专门的线程来处理所有的I/O事件,其他业务逻辑则可以运行在其他线程中,从而实现了高并发的处理能力。

Reactor模式的核心组件包括:事件处理器、事件源、事件循环以及回调函数。事件处理器负责处理I/O事件,事件源则是产生I/O事件的对象,事件循环则负责监听所有的事件源,回调函数则是具体的业务逻辑实现。

总之,Reactor模式是一种应用广泛的事件驱动编程模式,它可以提高程序的性能和可维护性,尤其适用于高并发的网络编程。
Proactor模式是一种异步I/O模式,它允许应用程序在等待I/O操作完成时继续执行其他任务,而不必阻塞线程。在Proactor模式中,应用程序通过提交I/O请求来启动I/O操作,然后立即返回。当I/O操作完成时,操作系统会通知应用程序,并将结果传递回应用程序。

Proactor模式的核心思想是将I/O操作的处理分为两个阶段:请求提交和请求完成。在请求提交阶段,应用程序提交I/O请求,并指定I/O操作完成后将要执行的回调函数。在请求完成阶段,当I/O操作完成后,操作系统将调用指定的回调函数,通知应用程序I/O操作已完成,并将结果传递给应用程序。

与Reactor模式不同,Proactor模式将I/O操作的管理责任移交给了操作系统,使得应用程序无需关心底层I/O操作的实现细节。这使得应用程序可以更加专注于业务逻辑的实现,而无需过多关注I/O操作的处理。

Proactor模式被广泛应用于网络编程、数据库访问等需要大量I/O操作的应用程序中,以提高应用程序的性能和可扩展性。
使用了select函数来监听多个文件描述符。在主程序中,我们首先创建一个服务器端的socket,并将其绑定到本地地址。然后,我们调用listen函数开始监听连接请求。

在每次循环中,我们使用FD_ZERO和FD_SET函数将服务器端的socket加入到读事件集合中,并将所有客户端的socket加入到读事件集合中。然后,我们使用select函数等待一个I/O事件的发生。

如果服务器端的socket可读,说明有新的连接请求,我们使用accept函数接受新的连接,并将其加入到客户端socket数组中。如果客户端socket可读,说明有数据可读,我们使用recv函数接收数据,并使用send函数将数据转换为大写后发送回客户端。如果客户端关闭了连接,我们将其从客户端socket数组中删除。注意,在这个例子中,我们使用了一个固定大小的客户端socket数组,最大值为MAX_CLIENTS。

这个例子中使用了阻塞I/O函数recv和send,因此它是同步I/O模式。虽然它不能像Proactor模式一样实现真正的异步I/O操作,但是它可以有效地利用操作系统提供的多路复用机制来实现并发处理,从而提高系统的吞吐量和响应速度。

五、线程同步机制类封装及线程池实现

线程池是一种常见的并发编程技术,它可以在应用程序启动时创建一组线程,并在需要时将任务分配给这些线程来执行。线程池可以提高应用程序的性能和响应速度,因为线程池中的线程可以重用,减少了线程创建和销毁的开销,同时也可以避免过多的线程数导致系统负载过高的问题。

线程池通常包括以下几个组成部分:

任务队列:用于存储等待执行的任务。

线程池管理器:用于管理线程池中的线程,包括创建、销毁、分配任务等操作。

线程池:由一组线程组成,用于执行任务队列中的任务。

线程池的工作流程如下:

初始化线程池:创建一组线程,并初始化任务队列和其他相关参数。

添加任务:将任务添加到任务队列中。

线程池管理器从任务队列中取出一个任务,分配给空闲的线程来执行。

线程执行任务并将执行结果返回。

线程池管理器将任务执行结果存储到一个结果队列中。

应用程序可以从结果队列中获取任务执行结果。

当任务队列为空时,线程池管理器等待新任务的到来。

当应用程序退出时,销毁线程池。

线程池的优点:

提高性能和响应速度:线程池可以重用线程,减少了线程创建和销毁的开销,同时也可以避免过多的线程数导致系统负载过高的问题。

简化编程:线程池可以将任务分离出来,使得应用程序的主线程可以专注于其他的工作,从而简化了编程。

提高代码可读性和可维护性:线程池可以将任务和线程分离出来,使得代码更加模块化,易于理解和维护。

提供了线程管理功能:线程池可以自动管理线程的创建、销毁和调度,使得程序员不必手动管理线程。

线程池的缺点:

需要额外的开销:线程池需要额外的开销来管理线程和任务队列,这可能会降低系统的性能。

可能引起死锁问题:线程池中的线程可能会因为竞争互斥锁而导致死锁问题。

任务分配可能存在不均衡问题:线程池中的线程可能会被分配到大量的繁重任务,导致其他任务无法得到及时执行。

线程池代码——线程同步机制封装类(互斥锁类、条件变量类、信号量类)和线程池类

定义了三个线程同步机制的封装类:互斥锁类(Mutex)、条件变量类(Condition)和信号量类(Semaphore),它们都是使用POSIX线程库(pthread)来实现的。

然后,我们定义了一个任务类(Task),用于封装任务的执行函数和参数。每个任务都有一个执行函数和一个参数,当任务被执行时,它会调用这个函数,并将参数传递给它。

最后,我们定义了一个线程池类(ThreadPool),它包含一个线程数组、一个任务队列、一个互斥锁和一个条件变量。线程池的构造函数会创建一组线程,并将它们存储在线程数组中。当任务被提交到线程池时,线程池会将任务添加到任务队列中,并使用条件变量通知空闲线程来执行任务。每个线程会重复执行一个worker()函数,该函数从任务队列中获取任务并执行。

请注意,这只是一个简单的线程池实现,可能不适用于所有情况。在实际应用中,您可能需要根据您的具体需求进行调整和优化。例如,您可能需要添加一个任务队列的最大大小限制,以避免任务队列过大导致系统资源耗尽的问题。

在网络编程中,EPOLLONESHOT事件是Linux中epoll描述符可以设置的一个标志。当设置了这个标志后,epoll描述符在传递一次事件给调用者后会被禁用。这意味着调用者必须通过调用epoll_ctl()函数并使用EPOLL_CTL_MOD命令以及EPOLLONESHOT标志来重新启用epoll描述符以接收后续的事件。

EPOLLONESHOT标志在多个线程等待同一个epoll描述符上的事件时非常有用。如果没有设置EPOLLONESHOT标志,多个线程可能会同时接收到同一个事件并尝试同时处理它,导致竞争条件和其他同步问题。通过设置EPOLLONESHOT标志,每个事件只会被一个线程接收,其他线程直到epoll描述符重新启用后才会被通知。

需要注意的是,设置EPOLLONESHOT标志并不意味着与事件相关联的套接字或文件描述符会被关闭或禁用,它只影响epoll描述符本身的行为。

六、web服务器核心代码

包括解析HTTP请求报文、解析请求完成及生成响应信息。

文件结构:

  • httpConn.cpp: HTTP 连接处理实现

  • httpConn.h: HTTP 连接处理类定义

  • locker.h: 互斥量和条件变量封装

  • main.cpp: 主函数

  • threadpool.h: 线程池实现

httpConn.h

httpConn.cpp

locker.h

threadpool.h

main.cpp

七、web服务器优化功能——定时检测非活跃连接

lst_timer.h

nonactive_conn.cpp

八、服务器压力测试

Webbench是一个用于测试Web服务器性能的开源工具,由Lionbridge公司的研发部门开发。它可以模拟多个客户端同时访问一个Web服务器,以便测试服务器的性能和负载能力。

Webbench的运行方式比较简单,用户只需指定要测试的URL以及并发访问的客户端数量,就可以启动测试。测试完成后,Webbench会输出测试结果,包括服务器的响应时间、吞吐量、错误率等信息。

Webbench的优点是具有简单易用、快速方便等特点,测试结果比较直观,可以帮助开发人员快速了解Web服务器的性能瓶颈。但是Webbench也存在一些缺点,比如测试方法比较简单,不考虑服务器的缓存机制等因素,测试结果可能不够准确;同时,Webbench只能测试静态页面的性能,对于动态页面的测试效果比较有限。

总的来说,Webbench是一款对于初学者来说比较适合的Web服务器性能测试工具,但是对于高级用户或专业测试人员来说,可能需要使用更加复杂和全面的测试工具来进行Web服务器性能测试。


Linux基础(五)——项目实战与总结的评论 (共 条)

分享到微博请遵守国家法律