实验9 信号驱动I/O |
1 信号驱动I/O 模型 使用信号驱动I/O 时,内核通过发送SIGIO 信号通知应用进程,于是应用进 程开始读取数据,信号驱动I/O 有时也成为异步I/O。 程序首先要允许套接字使用信号驱动模式,通过sigacton 调用注册一个 SIGIO 处理程序,当有数据到达后,系统向应用程序交付一个SIGIO 信号,应用 程序可以在信号处理程序中读取套接字数据。如下图所示: |
内核 |
应用 |
sigaction |
SIGIO |
read in signal handler |
拷贝数据到用户空间 |
return success |
处理数据 |
2 设置套接字工作于信号驱动I/O 模式 1)注册SIGIO 信号处理程序 2)使用fcntl 的F_SETOWN 命令,设置套接字所有者为当前进程。 3)使用fctnl 的F_SETFL 命令,设置O_ASYNC 标志,允许套接字使用信号 驱动I/O。 |
UDP 编程中可以使用信号驱动,此时SIGIO 产生于下面两种情况 1)套接字收到一个数据包 2)套接字上发生了异步错误 |
对于TCP 套接字,信号驱动I/O 就没有意义了,因为下面5 种情况都会产生 SIGIO,而且很难(有时甚至无法)区分是什么情况导致的: 1)监听套接字完成了一个连接请求 2)收到了一个断连请求 3)断连操作完成 4)套接字收到数据 5)有数据从套接字发出 |
3 程序示例 #include <stdio.h> |
#include <stdlib.h> #include <sys/socket.h> #include <signal.h> #include <string.h> #include <arpa/inet.h> #include <fcntl.h> #define MAXBUF 512 #define PORT 5001 int s; |
char buf[MAXBUF]; void do_sigio(int sig) { struct sockaddr_in cli; int len=sizeof cli; int n=recvfrom(s,buf,MAXBUF,0,(struct sockaddr*)&cli,&len); buf[n]=0; printf("%s from %s:%u\r\n",buf,inet_ntoa(cli.sin_addr),ntohs(cli.sin_port)); } int main() { s=socket(AF_INET,SOCK_DGRAM,0); struct sigaction act; memset(&act,0,sizeof(act)); act.sa_handler=do_sigio; act.sa_flags=0; int sret=sigaction(SIGIO,&act,NULL); if(sret==-1){printf("install signal error !\r\n");exit(-1);}; int r1=fcntl(s,F_SETOWN,getpid());//设置套接字的属主进程,为该jincfc int flag; int r2=fcntl(s,F_GETFL,&flag); flag|=O_ASYNC|O_NONBLOCK; int r3=fcntl(s,F_SETFL,flag);//设置该套接字为信号驱动模式和非阻塞模式 if(r1<0||r2<0||r3<0){printf("设置套接字属性错误!\r\n");exit(-1);}; |
struct sockaddr_in svr; inet_aton("0.0.0.0",&svr.sin_addr); svr.sin_port=htons(PORT); svr.sin_family=AF_INET; bind(s,(struct sockaddr*)&svr,sizeof svr); while(1) { } |
} |