结合阻塞与非阻塞访问、poll 函数可以较好地解决设备的读写,但是如果有了异步通知就更方便了。异步通知的意思是:一旦设备就绪,则主动通知应用程序,这样应用程序根本就不需要查询设备状态,这一点非常类似于硬件上“中断”地概念,比较准确的称谓是:信号驱动(SIGIO)的异步 I/O。可以使用signal()函数来设置对应的信号的处理函数。函数原型是:

void (*signal(int signo,void (*func)(int))) (int)

我们先来看一个使用信号驱动的例子,通过signal(SIGIO,input_handler) 对打开的文件fd 启动信号机制,输入可获得时inputhandler被调用,代码如下:

/*async_io_app.c*/

#include #include#include#include#include#include#include#define MAX_LEN 100 intfd;void input_handler(intnum)

{chardata[MAX_LEN];intlen;//读取并输出 STDIN_FILENO 上的输入 len= read(fd, &data, MAX_LEN);

data[len]= 0;

printf("input available:%s\n", data);

}intmain()

{intoflags;//启动信号驱动机制 fd= open("/dev/CDEV_ZHU", O_RDWR, S_IRUSR |S_IWUSR);if(fd == -1)

{

printf("Device Open Failure !\n");

exit(0);

}

signal(SIGIO, input_handler);

fcntl(fd, F_SETOWN, getpid());

oflags=fcntl(fd, F_GETFL);

fcntl(fd, F_SETFL, oflags|FASYNC);//最后进入一个死循环,程序什么都不干了,只有信号能激发 input_handler 的运行//如果程序中没有这个死循环,会立即执行完毕 while (1);return 0;

}

下面来解释一下上面的代码。为了一个用户在用户空间中能处理一个设备释放的信号,它必须完成一下3份工作:

1)通过F_SETOWN控制指令设置设备文件的拥有者为本进程,这样从设备驱动中发出的信号才能被本进程收到。

2)通过F_SETFL 控制命令设置设备文件支持FASYNC,即异步通知模式。

3)通过signal()链接信号和信号处理函数。

有了信号的发送,那么就一定得有信号的释放了:

在设备驱动和应用程序的异步通知交互中,仅仅在应用程序端捕获信号是不够的,因为信号没有的源头是在驱动端,因此要在适当的时机让设备驱动释放信号。

为了使设备支持异步通知机制,驱动程序中涉及三个操作:

1)支持F_SETOWN命令,能在这个控制命令处理中设置filp->f_owner为对应的进程ID。不过此项工作已由内核完成,设备驱动无须处理。

2)支持F_SETFL命令的处理,每当FASYNC标志改变时,驱动程序中fasync()函数将得以进行。因此,驱动程序必须实现fasync()函数。

3)在设备资源可获得时,调用kill_fasync()函数激发相应的信号。

驱动程序中上面的三步是和应用程序是一一对应的。如下图:

设备驱动中异步通知编程还是比较简单的,主要就是一些数据结构,和两个函数:

数据结构:fasync_struct结构体

函数:1)处理FASYNC标志变更的函数int fasync_helper(int fd, struct file *filp, int mode ,struct fasync_struct **fa);

2) 释放信号用的函数void kill_fasync(struct fasync_struct **fa, int sig, int band);

和其他设备驱动一样,一般将fasync_struct放到设备结构体中。

下面给出驱动程序部分实现支持异步IO的代码:

/* async_io_driver.c */

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

MODULE_LICENSE("GPL");

#define LEN  30

#define init_MUTEX(LOCKNAME) sema_init(LOCKNAME,1)

#define DEVICE_NAME  "CDEV_ZHU"

static struct class *cdev_class;

struct asycIO

{

struct cdev dev_c; /*cdev结构体*/

dev_t  dev;

char  mem[LEN];

int   flag ;

struct semaphore sem; /*并发控制用的信号量*/

wait_queue_head_t r_wait; /*阻塞读用的等待队列头*/

struct fasync_struct *async_queue; /* 异步结构体指针,用于读 */

};

struct asycIO  asyc_device;

static int asyc_io_fasync(int fd, struct file *filp, int mode)

{

return fasync_helper(fd, filp, mode, &asyc_device.async_queue);

}

/*文件释放函数*/

int asyc_io_release(struct inode *inode, struct file *filp)

{

/* 将文件从异步通知列表中删除 */

asyc_io_fasync( - 1, filp, 0);

return 0;

}

/*写操作*/

static ssize_t asyc_write(struct file *filp, const char __user *buf,size_t count, loff_t *ppos)

