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(