信号驱动io

本文详细介绍了信号驱动I/O模型,它使得当网络套接字可读时,内核通过发送SIGIO信号通知应用进程,应用可以开始读取数据。通过设置套接字允许信号驱动I/O,注册SIGIO信号处理程序,使用fcntl进行相关设置,实现非阻塞的数据读取。信号驱动I/O常用于UDP编程,避免阻塞,提高效率。

5.4 信号驱动I/O
使用信号驱动I/O时,当网络套接字可读后,内核通过发送SIGIO信号通知应用进程,于是应用可以开始读取数据。有时也称此方式为异步I/O。但是严格讲,该方式并不能算真正的异步I/O,因为实际读取数据到应用进程缓存的工作仍然是由应用自己负责的。
5.4.1 信号驱动I/O模型

图5-5 信号驱动I/O

首先允许套接字使用信号驱动I/O模式,并且通过sigaction系统调用注册一个SIGIO信号处理程序。当有数据到达后,系统向应用进程交付一个SIGIO信号,然后既可以如图中所示那样在信号处理程序中读取套接字数据,然后通知主循环处理逻辑,也可以直接通知主循环逻辑,让主程序进行读取操作。
无论采用上面描述的哪种方式读取数据,应用进程都不会因为尚无数据达到而被阻塞,应用主循环逻辑可以继续执行其他功能,直到收到通知后去读取数据或者处理已经在信号处理程序中读取完毕的数据。

5.4.2 设置套接字允许信号驱动I/O
为了让套接字描述符可以工作于信号驱动I/O模式,应用进程必须完成如下三步设置:
1.注册SIGIO信号处理程序。(安装信号处理器)
2.使用fcntl的F_SETOWN命令,设置套接字所有者。(设置套接字的所有者)
3.使用fcntl的F_SETFL命令,置O_ASYNC标志,允许套接字信号驱动I/O。(允许这个套接字进行信号输入输出)
注意,必须保证在设置套接字所有者之前,向系统注册信号处理程序,否则就有可能在fcntl调用后,信号处理程序注册前内核向应用交付SIGIO信号,导致应用丢失此信号。下面的程序片段描述了怎样为套接字设置信号驱动I/O:
sigaction 函数:
    int sigaction(int signum,const struct sigaction *act,struct sigaction *oldact)
该函数会按照参数signum指定的信号编号来设置该信号的处理函数,signum可指定SIGK
ILL和SIGSTOP以外的所有信号,如果参数act不是NULL指针,则用来设置新的信号处理方式。
sigaction 结构体:
    struct sigaction
    {
        void (*sa_handler)(int);//信号处理函数
        sigset_t sa_mask;//用来设置在处理该信号时暂时将sa_mask指定的信号搁置
        int sa_flags;//用来设置信号处理的其他相关操作
        void (*sa_restorer)(void);//这个参数没有使用
    }
sa_flags的值为:
    A——NOCLDSTOP 如果参数signum为SIGCHILD,则当子进程暂停时并不会通知父进程
    SA_ONESHOT/SA——RESETHAND:但调用新的信号处理函数前,将这个信号处理方式改
    为系统默认方式。
    SA——NOMASK/SA——NODEFER:在处理这个信号没有结束前,不理会这个信号的再次到
    来。
以上的几个信号可以用“|”or运算组合起来使用。
struct tm
{
    int tm_sec;
    int tm_min;
    int tm_hour;
    int tm_mday;
    int tm_mon;
    int tm_year;
    int tm_wday;
    int tm_yday;
    int tm_isdst;
#ifdef __USE_BSD
    long int __tm_gmtoff;
    __const char *__tm_zone;
#endif
};

sigmptyset函数:
#include
    int sigemptyset(sigset_t *set);
    用来将参数set信号集初始化并清空;
    执行成功则返回0,如果有错误则返回-1;
    函数的实现:
static inline void sigemptyset(sigset_t,*set)
{
    switch(_NSIG_WORDS){
        default:
            memset(set,0,sizeof(sigse_t));
            break;
        case 2:set->sig[1]=0;
        case 1:set->sig[0]=0;
               break;
    }
}

sigset_t
信号集级信号集操作函数:信号集被定义为一种数据类型:
typedef struct
{
    unsigned long sig[_NSIG_WORDS];
}sigset_t;

static inline void sigaddset(sigset_t *set ,int _sig)
{
    asm ("bfset %0{%1,#1}"
        :"+od" (*set)
        :"id"((_sig-1^31)
        :"cc"));
}//没有看懂这段代码的意思,函数的功能是将_sig 代表的信号
加入到set信号集里面。