{

int ret = count;

printk("In asyc_write! \n");

down(&asyc_device.sem);  //获取信号量

memset(asyc_device.mem,0,LEN);

if (copy_from_user(asyc_device.mem, buf, count))

{

up(&asyc_device.sem);

return    - EFAULT;

}

printk("kernel recieve: %s  and the length is %d \n",asyc_device.mem,count);

up(&asyc_device.sem);

asyc_device.flag = 1;

if (asyc_device.async_queue)

kill_fasync(&asyc_device.async_queue, SIGIO, POLL_IN);

wake_up_interruptible(&asyc_device.r_wait);

return ret;

}

static ssize_t asyc_read(struct file *filp, char *buf, size_t len, loff_t *off)

{

int ret = len;

printk("In asyc_read \n");

if (wait_event_interruptible(asyc_device.r_wait, asyc_device.flag != 0))

{

return    - ERESTARTSYS;

}

if (down_interruptible(&asyc_device.sem))

{

return    - ERESTARTSYS;

}

asyc_device.flag = 0;

if (copy_to_user(buf, asyc_device.mem, len))

{

up(&asyc_device.sem);

return    - EFAULT;

}

up(&asyc_device.sem);

return ret;

}

struct file_operations asyc_fops =

{

read: asyc_read,

write: asyc_write,

fasync: asyc_io_fasync,

release: asyc_io_release,

};

static int __init asyc_init(void)

{

int ret,err;

ret = alloc_chrdev_region(&(asyc_device.dev),0,1,DEVICE_NAME) ;

if (ret)

{

printk("globalvar register failure");

}

else

{

cdev_init(&(asyc_device.dev_c),&asyc_fops);

err = cdev_add(&(asyc_device.dev_c),asyc_device.dev,1);

if(err)

{

printk(KERN_NOTICE "error %d adding FC_dev\n",err);

unregister_chrdev_region(asyc_device.dev, 1);

return err;

}

else

{

printk("device register success! \n");

}

cdev_class = class_create(THIS_MODULE,DEVICE_NAME);

if(IS_ERR(cdev_class))

{

printk("ERR:cannot create a cdev_class\n");

unregister_chrdev_region(asyc_device.dev, 1);

return -1;

}

device_create(cdev_class, NULL, asyc_device.dev, 0, DEVICE_NAME);

asyc_device.flag = 0;

init_MUTEX(&(asyc_device.sem));

init_waitqueue_head(&(asyc_device.r_wait));

}

return ret;

}

static void __exit asyc_exit(void)

{

device_destroy(cdev_class,asyc_device.dev);

class_destroy(cdev_class);

unregister_chrdev_region(asyc_device.dev,1);

printk(" device exit! \n");

}

module_init(asyc_init);

module_exit(asyc_exit);

应用程序实现写入功能:

/*async_io_app_w.c*/

#include

#include

#include

#include

#include

int main()

{

int fd, num;

char buffer[100] = {0};

fd = open("/dev/CDEV_ZHU", O_RDWR, S_IRUSR | S_IWUSR);

printf("open /dev/CDEV_ZHU fd = %d \n",fd);

if (fd != -1)

{

while (1)

{

memset(buffer,0,sizeof(buffer));

printf("Please input the buffer:\n");

scanf("%s", buffer);

if (buffer[0] == '0') //如果输入 0,退出

{

close(fd);

break;

}

write(fd, buffer, strlen(buffer));

printf("We have written: %s\n",buffer);

}

}

else

{

printf("device open failure\n");

}

return 0;

}

将上面的“async_io_app.c”、“async_io_driver.c”、“async_io_app_w.c”进行编译,加载驱动之后,开两个终端,分别运行async_io_app 和 async_io_app_w,当async_io_app_w有数据写入的时候,async_io_app的终端会打印所写入的数据,当然内核也会打印数据,下面是结果:

说明:上面图是三个终端的打印结果,从左到右一次是async_io_app_w , async_io_app 和使用dmesg 打印内核的结果。

注:我本来也想用代码格式,但是感觉在vim上排版很舒服的,上来用代码格式反而还不好看了,于是就这样了

