Linux 网络编程的5种IO模型:信号驱动IO模型
Linux 网络编程的5种IO模型:信号驱动IO模型
背景
上一讲 Linux 网络编程的5种IO模型:多路复用(select/poll/epoll) 我们讲解了多路复用等方面的知识,以及有关例程。
这一讲我们来看 信号驱动IO 模型。
介绍
情景引入:
在信号驱动IO模型中,当用户线程发起一个IO请求操作,会给对应的socket注册一个信号函数,然后用户线程会继续执行,当内核数据就绪时会发送一个信号给用户线程,用户线程接收到信号之后,便在信号函数中调用IO读写操作来进行实际的IO请求操作。这个一般用于UDP中,对TCP套接口几乎是没用的,原因是该信号产生得过于频繁,并且该信号的出现并没有告诉我们发生了什么事情
sequenceDiagram
title : 信号驱动IO模型
participant application
participant kernel
Note right of application: 应用程序调用系统调用
application ->> kernel: signaction
kernel ->> application: 返回
kernel ->> application: 递交SIGIO信号
application ->> application : 信号处理
application ->> kernel : recvfrom
kernel ->> kernel: 准备好数据,拷贝到用户空间
kernel ->> application: 拷贝完成,返回成功
在UDP上,SIGIO信号会在下面两个事件的时候产生:
1 数据报到达套接字
2 套接字上发生错误
因此我们很容易判断SIGIO出现的时候,如果不是发生错误,那么就是有数据报到达了。
而在TCP上,由于TCP是双工的,它的信号产生过于频繁,并且信号的出现几乎没有告诉我们发生了什么事情。因此对于TCP套接字,SIGIO信号是没有什么使用的。
有关函数
#include <signal.h>
int sigaction(int signum, const struct sigaction *act,
struct sigaction *oldact);
关于有关内容的讲解,请参考:Linux 系统编程 学习:进程间通信-Unix IPC-信号
例程
这对例程是不太规范的,因为有BUG。但因为这种消息模型用的比较少所以我就不改了。
server.c
#include <stdio.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <fcntl.h>
int listenfd1;
volatile int read_flag = 0;
static char buf[256] = { 0 };
void do_sigio(int sig)
{
struct sockaddr_in cli_addr;
int clifd, clilen;
read_flag = 1;
memset(buf, 0, sizeof(buf));
recvfrom(listenfd1, buf, sizeof(buf), 0, (struct sockaddr *)&cli_addr, &clilen);
printf("Listenfd1 Message %s \n", buf);
perror("recvfrom");
sendto(listenfd1, "Reply", sizeof("Reply"),0, (struct sockaddr *)&cli_addr, sizeof(cli_addr));
perror("sendto");
printf("sigio end\n");
read_flag = 0;
}
int main(int argc, char *argv[])
{
//绑定监听7779端口的fd
struct sockaddr_in serv_addr;
listenfd1 = socket(AF_INET, SOCK_DGRAM, 0);
bzero((char *) &serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(7779);
serv_addr.sin_addr.s_addr = INADDR_ANY;
struct sigaction sigio_action;
memset(&sigio_action, 0, sizeof(sigio_action));
sigio_action.sa_flags = 0;
sigio_action.sa_handler = do_sigio;
sigaction(SIGIO, &sigio_action, NULL);
fcntl(listenfd1, F_SETOWN, getpid());
int flags;
flags = fcntl(listenfd1, F_GETFL, 0);
flags |= O_ASYNC ;//| O_NONBLOCK;
fcntl(listenfd1, F_SETFL, flags);
bind(listenfd1, (struct sockaddr *) &serv_addr, sizeof(serv_addr));
while(1)
{
sleep(2);
}
close(listenfd1);
return 0;
}
client.c
#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <string.h>
#include <unistd.h>
int main(int argc, char* argv[])
{
int socketfd;
socklen_t n;
socketfd = socket(AF_INET, SOCK_DGRAM, 0);
struct sockaddr_in serv_addr;
struct sockaddr_in addr;
bzero((char *)&serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(7779);
char buf[64] = {0};
//write(socketfd, "client message", sizeof("client message"));
sendto(socketfd, "client message", sizeof("client message"),0, (struct sockaddr *)&serv_addr, sizeof(serv_addr));
memset(buf, 0, sizeof(buf));
//read(socketfd, buf, sizeof(buf));
recvfrom(socketfd, buf, sizeof(buf), 0, (struct sockaddr *)&serv_addr, &n);
printf("%u %s\n",n ,buf);
return 0;
}
附录:异步通知
ref :异步通知
注意:异步通知只有SIGIO信号,没有别的信号可用,其他各种信号在app空间可以任意使用.
通过使用异步通知,应用程序可以在数据可用时收到一个信号,而无需不停地轮询。
启用步骤:
(1)它们指定一个进程作为文件的拥有者:使用 fcntl 系统调用发出 F_SETOWN 命令,这个拥有者进程的 ID 被保存在 filp->f_owner。目的:让内核知道信号到达时该通知哪个进程。
(2)使用 fcntl 系统调用,通过 F_SETFL 命令设置 FASYNC 标志。
内核操作过程
1.F_SETOWN被调用时filp->f_owner被赋值。
当 F_SETFL 被执行来打开 FASYNC, 驱动的 fasync 方法被调用.这个标志在文件被打开时缺省地被清除。
当数据到达时,所有的注册异步通知的进程都会被发送一个 SIGIO 信号。
Linux 提供的通用方法是基于一个数据结构和两个函数,定义在。
数据结构:
struct fasync_struct{
int magic;
int fa_fd;
struct fasync_struct *fa_next;/* singly linked list */
struct file *fa_file;
};
驱动调用的两个函数的原型:
int fasync_helper(int fd,structfile*filp,int mode, struct fasync_struct**fa);
void kill_fasync(struct fasync_struct**fa,int sig, int band);
当一个打开的文件的FASYNC标志被修改时,调用 fasync_helper 来从相关的进程列表中添加或去除文件。除了最后一个参数, 其他所有参数都时被提供给 fasync 方法的相同参数并被直接传递。 当数据到达时,kill_fasync 被用来通知相关的进程,它的参数是被传递的信号(常常是 SIGIO)和 band(几乎都是 POLL_IN)。
这是 scullpipe 实现 fasync 方法的:
staticint scull_p_fasync(int fd,struct file*filp,int mode)
{
struct scull_pipe *dev = filp->private_data;
return fasync_helper(fd, filp, mode,&dev->async_queue);
}
当数据到达, 下面的语句必须被执行来通知异步读者. 因为对 sucllpipe 读者的新数据通过一个发出 write 的进程被产生, 这个语句出现在 scullpipe 的 write 方法中:
if (dev->async_queue) kill_fasync(&dev->async_queue, SIGIO, POLL_IN); /* 注意, 一些设备也针对设备可写而实现了异步通知,在这个情况,kill_fasnyc 必须以 POLL_OUT 模式调用.*/
当文件被关闭时必须 调用fasync 方法,来从活动的异步读取进程列表中删除该文件。尽管这个调用仅当 filp->f_flags 被设置为 FASYNC 时才需要,但不管什么情况,调用这个函数不会有问题,并且是普遍的实现方法。 以下是 scullpipe 的 release 方法的一部分:
/* remove this filp from the asynchronously notified filp's */ scull_p_fasync(-1, filp, 0);
异步通知使用的数据结构和 struct wait_queue 几乎相同,因为他们都涉及等待事件。区别异步通知用 struct file 替代 struct task_struct. 队列中的 file 用获取 f_owner, 一边给进程发送信号。
Linux 网络编程的5种IO模型:信号驱动IO模型的更多相关文章
- Linux 网络编程的5种IO模型:多路复用(select/poll/epoll)
Linux 网络编程的5种IO模型:多路复用(select/poll/epoll) 背景 我们在上一讲 Linux 网络编程的5种IO模型:阻塞IO与非阻塞IO中,对于其中的 阻塞/非阻塞IO 进行了 ...
- Linux 网络编程的5种IO模型:阻塞IO与非阻塞IO
背景 整理之前学习socket编程的时候复习到了多路复用,搜索了有关资料,了解到多路复用也有局限性,本着打破砂锅问到底的精神,最终找到了关于IO模型的知识点. 在<Unix网络编程>一书中 ...
- Linux 网络编程的5种IO模型:异步IO模型
Linux 网络编程的5种IO模型:异步IO模型 资料已经整理好,但是还有未竟之业:复习多路复用epoll 阅读例程, 异步IO 函数实现 背景 上一讲< Linux 网络编程的5种IO模型:信 ...
- 网络编程并发 多进程 进程池,互斥锁,信号量,IO模型
进程:程序正在执行的过程,就是一个正在执行的任务,而负责执行任务的就是cpu 操作系统:操作系统就是一个协调.管理和控制计算机硬件资源和软件资源的控制程序. 操作系统的作用: 1:隐藏丑陋复杂的硬件接 ...
- 【网络IO系列】IO的五种模型,BIO、NIO、AIO、IO多路复用、 信号驱动IO
前言 在上一篇文章中,我们了解了操作系统中内核程序和用户程序之间的区别和联系,还提到了内核空间和用户空间,当我们需要读取一条数据的时候,首先需要发请求告诉内核,我需要什么数据,等内核准备好数据之后 , ...
- 《Linux/UNIX系统编程手册》第63章 IO多路复用、信号驱动IO以及epoll
关键词:fasync_helper.kill_async.sigsuspend.sigaction.fcntl.F_SETOWN_EX.F_SETSIG.select().poll().poll_wa ...
- 【死磕NIO】— 阻塞IO,非阻塞IO,IO复用,信号驱动IO,异步IO,这你真的分的清楚吗?
通过上篇文章([死磕NIO]- 阻塞.非阻塞.同步.异步,傻傻分不清楚),我想你应该能够区分了什么是阻塞.非阻塞.异步.非异步了,这篇文章我们来彻底弄清楚什么是阻塞IO,非阻塞IO,IO复用,信号驱动 ...
- Linux 网络编程(IO模型)
针对linux 操作系统的5类IO模型,阻塞式.非阻塞式.多路复用.信号驱动和异步IO进行整理,参考<linux网络编程>及相关网络资料. 阻塞模式 在socket编程(如下图)中调用如下 ...
- linux网络编程IO模型
同步与异步: 同步就是一个任务的完成需要依赖另外一个任务时,只有等待被依赖的任务完成后,依赖的任务才能算完成. 异步是不需要等待被依赖的任务完成,只是通知被依赖的任务要 ...
随机推荐
- 微信小程序-实现文字跑马灯-wepy
百度蛮多例子的,但是代码太长懒得看了 前言 要实现跑马灯主要就是获得判断开始定界和结束定界, 1.9.3新增的wxml操作接口 就可以拿到节点长宽等属性,当然你也可以直接用 文字数量 * 文字大小(注 ...
- 实验 5:OpenFlow 协议分析和 OpenDaylight 安装
一.实验目的 回顾 JDK 安装配置,了解 OpenDaylight 控制的安装,以及 Mininet 如何连接;通过抓包获取 OpenFlow 协议,验证 OpenFlow 协议和版本,了解协议内容 ...
- sublime text3配置Python2、Python3的编译环境
由于Python2.Python3使用量都很高,Python3虽然是未来趋势,但是目前个别库还是只支持Python2.所以,很多人会选择在电脑上安装两个版本的Python,那么使用sublime执行代 ...
- c++中的GetModuleFileName函数的用法以及作用
参考: 1. http://blog.sina.com.cn/s/blog_b078a1cb0101fw48.html 2. https://www.cnblogs.com/Satu/p/820393 ...
- CentOS之—双网卡双IP双网关配置
转载请注明出处:http://blog.csdn.net/l1028386804/article/details/77487639 一.配置讲解 1.配置DNS 修改对应网卡的DNS的配置文件 # v ...
- 多测师讲解python _unttest框架002(结合项目实战)_高级讲师肖sir
第一种调用方法: if __name__ == '__main__':# #第一种运行方法:运行所有的用例 import unittestfrom selenium import webdriverf ...
- Rust之路(4)——所有权
[未经书面同意,严禁转载] -- 2020-10-14 -- 所有权是Rust的重中之重(这口气咋像高中数学老师 WTF......). 所有权是指的对内存实际存储的数据的访问权(包括读取和修改),在 ...
- 【手摸手,带你搭建前后端分离商城系统】02 VUE-CLI 脚手架生成基本项目,axios配置请求、解决跨域问题
[手摸手,带你搭建前后端分离商城系统]02 VUE-CLI 脚手架生成基本项目,axios配置请求.解决跨域问题. 回顾一下上一节我们学习到的内容.已经将一个 usm_admin 后台用户 表的基本增 ...
- 干货分享:用一百行代码做一个C/C++表白小程序,程序员的浪漫!
前言:很多时候,当别人听到你是程序员的时候.第一印象就是,格子衫.不浪漫.直男.但是程序员一旦浪漫起来,真的没其他人什么事了.什么纪念日,生日,情人节,礼物怎么送? 做一个浪漫的程序给她,放上你们照片 ...
- msyql查看连接数
连接数 SHOW FULL PROCESSLIST 1. 查看允许的最大并发连接数 SHOW VARIABLES LIKE 'max_connections'; 2. 修改最大连接数 方法1:临时 ...