信号驱动式l/0的本质就是
:进程预先告知内核当某个描述符发生事件时,内核会向该进程发送SIGIO信 号量通知进程,进程可在信号处理函数中进行处理。
进程可以通过fcntl打开O_ ASYNC标志
或ioctl打开FIOASYNC标志
来通知内核,二者的区别是一些系统不支持fcntl,所以应尽量使用ioctl。
对于socket产生SIGIO的条件:
TCP套接字:
1.监听套接字上有新连接请求完成
2某个断连请求发起
3.某个断连请求完成
4.数据到达套接字
5数据已从套接字发送走(输出缓冲区有空闲空间)
6.发生某个异步错误
UDP套接字:
1.数据报到达套接字
2.套接字上发生异步错误
对于套接字而言: TCP套接字和UDP套接字致使内核产生SIGIO信号的条件有所不同,其中TCP可产生该信号的条件较多,而UDP套接字产生该信号的条件只有两个,因为我们无法得知具体是什么事件导致内核产生该信号。对于UDP套接字产生该信号条件的判断就简单的多,这也是信号驱动式l/0主要用于UDP套接字的原因
。
套接字使用信号驱动式I/O的步骤:
1、建立SIGIO信号处理函数
2、设置设置套接字属主(使用fcnt的F_ SETOWN命令)
3、开启信号驱动式/O(fcnt打开O_ ASYNC或ioct打T开FIOASYNC)
代码如下:
net_info.h
#ifndef _NET_INFO_H
#define _NET_INFO_H
#define RCV_LEN 1024
#define LISTEN_PORT 9999
#endif
udp_client.c
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <time.h>
#include <string.h>
#include <sys/select.h>
#include <sys/time.h>
#include <errno.h>
#include <signal.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <net/if.h>
#include <net/if_arp.h>
#include <sys/ioctl.h>
#include <sys/ioctl.h>
#include "net_info.h"
int main()
{
int skfd;
skfd = socket(AF_INET, SOCK_DGRAM, 0);
if (skfd < 0)
{
perror("socket error");
exit(-1);
}
int ret;
time_t tm;
struct sockaddr_in desAddr;
bzero(&desAddr, sizeof(desAddr));
desAddr.sin_family = AF_INET;
desAddr.sin_port = htons(LISTEN_PORT);
desAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
while (1)
{
time(&tm);
ret = sendto(skfd, ctime(&tm), strlen(ctime(&tm)), 0,
(struct sockaddr*)&desAddr, sizeof(desAddr));
if (ret < 0)
{
perror("");
exit(-1);
}
sleep(2);
}
return 0;
}
udp_server.c
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <time.h>
#include <string.h>
#include <sys/select.h>
#include <sys/time.h>
#include <errno.h>
#include <signal.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <net/if.h>
#include <net/if_arp.h>
#include <sys/ioctl.h>
#include <sys/ioctl.h>
#include "net_info.h"
static void sigio_handler(int signo);
void sigio_socket_init(int skfd);
char recvBuf[RCV_LEN];
int listenfd;
int create_socket()
{
int skfd = socket(AF_INET, SOCK_DGRAM, 0);
if (skfd < 0)
return -1;
struct sockaddr_in saddr;
bzero(&saddr, sizeof(saddr));
saddr.sin_family = AF_INET;
saddr.sin_port = htons(LISTEN_PORT);
saddr.sin_addr.s_addr = htonl(INADDR_ANY);
if (bind(skfd, (struct sockaddr*)&saddr, sizeof(saddr)) < 0)
return -1;
return skfd;
}
int main()
{
int run_cnt = 0;
listenfd = create_socket();
if (listenfd < 0)
{
perror("prepare error");
exit(-1);
}
sigio_socket_init(listenfd);
while(1)
{
printf("run_cnt=%d\n",run_cnt++);
sleep(1);
}
return 0;
}
void sigio_socket_init(int skfd)
{
int ret = 0;
//1.建立SIGIO信号的处理函数
signal(SIGIO, sigio_handler);
//2.设置该套接字的属主
ret = fcntl(skfd, F_SETOWN, getpid());
if (ret < 0)
{
perror("fcntl error");
exit(-1);
}
//3.开启该套接字的信号驱动式I/O
int on = 1;
ret = ioctl(skfd, FIOASYNC, &on);
if (ret < 0)
{
perror("ioctl error");
exit(-1);
}
}
static void sigio_handler(int signo)
{
if (signo != SIGIO)
return;
printf("recv SIGIO(%d) from kernel\n",signo);
int i = 0;
for(i = 0; i < 3; i++){
int ret = recvfrom(listenfd, recvBuf, sizeof(recvBuf),0, NULL,NULL);//当内核数据没有准备好,进程继续阻塞
if (ret < 0)
{
if (errno == EWOULDBLOCK)
return ;
else
{
perror("recvfrom error");
exit(-1);
}
}
printf("recv = %s\n",recvBuf);
}
}
执行结果:
五种网络IO模型-阻塞I/O、非阻塞I/O、I/O多路复用、信号驱动I/O、异步I/O
推荐博客
(1)
https://blog.csdn.net/weixin_30920513/article/details/95158227
(2)
https://www.cnblogs.com/shengguorui/p/11949282.html