python 网络编程 异步io_异步IO实现 小例(程序+驱动程序)相关推荐

  1. python网络编程实战_Python 异步网络编程实战

    近年来 Python 的发展的非常迅速,"简单"."高效"是 Python 吸引人的一大特色.在国内 Python 开发需求越来越大,Python 具有丰富强大 ...

  2. python网络编程基础(线程与进程、并行与并发、同步与异步、阻塞与非阻塞、CPU密集型与IO密集型)...

    python网络编程基础(线程与进程.并行与并发.同步与异步.阻塞与非阻塞.CPU密集型与IO密集型) 目录 线程与进程并行与并发同步与异步阻塞与非阻塞CPU密集型与IO密集型 线程与进程 进程 前言 ...

  3. python网络编程知识点_python 网络编程要点

    From http://www.zhihu.com/question/19854853 Python网络编程是一个很大的范畴,个人感觉需要掌握的点有: 1. 如何使用Python来创建socket, ...

  4. Linux io模型及函数调用,Linux 网络编程的5种IO模型:信号驱动IO模型

    Linux 网络编程的5种IO模型:信号驱动IO模型 背景 这一讲我们来看 信号驱动IO 模型. 介绍 情景引入: 在信号驱动IO模型中,当用户线程发起一个IO请求操作,会给对应的socket注册一个 ...

  5. python网络编程内容_图解Python网络编程

    Python Python开发 Python语言 图解Python网络编程 本篇索引 (1)基本原理 本篇指的网络编程,仅仅是指如何在两台或多台计算机之间,通过网络收发数据包:而不涉及具体的应用层功能 ...

  6. 读书笔记 - -《Python网络编程》重点

    文章目录 一.前言 二.客户/服务器网络编程简介 三.UDP 3.1 端口号 3.2 套接字 3.3 UDP分组 3.4 小结 四.TCP 4.1 TCP工作原理 4.2 绑定接口 4.3 死锁 4. ...

  7. python网络编程基础百度云_PYTHON网络编程基础 PDF 下载

    相关截图: 资料简介: <Python网络编程基础>全面介绍了使用Python语言进行网络编程的基础知识,主要内容包括网络基础知识.高级网络操作.Web Services.解析HTML和X ...

  8. python网络编程证书_《Python网络编程基础》笔记

    python网络编程基础 ================== Author: lujun9972 Date: 2013-03-08 22:29:20 CST Table of Contents == ...

  9. Python网络编程(Socket)

    Python网络编程(Socket) Python提供了两个访问级别的网络服务.在一个较低的水平,您可以访问底层操作系统的基本套接字支持,允许你实现面向连接和无连接协议的客户端和服务器 Python有 ...

  10. python网络编程难点_python之路网络编程总结(三)

    2018-9-22 20:58:25 1. 端口 1.1知名端口是众所周知的端口,范围从0-1023 例: 80端口分配给HTTP服务 21 端口分配给FTP服务 1.2动态端口 : 范围从1024 ...

最新文章

  1. 纯css+html实现发光伸缩卡片
  2. bootcamp空间不足_Bootcamp安装WinXP补丁时C盘空间不足怎么办?
  3. 浏览器窗口控制---使用localStorage
  4. java多文件post请求_如何使用Java发出多部分/表单数据POST请求?
  5. 并发、并行、线程、进程与CPU基本概念
  6. Flask 蓝图机制及应用
  7. NoSQL之【MongoDB】学习(三):配置文件说明
  8. LTE基本结构(常见接口)
  9. oracle 11g查隐含参数,oracle隐含参数修改与查看
  10. 条件随机场介绍(2)—— An Introduction to Conditional Random Fields
  11. win7设置无线wifi连接到服务器,自动连接wifi怎么设置_如何设置无线网自动连接...
  12. CC00388.CloudKubernetes——|KuberNetesCI/CD.V26|——|Jenkins.v06|自动构建Java应用.v06|报错处理|
  13. Python之校庆代码
  14. tic/toc/cputime测试时间的区别
  15. 开源免费,最好用的3大系统9大防火墙软件安利给你们
  16. Angular:解决innerHTML绑定页面内容,sanitizing HTML stripped some content警告处理和富文本背景色样式无法正常显示的问题
  17. 《惢客创业日记》2021.08.06-09(周五)惢客与征信的区别(下)
  18. ldpc译码讲解_LDPC码及其译码实现
  19. ZIGBEE学习之---ZSTACK1.4.3修改密钥(CC2430)
  20. 移动互联网思维的5F法则

热门文章

  1. groovy+mysql数据库_使用Groovy连接到MySQL
  2. android中给button加图标,Android,如何在我的代码中的TabButton中添加图标
  3. matlab安装无效距离过远,求助matlab的远程序
  4. oracle常用用户权限,oracle创建新用户及授予常用权限
  5. linux文件名过长无法删除,不能删除文件,出现“源文件名长度大于系统支持的长度...
  6. dbeaver无法修改表数据_隐藏彩蛋:你知道python有一个内置的数据库吗?
  7. LeetCode算法入门- Search Insert Position -day19
  8. 信号与系统实验:信号抽样
  9. (递归)斐波那契数列
  10. android xml怎么建立,androidXmlSerializer创建XML文件