1. 定义ngx_nonblocking 定义在 ./nginx-1.24.0/src/os/unix/ngx_socket.h#if(NGX_HAVE_FIONBIO)intngx_nonblocking(ngx_socket_ts);intngx_blocking(ngx_socket_ts);#definengx_nonblocking_nioctl(FIONBIO)#definengx_blocking_nioctl(!FIONBIO)#else#definengx_nonblocking(s)fcntl(s,F_SETFL,fcntl(s,F_GETFL)|O_NONBLOCK)#definengx_nonblocking_nfcntl(O_NONBLOCK)#definengx_blocking(s)fcntl(s,F_SETFL,fcntl(s,F_GETFL)~O_NONBLOCK)#definengx_blocking_nfcntl(!O_NONBLOCK)#endif过预编译指令 #if (NGX_HAVE_FIONBIO) 根据当前操作系统的支持情况 选择使用 ioctl 还是 fcntl 来实现2. 详解NGX_HAVE_FIONBIO 是一个宏 通常在 nginx 的自动配置阶段./configure根据系统特性定义。 如果系统支持通过 ioctl 的 FIONBIO 命令设置非阻塞模式则定义该宏进入第一个分支 否则如 Linux 等主流 POSIX 系统进入第二个分支使用 fcntl 实现。1. ioctlintngx_nonblocking(ngx_socket_ts){intnb;nb1;returnioctl(s,FIONBIO,nb);}#1 使用 ioctl 系统调用和 FIONBIO 命令 用于将 socket 设置为非阻塞模式 ioctl(s, FIONBIO, nb) 执行实际的设置 FIONBIO 是一个 ioctl 命令全称是“File I/O Non-Blocking I/O” 专门用于设置文件描述符包括 socket的非阻塞标志。 第三个参数是一个指向整数的指针。 在大多数系统中如果指针指向的整数值为非零则启用非阻塞模式 如果为零则禁用即设为阻塞模式。这里 nb 1 表示启用。 成功时返回 0。 失败时返回 -1并设置全局变量 errno 以指示错误原因#2 ioctl 是 Unix/Linux 系统中的一个系统调用其名称是 I/O Control 的缩写。 ioctl 的作用是对设备或文件描述符进行除了“读”和“写”之外的控制操作 在 Unix/Linux 哲学中“一切皆文件”。 read() / write()用于对文件/设备进行数据传输读内容、写内容。 open() / close()用于生命周期管理打开、关闭。 ioctl()用于参数配置和状态查询。 当某个设备或文件有一些特殊的属性需要设置 或者需要获取一些底层状态信息而 read/write 无法完成时就使用 ioctl。 例如 终端控制这是 ioctl 最原始的用途之一 获取终端窗口大小 设置终端模式例如关闭回显输入密码时不显示星号 存储设备控制 弹出光驱 ...2. fcntl#definengx_nonblocking(s)fcntl(s,F_SETFL,fcntl(s,F_GETFL)|O_NONBLOCK)#1 使用 fcntl 系统调用将 socket 描述符 s 设置为非阻塞模式的标准 POSIX 实现 目的 为文件描述符 s通常是 socket添加 O_NONBLOCK 标志使其变为非阻塞模式。 效果 设置后对 s 进行的读写操作如 read, write, accept, connect若无法立即完成不会阻塞进程 而是立即返回 -1 并设置 errno 为 EAGAIN 或 EWOULDBLOCK让应用程序有机会处理其他事件。#2 fcntl(s, F_GETFL) 命令F_GETFL —— 获取文件描述符 s 的当前文件状态标志 返回值 一个整数其中各个二进制位表示当前生效的标志如 O_RDONLY、O_NONBLOCK、O_APPEND 等。 如果调用失败返回 -1 并设置 errno。 作用 读取当前标志为后续修改做准备避免覆盖其他重要的标志位。#3 位运算| O_NONBLOCK O_NONBLOCK 是一个宏 代表一个特定的二进制位 将内层获取的标志与 O_NONBLOCK 进行按位或运算 结果是一个新的标志值其中 O_NONBLOCK 位被设置为 1 而其他标志位保持不变。 作用 确保只添加非阻塞标志而不影响其他已设置的标志如 O_APPEND、O_ASYNC 等。#4 外层调用fcntl(s, F_SETFL, new_flags) 命令F_SETFL —— 将文件描述符 s 的文件状态标志设置为第三个参数指定的值。 第三个参数即上一步位运算得到的新标志值。 返回值成功返回 0失败返回 -1 并设置 errno。#5 整体执行流程 1 读取当前标志 old_flags。 2 计算 new_flags old_flags | O_NONBLOCK。 3 将 new_flags 写回内核。#6 fcntl 是 Unix/Linux 系统中的一个系统调用其名称是 file control 的缩写。 它提供了一系列针对已打开文件描述符的控制操作 这些操作无法通过常规的 read、write 等 I/O 函数完成。 如果说 open 是开门read/write 是进出搬运东西 那么 fcntl 就是管理门的属性 比如这门是只能进不能出这门要不要在执行新程序时自动关上这门是不是非阻塞的。3. fcntl 与 ioctl 的设置非阻塞的区别 #1 fcntl (F_SETFL O_NONBLOCK) 是 POSIX.1 标准的一部分 (Unix/Linux 通用)可移植性好 主要平台 Linux, macOS, BSD, Unix 类系统 操作对象 文件描述符 (FD) 通用接口#2 ioctl (FIONBIO) 起源于 BSD Socket API后被 Winsock 采纳。 它操作的是“设备”或Socket的特定控制命令。 劣势: 命令码如 FIONBIO在不同系统间可能不兼容。Windows 下甚至函数名都变成了 ioctlsocket 主要平台 Windows (必须), 部分 Unix (兼容) 操作对象 Socket / 设备 专用接口#3 fcntl 是 Unix 标准通用方案ioctl 是 Windows/特定方案。 Nginx 通过条件编译在 Linux 上用 fcntl 保标准 在 Windows 上用 ioctl 保兼容从而实现了完美的跨平台支持。 总的来说fcntl 是设置非阻塞模式的现代、可移植的标准方法 而 ioctl 是一种历史遗留的替代方案仅在特定环境下仍被使用。 跨平台软件通常会在配置阶段检测系统特性选择合适的实现。
ngx_nonblocking
1. 定义ngx_nonblocking 定义在 ./nginx-1.24.0/src/os/unix/ngx_socket.h#if(NGX_HAVE_FIONBIO)intngx_nonblocking(ngx_socket_ts);intngx_blocking(ngx_socket_ts);#definengx_nonblocking_nioctl(FIONBIO)#definengx_blocking_nioctl(!FIONBIO)#else#definengx_nonblocking(s)fcntl(s,F_SETFL,fcntl(s,F_GETFL)|O_NONBLOCK)#definengx_nonblocking_nfcntl(O_NONBLOCK)#definengx_blocking(s)fcntl(s,F_SETFL,fcntl(s,F_GETFL)~O_NONBLOCK)#definengx_blocking_nfcntl(!O_NONBLOCK)#endif过预编译指令 #if (NGX_HAVE_FIONBIO) 根据当前操作系统的支持情况 选择使用 ioctl 还是 fcntl 来实现2. 详解NGX_HAVE_FIONBIO 是一个宏 通常在 nginx 的自动配置阶段./configure根据系统特性定义。 如果系统支持通过 ioctl 的 FIONBIO 命令设置非阻塞模式则定义该宏进入第一个分支 否则如 Linux 等主流 POSIX 系统进入第二个分支使用 fcntl 实现。1. ioctlintngx_nonblocking(ngx_socket_ts){intnb;nb1;returnioctl(s,FIONBIO,nb);}#1 使用 ioctl 系统调用和 FIONBIO 命令 用于将 socket 设置为非阻塞模式 ioctl(s, FIONBIO, nb) 执行实际的设置 FIONBIO 是一个 ioctl 命令全称是“File I/O Non-Blocking I/O” 专门用于设置文件描述符包括 socket的非阻塞标志。 第三个参数是一个指向整数的指针。 在大多数系统中如果指针指向的整数值为非零则启用非阻塞模式 如果为零则禁用即设为阻塞模式。这里 nb 1 表示启用。 成功时返回 0。 失败时返回 -1并设置全局变量 errno 以指示错误原因#2 ioctl 是 Unix/Linux 系统中的一个系统调用其名称是 I/O Control 的缩写。 ioctl 的作用是对设备或文件描述符进行除了“读”和“写”之外的控制操作 在 Unix/Linux 哲学中“一切皆文件”。 read() / write()用于对文件/设备进行数据传输读内容、写内容。 open() / close()用于生命周期管理打开、关闭。 ioctl()用于参数配置和状态查询。 当某个设备或文件有一些特殊的属性需要设置 或者需要获取一些底层状态信息而 read/write 无法完成时就使用 ioctl。 例如 终端控制这是 ioctl 最原始的用途之一 获取终端窗口大小 设置终端模式例如关闭回显输入密码时不显示星号 存储设备控制 弹出光驱 ...2. fcntl#definengx_nonblocking(s)fcntl(s,F_SETFL,fcntl(s,F_GETFL)|O_NONBLOCK)#1 使用 fcntl 系统调用将 socket 描述符 s 设置为非阻塞模式的标准 POSIX 实现 目的 为文件描述符 s通常是 socket添加 O_NONBLOCK 标志使其变为非阻塞模式。 效果 设置后对 s 进行的读写操作如 read, write, accept, connect若无法立即完成不会阻塞进程 而是立即返回 -1 并设置 errno 为 EAGAIN 或 EWOULDBLOCK让应用程序有机会处理其他事件。#2 fcntl(s, F_GETFL) 命令F_GETFL —— 获取文件描述符 s 的当前文件状态标志 返回值 一个整数其中各个二进制位表示当前生效的标志如 O_RDONLY、O_NONBLOCK、O_APPEND 等。 如果调用失败返回 -1 并设置 errno。 作用 读取当前标志为后续修改做准备避免覆盖其他重要的标志位。#3 位运算| O_NONBLOCK O_NONBLOCK 是一个宏 代表一个特定的二进制位 将内层获取的标志与 O_NONBLOCK 进行按位或运算 结果是一个新的标志值其中 O_NONBLOCK 位被设置为 1 而其他标志位保持不变。 作用 确保只添加非阻塞标志而不影响其他已设置的标志如 O_APPEND、O_ASYNC 等。#4 外层调用fcntl(s, F_SETFL, new_flags) 命令F_SETFL —— 将文件描述符 s 的文件状态标志设置为第三个参数指定的值。 第三个参数即上一步位运算得到的新标志值。 返回值成功返回 0失败返回 -1 并设置 errno。#5 整体执行流程 1 读取当前标志 old_flags。 2 计算 new_flags old_flags | O_NONBLOCK。 3 将 new_flags 写回内核。#6 fcntl 是 Unix/Linux 系统中的一个系统调用其名称是 file control 的缩写。 它提供了一系列针对已打开文件描述符的控制操作 这些操作无法通过常规的 read、write 等 I/O 函数完成。 如果说 open 是开门read/write 是进出搬运东西 那么 fcntl 就是管理门的属性 比如这门是只能进不能出这门要不要在执行新程序时自动关上这门是不是非阻塞的。3. fcntl 与 ioctl 的设置非阻塞的区别 #1 fcntl (F_SETFL O_NONBLOCK) 是 POSIX.1 标准的一部分 (Unix/Linux 通用)可移植性好 主要平台 Linux, macOS, BSD, Unix 类系统 操作对象 文件描述符 (FD) 通用接口#2 ioctl (FIONBIO) 起源于 BSD Socket API后被 Winsock 采纳。 它操作的是“设备”或Socket的特定控制命令。 劣势: 命令码如 FIONBIO在不同系统间可能不兼容。Windows 下甚至函数名都变成了 ioctlsocket 主要平台 Windows (必须), 部分 Unix (兼容) 操作对象 Socket / 设备 专用接口#3 fcntl 是 Unix 标准通用方案ioctl 是 Windows/特定方案。 Nginx 通过条件编译在 Linux 上用 fcntl 保标准 在 Windows 上用 ioctl 保兼容从而实现了完美的跨平台支持。 总的来说fcntl 是设置非阻塞模式的现代、可移植的标准方法 而 ioctl 是一种历史遗留的替代方案仅在特定环境下仍被使用。 跨平台软件通常会在配置阶段检测系统特性选择合适的实现。