meiyao 发表于 2024-3-28 19:57

《原子Linux驱动开发》+阻塞和非阻塞IO,异步通知

<p>《原子Linux驱动开发》这本书主要聚焦于Linux内核驱动开发的核心概念和实现方法,对于驱动开发者来说是一本非常有价值的参考书籍。在驱动开发中,处理输入/输出(I/O)是常见的任务,而阻塞和非阻塞I/O以及异步通知是处理I/O操作的几种重要方法。</p>

<h3>阻塞I/O</h3>

<p>阻塞I/O是一种常见的I/O模式,在这种模式下,当线程发起一个I/O操作(如读取或写入)时,如果数据没有准备好,线程会阻塞(即暂停执行),直到数据准备好。这意味着线程在等待数据期间无法执行其他任务,从而可能导致资源利用率低下。</p>

<h3>非阻塞I/O</h3>

<p>与阻塞I/O不同,非阻塞I/O允许线程在等待数据准备好的同时执行其他任务。如果数据没有准备好,非阻塞I/O调用会立即返回错误,而不是让线程阻塞。这提高了系统的响应性和吞吐量,但也可能增加程序的复杂性,因为线程需要不断地检查数据是否准备好。</p>

<h3>异步通知</h3>

<p>异步通知是一种机制,用于在数据准备好时通知线程,而不是让线程主动轮询或阻塞。这通常通过中断、信号或回调函数来实现。当数据准备好时,硬件或内核会触发一个通知,告知线程可以进行I/O操作。这种机制可以提高系统的效率和响应性,特别是在处理大量并发I/O操作时。</p>

<p>在Linux驱动开发中,实现这些I/O模式需要深入理解Linux内核的I/O机制、中断处理、信号量、等待队列等概念。驱动开发者需要根据具体的应用场景和需求选择适合的I/O模式,并合理地设计驱动程序的结构和接口。</p>

<p>&nbsp;</p>

<p> &nbsp;</p>

<p>在Linux系统中,非阻塞I/O访问模式是一种常见的方法,用于处理那些可能需要花费较长时间等待的操作,比如从设备读取数据。当设备不可用或数据未准备好时,非阻塞I/O调用会立即返回一个错误码,而不是让调用线程进入睡眠状态等待数据准备好。这样,应用程序可以继续执行其他任务,而不是阻塞等待数据。</p>

<p>&nbsp;</p>

<p> &nbsp;<code>select()</code>函数允许一个进程监视多个文件描述符,以查看它们中的任何一个是否已准备好进行读、写或异常操作。这在处理多个套接字或文件描述符时特别有用,因为它允许进程非阻塞地等待多个事件。</p>

<h3><code>select()</code>&nbsp;函数原型</h3>

<pre>
<span style="font-weight: bold;">参数解释</span>
</pre>

<ul>
        <li>nfds:这是所有文件描述符集合中最大的文件描述符加1。这通常设置为要监视的所有文件描述符中的最大值加1。</li>
        <li>readfds:指向fd_set结构的指针,用于监视准备读操作的文件描述符。</li>
        <li>writefds:指向fd_set结构的指针,用于监视准备写操作的文件描述符。</li>
        <li>exceptfds:指向fd_set结构的指针,用于监视异常条件。</li>
        <li>timeout:指向timeval结构的指针,指定了select()等待的最长时间。如果为NULL,则select()将无限期等待。</li>
</ul>

<p>&nbsp;</p>

<p></p>

<ul>
        <li>FD_ZERO(fd_set *set): 清除fd_set集合的所有位。</li>
        <li>FD_SET(int fd, fd_set *set): 在fd_set集合中设置与文件描述符fd相关联的位。</li>
        <li>FD_CLR(int fd, fd_set *set): 清除fd_set集合中与文件描述符fd相关联的位。</li>
        <li>FD_ISSET(int fd, fd_set *set): 测试fd_set集合中是否设置了与文件描述符fd相关联的位。</li>
</ul>

<p>&nbsp;</p>

<p>epoll()函数是Linux特有的I/O复用函数,用于处理大量的并发连接。相比于select和poll函数,epoll在性能上有所提升和改进。</p>

<p>epoll函数通过一组相关的函数来实现I/O复用,它使用一种高效的事件通知机制,将用户关心的文件描述符上的事件放在内核里的一个事件表中。这样,就无需像select和poll那样每次调用都要重复传入文件描述符集或事件集,从而提高了效率。</p>

<p>使用epoll函数,通常需要经过以下步骤:</p>

<ol>
        <li>创建epoll文件描述符:通过调用epoll_create函数来创建一个epoll实例,并返回一个用于后续操作的文件描述符。</li>
        <li>添加文件描述符到epoll中:使用epoll_ctl函数将需要监视的文件描述符添加到epoll实例中。这个函数可以进行注册、修改或删除文件描述符上的事件。</li>
        <li>等待事件的发生:调用epoll_wait函数来等待在epoll实例上注册的文件描述符上的事件发生。当有事件发生时,该函数会返回并告知哪些文件描述符就绪,可以进行相应的读或写操作。</li>
</ol>

<p>&nbsp;</p>

<p> &nbsp;</p>

<p>&nbsp;</p>

<p>关于Linux中的信号机制。信号是一种软件中断,用于通知进程发生了某个事件。在Linux系统中,信号用于通知进程各种异步事件,如用户按键、硬件故障、进程异常等。每个信号都有一个唯一的编号,并且通常与一个默认的行为相关联。</p>

<p>正如您所提到的,大多数信号都可以被进程捕获并处理,但是有几个信号是例外,例如SIGKILL和SIGSTOP。SIGKILL信号用于强制终止进程,它不能被忽略、阻塞或捕获。SIGSTOP信号用于暂停进程的执行,同样不能被忽略或捕获。</p>

<p>在应用程序中,可以使用signal()函数或更现代的sigaction()函数来设置信号的处理函数。当进程接收到一个信号时,如果该信号的处理函数已被设置,那么就会调用这个处理函数来响应这个信号。</p>

<p> &nbsp;</p>

<p>由于信号是异步的,因此信号处理函数应该尽可能简单和快速,避免执行可能阻塞或长时间运行的操作。这是因为信号可能在程序的任何时刻到达,如果处理函数执行复杂操作,可能会导致不可预测的行为或死锁。</p>
页: [1]
查看完整版本: 《原子Linux驱动开发》+阻塞和非阻塞IO,异步通知