int sigprocmask(int how,sigset_t *set,sigset_t *oldset)
{
    int error;
    spin_lock_irq(¤t->sighand->siglock);
    if(oldset)
        *oldset=current->blocked;
    error=0;
    switch(how)
    {
        case SIG_BLOCK:
            sigorsets(¤t->blocked,¤t->blocked,set);
            break;
        case SIG_UNBLOCK:
            signandsets(¤t->blocked,¤t->blocked,set);
            break;
    &nbs

<think>嗯,用户想了解事件驱动IO信号驱动IO的区别,还提到五大IO模型中并没有事件驱动IO。我需要先理清楚这两个概念以及它们在IO模型中的位置。首先,得回忆一下五大IO模型,分别是阻塞IO、非阻塞IOIO多路复用、信号驱动IO和异步IO。确实,标准分类里没有事件驱动IO这个术语,但用户可能在哪里看到过混淆的说法? 事件驱动IO通常是指一种编程模型,可能结合了IO多路复用,比如使用select、poll或epoll这些机制。而信号驱动IO则是通过SIGIO信号通知应用程序数据就绪,属于不同的机制。需要明确两者的区别,避免混淆。 用户可能对事件驱动IO多路复用的关系不太清楚,或者有些资料里把事件驱动IO多路复用混为一谈。需要解释清楚事件驱动通常属于应用层设计模式,而IO多路复用是底层机制的一种,属于同步IO模型。 还要注意,信号驱动IO虽然使用信号通知,但处理信号时仍然可能涉及阻塞操作,所以它和异步IO不同,异步IO是整个操作完成后才通知,数据已经准备好,可以直接使用,无需再次读取。 可能需要举例说明,比如事件驱动框架如Node.js或Nginx,它们基于IO多路复用实现高并发,而信号驱动IO则较少见,常用于特定场景如网络socket设置SIGIO信号。 另外,用户可能存在的疑问是为什么事件驱动不在五大模型中,需要指出事件驱动更多是架构设计,而五大模型是操作系统级别的IO处理方式,属于不同层次的概念。确保回答结构清晰,逐步解释每个概念,并对比它们的异同。</think>好的,我将逐步解释这两个概念及其与五大IO模型的关系: --- ### 一、五大IO模型的标准分类 根据UNIX网络编程中的经典定义,五大IO模型为: 1. **阻塞IO** - 进程持续等待数据就绪,期间无法执行其他任务 2. **非阻塞IO** - 进程轮询检查数据状态,需主动消耗CPU资源 3. **IO多路复用 (IO Multiplexing)** - 通过`select/poll/epoll`等机制监控多个描述符 4. **信号驱动IO (Signal-Driven IO)** - 内核通过信号(如`SIGIO`)通知进程数据就绪 5. **异步IO (Async IO)** - 内核完成所有操作后通知进程(如Linux的`io_uring`) --- ### 二、关于"事件驱动IO" #### 1. 概念辨析 - **事件驱动**是编程模型,非标准IO模型 $$ \text{事件驱动} \approx \text{IO多路复用} + \text{回调机制} $$ - 典型实现: - `epoll`(Linux)或`kqueue`(BSD)作为底层多路复用器 - 框架如Node.js、Nginx通过事件循环处理IO #### 2. 与标准模型的对应 - 事件驱动本质属于**IO多路复用**模型 - 区别在于抽象层级: | 层级 | IO多路复用 | 事件驱动 | |------|------------|----------| | 实现 | 系统调用 | 编程框架 | | 关注点 | 描述符状态 | 事件响应 | --- ### 三、信号驱动IO详解 #### 1. 工作原理 $$ \text{进程注册信号处理函数} \rightarrow \text{内核发送SIGIO} \rightarrow \text{进程处理数据} $$ - 设置步骤: ```c fcntl(fd, F_SETOWN, pid); // 设置接收信号的进程 fcntl(fd, F_SETFL, O_ASYNC); // 启用信号驱动 signal(SIGIO, handler_function); // 注册信号处理函数 ``` #### 2. 关键特征 - **通知阶段**:数据到达内核缓冲区时触发信号 - **数据阶段**:仍需进程调用`recvfrom`同步读取数据 - 与异步IO的核心区别: $$ \text{信号驱动IO} \in \text{同步IO} \quad vs \quad \text{异步IO} \in \text{异步操作} $$ --- ### 四、对比总结 | 特征 | 事件驱动IO多路复用) | 信号驱动IO | |---------------------|-------------------------------|------------------------| | **通知机制** | 就绪事件列表(可读/可写等) | SIGIO信号 | | **编程复杂度** | 需管理事件循环 | 需处理信号竞争 | | **典型应用场景** | Web服务器、高并发网络编程 | 实时设备监控 | | **数据操作阶段** | 同步读取 | 同步读取 | | **平台兼容性** | 依赖具体实现(epoll/kqueue) | POSIX标准支持 | --- ### 五、常见误解澄清 1. **为什么常听说"事件驱动模型"?** - 这是应用层设计模式的概念迁移,实际对应IO多路复用 2. **信号驱动IO的缺陷** - 信号队列溢出可能导致丢失通知 - 信号处理函数中不能调用非异步安全函数 3. **异步IO vs 事件驱动** - 异步IO是真正的全异步(如Windows IOCP) - 事件驱动本质仍是同步非阻塞模式 --- ### 六、扩展理解 - **Proactor模式**:真正的异步IO实现(如Windows IOCP) - **Reactor模式**:事件驱动的基础(如libevent库) - 现代发展趋势: $$ \text{io_uring(Linux)} \rightarrow \text
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值