-
IO模型: 一种通信的模型
IO, input output 其实就是 ###通信####为了解决#通信#过程中,出现的问题. -
IO模型: 网络编程
-
阻塞
一个运行的进程 去访问资源,发现资源不满足,于是进程 ## 主动睡眠## 等待资源满足,被唤醒.当资源(一般伴随中断)到来的时候, (中断)去唤醒等待的进程.##睡眠###,不消耗
- 定义并初始化一个##等待队列###
- 运行的进程发现资源不满足, 主动将自己挂载到等待队列上去. 然后让内核重新调度.
- 某个执行单元将数据送达, 然后唤醒等待队列上的进程.
a = {b=3;c=4; b+c};
- 非阻塞
进程去访问资源,发现资源不满足, 进程立马去做其他任务, 通过轮训的方式,不断地检查资源.内核如何支持 非阻塞呢???
struct file {
unsigned
int f_flags; //有32个bit, 每一个bit表示一种功能. 可读 可写 可执行 权限 ....其中,有一个bit 表示改文件 当前是 阻塞(0)还是非阻塞(1).一个文件默认是阻塞.
};
- 异步通知
linux系统中,异步通知使用 #信号#来实现.当内核 有数据之后, 主动向用户程序发送一个信号, app得到信号之后,再去内核访问数据.
故事1:老王烧开水。
出场人物:老王,水壶两把(普通水壶,简称水壶;会响的水壶,简称响水壶)。老王想了想,有好几种等待方式
1.老王用水壶煮水,并且站在那里,不管水开没开,每隔一定时间看看水开了没。 -同步阻塞
2.老王还是用水壶煮水,不再傻傻的站在那里看水开,跑去寝室上网,但是还是会每隔一段时间过来看看水开了没有,水没有开就走人。 -同步非阻塞
3.老王这次使用高大上的响水壶来煮水,站在那里,但是不会再每隔一段时间去看水开,而是等水开了,水壶会自动的通知他。 -异步阻塞
4.老王还是使用响水壶煮水,跑到客厅上网去,等着响水壶自己把水煮熟了以后通知他。 -异步非阻塞
故事2::在网上买了个东西,然后就等东西送到快递去取了。就有这4种情况:
你立刻傻站在快递门口等东西来
你继续做你要做的事,不会因为会有包裹影响正常生活该做的
东西邮到给你发短信
东西邮到没人吭声通知你
阻塞(因为会有包裹,你就专门等包裹来,包裹不到你就在快递门口罢工!绝食!)
非阻塞(不会因为有包裹你就影响你正常生活和该做的工作)
异步(人家很敬业的通知你)
同步(暴走大世界里黑ems邮件堆成山却不通知人取就是这种情况啦)
然后排列组合,又有了这四种情况:
1.同步阻塞:包裹来了不通知你,你就傻站在快递门口等包裹来了
2.同步非阻塞(轮询):你该干啥干啥,虽然人家不通知你,但你时不时的跑去快递问问我包裹到了没.至于你是每天中午问呢还是每隔一小时就跑去问一次,那就看你有多能墨迹了
3.异步阻塞:虽然人家会短信通知你,但你偏要在快递门口傻站着等包裹到.有时间,就是这么任性!但我估计程序猿应该没谁会写出这么任性的代码吧?
4.异步非阻塞(回调):其实第一次听说要在快递外面等邮件我是拒绝的,因为,你不能让我搁下所有工作,我就马上去快递外面等快递……等了一阵时间,手机,DUANGDUANGDUANG~~我的快递到快递里了
- 非阻塞IO中可以使用自旋锁、原子变量
- 阻塞IO中可以使用互斥体
简单实例
- chdev.c
/*****************************************************************
* Copyright (C) 2019 Sangfor Ltd. All rights reserved.
*
* 文件名称:chdev.c
* 创 建 者:yinfei-hu
* 创建日期:2019年03月07日 星期4 20时22分05秒
* 功能描述:操作LED GPIO
*
*****************************************************************/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <linux/sched.h>
#include "comm.h"
/*
############# io 模型 #####################
阻塞
1. 定义并初始化 一个等待队列
wait_queue_head_t waitqhead;
void init_waitqueue_head(wait_queue_head_t *q)
2. 运行的进程 发现资源不满足, 主动将自己 挂载到 等待队列上去.
然后让内核重新调度.
进程主动调用该 宏, 该进程 在 宏内部 阻塞/睡眠
不在返回. 当被唤醒的时候,该函数/进程 返回.
wait_event_interruptible(wait_queue_head_t wq,
int condition)
3. 某个执行单元 将数据送达, 然后唤醒等待队列上的 进程.
void wake_up_interruptible(wait_queue_head_t *wq)
非阻塞
实现逻辑
如果用户要求 文件时非阻塞的, 并且当前没有数据,则 立马返回-EAGAIN.
异步通知
1.定义 struct fasync_struct *fapp;
2.实现 fops.fasync 方法
3.发信号
kill_fasync(&pt_gstruct->fapp,SIGIO,POLL_IN)
*/
#define GPX2CON 0x11000c40
#define GPX2DAT 0x11000c44
struct global_struct {
struct class *cls;
int major,minor;
/* 字符设备驱动对象 */
struct cdev chdevobj; //inode->i_cdev
void *config;
void *data;
wait_queue_head_t waitqhead; //定义一个等待队列
int data_flags; //数据有和无的标志, 0-无 1-有
char buf[128];
struct fasync_struct *fapp; //异步通知的实现
};
/* 全局变量 */
struct global_struct gstruct;
int led_init(void)
{
int val;
gstruct.config = ioremap(GPX2CON,4);
gstruct.data = ioremap(GPX2DAT,4);
val = readl(gstruct.config);
val = val & (~0xF<<28);
val = val | 1<<28;
writel(val ,gstruct.config);
//关灯
val = readl(gstruct.data);
val = val & ~(1<<7);
writel(val,gstruct.data);
return 0;
}
int led_ctrl(int on )
{
int val;
if(on){
val = readl(gstruct.data);
val = val| (1<<7);
writel(val,gstruct.data);
}else {
//关灯
val = readl(gstruct.data);
val = val & ~(1<<7);
writel(val,gstruct.data);
}
return 0;
}
int led_deinit(void)
{
int val;
//关灯
val = readl(gstruct.data);
val = val & ~(1<<7);
writel(val,gstruct.data);
return 0;
}
/*
open, 一般执行对硬件进行初始化的动作
*/
int chdev_open(struct inode *inode, struct file *file)
{
struct global_struct *pt_gstruct ;
printk("%s->%d\n",__FUNCTION__,__LINE__);
/*inode->i_cdev 记录了 该文件节点所对应的 字符设备对象的地址
一个大结构体变量gstruct, 如果已知其中一个 成员的地址&chdevobj,
如何求取大结构体变量 gstruct 的地址呢..... 成员地址减去 成员前面的大小
bigstruct *pt_gstruct =
container_of(little_member_addr,大结构体类型,小成员的变量名)
*/
pt_gstruct = container_of(inode->i_cdev ,struct global_struct,chdevobj);
// pt_gstruct ====== &gstruct
file->private_data = pt_gstruct;
led_init();
/*成功, 返回0
失败, 返回一个负数, 记录 数值不能乱写,要根据 内核的错误宏定义
*/
return 0;
}
/*
close, 一般执行对硬件进行 去初始化的动作
*/
int chdev_close (struct inode *inode, struct file *file)
{
struct global_struct *pt_gstruct = file->private_data;
printk("%s->%d major%d minor%d\n",__FUNCTION__,__LINE__,pt_gstruct->major,pt_gstruct->minor);
led_deinit();
/*成功, 返回0
失败, 返回一个负数, 记录 数值不能乱写,要根据 内核的错误宏定义
*/
return 0;
}
ssize_t chdev_write(struct file *file, const char __user *usr, size_t size, loff_t *loff)
{
int ret;
struct global_struct *pt_gstruct = file->private_data;
printk("%s->%d major%d minor%d\n",__FUNCTION__,__LINE__,
pt_gstruct->major,pt_gstruct->minor);
/* *usr来自用户空间, 可能是null,或者 非法地址
如果不加判断,内核直接使用, 会造成内核 异常
我们一般使用 安全版本的 memcpy ,copy_from_user
返回 实际尚未拷贝的字节数, 你要求拷贝100,实际拷贝89,返回11
*/
ret = copy_from_user(pt_gstruct->buf,usr,size);
if(ret ){
printk("%s->%d copy_from_user err\n",__func__,__LINE__);
return -16;
}
printk("%s->%d get buf:%s\n",__func__,__LINE__,pt_gstruct->buf);
pt_gstruct->data_flags = 1;
wake_up_interruptible(&pt_gstruct->waitqhead);
//异步通知,发送信号
kill_fasync(&pt_gstruct->fapp,SIGIO,POLL_IN);
/*成功, 返回实际拷贝的大小
失败, 返回一个负数, 记录 数值不能乱写,要根据 内核的错误宏定义
*/
return size ;
}
ssize_t ch_read(struct file *file, char __user *usr, size_t size, loff_t *loff)
{
int ret;
struct global_struct *pt_gstruct = file->private_data;
printk("%s->%d major%d minor%d\n",__FUNCTION__,__LINE__,pt_gstruct->major,pt_gstruct->minor);
/*
非阻塞, 实现逻辑
如果用户要求 文件时非阻塞的, 并且当前没有数据,则 立马返回.
*/
if(file->f_flags & O_NONBLOCK){
if(pt_gstruct->data_flags == 0){
return -EAGAIN;
}
}
/*注意, 如果 没有数据,则当前进程 阻塞在该函数内部
不在返回 . 当数据到来,被唤醒,该函数 返回
该函数,再带 判断, 当有数据的时候,该函数 不会睡眠, 没有数据则会睡眠
*/
wait_event_interruptible(pt_gstruct->waitqhead,pt_gstruct->data_flags);
/* usr 来自用户空间, 有可能 是个非法地址
需要使用 安全版本 memcpy
*/
ret = copy_to_user(usr,pt_gstruct->buf,size);
if(ret ){
printk("%s->%d copy_to_user err\n",__func__,__LINE__);
return -16;
}
memset(pt_gstruct->buf,0,sizeof(pt_gstruct->buf));
pt_gstruct->data_flags = 0; //取走之后,清空数据标志位
/*成功, 返回实际拷贝的大小
失败, 返回一个负数, 记录 数值不能乱写,要根据 内核的错误宏定义
*/
return size;
}
long chdev_ioctl(struct file *file, unsigned int cmd, unsigned long args)
{
int ret;
struct tv_switch swtich;
struct tv_chnl chnl;
struct tv_vol vol;
struct tv_stat stat;
struct led_ctrl ctl;
struct global_struct *pt_gstruct = file->private_data;
printk("%s->%d major%d minor%d\n",__FUNCTION__,__LINE__,pt_gstruct->major,pt_gstruct->minor);
switch(cmd){
case TV_CMD_SWITCH:
ret = copy_from_user(&swtich,(void *)args,sizeof(swtich) );
printk("%s->%d TV_CMD_SWITCH get on%d\n",__func__,__LINE__,swtich.on);
break;
case TV_CMD_CHNL:
ret = copy_from_user(&chnl,(void *)args,sizeof(chnl));
printk("%s->%d TV_CMD_CHNL get up%d num%d\n",__func__,__LINE__,chnl.up,chnl.num);
break;
case TV_CMD_VOL:
ret = copy_from_user(&vol,(void *)args,sizeof(vol));
printk("%s->%d TV_CMD_VOL get up%d step%d\n",__func__,__LINE__,vol.up,vol.step);
break;
case TV_CMD_STAT:
stat.chnl = 129;
stat.vol = 56;
stat.light = 57;
ret = copy_to_user((void *)args,&stat,sizeof(stat));
break;
case LED_CMD_SW:
ret = copy_from_user(&ctl,(void *)args,sizeof(ctl));
led_ctrl(ctl.on);
break;
}
return 0;
}
int chdev_fasync(int fd, struct file *file, int on)
{
struct global_struct *pt_gstruct = file->private_data;
return fasync_helper(fd,file ,on,&pt_gstruct->fapp);
}
struct file_operations chdev_ops = {
.open = chdev_open,
.release = chdev_close,
.write = chdev_write,
.read = ch_read,
.unlocked_ioctl = chdev_ioctl,
.fasync = chdev_fasync,
};
int module_fun_init(void)
{
int ret;
dev_t devno;
/* 等待 队列初始化*/
init_waitqueue_head(&gstruct.waitqhead);
memset(gstruct.buf,0,sizeof(gstruct.buf));
gstruct.data_flags = 0;
/* 1.向内核申请设备号*/
ret = alloc_chrdev_region(&devno,0,1,"chdev test");
if(ret <0){
printk("alloc_chrdev_region err %s->%d\n",__FUNCTION__,__LINE__);
return -12;
}
gstruct.major = MAJOR(devno); //major = devno >> 20;
gstruct.minor = MINOR(devno); //devno <<12>>12;
devno = MKDEV(gstruct.major,gstruct.minor); //major<<20 | minor;
printk("%s->%d get major%d minor%d\n",__FUNCTION__,__LINE__,gstruct.major,gstruct.minor);
/*创建并初始化 字符设备驱动 对象*/
cdev_init(&gstruct.chdevobj,&chdev_ops);
ret = cdev_add(&gstruct.chdevobj,devno,1);
if(ret <0){
printk("cdev_add err %s->%d\n",__FUNCTION__,__LINE__);
return -14;
}
/*第三步, 1.创建一个类*/
gstruct.cls = class_create(THIS_MODULE,"chdev class");
if(!gstruct.cls){
printk("class_create err %s->%d\n",__FUNCTION__,__LINE__);
return -16;
}
/*2.创建一个节点/文件 ,在类下
printf(const char * fmt,...); printf("%s %d %c %lf ",........);
在 /dev/目录下,会创建 /dev/chdev0 文件节点
*/
device_create(gstruct.cls,NULL,devno,NULL,"chdev%d",0);
return 0;
}
void module_fun_exit(void)
{
//释放 init里面的动作,注意 和init是完全的逆序
//释放文件节点
device_destroy(gstruct.cls,MKDEV(gstruct.major,gstruct.minor));
//释放类
class_destroy( gstruct.cls);
//删除 对象
cdev_del(&gstruct.chdevobj);
//释放设备号
unregister_chrdev_region(MKDEV(gstruct.major,gstruct.minor),1);
printk("%s->%d\n",__FUNCTION__,__LINE__);
}
/* 内核模块入口 module_init 里面的参数,指向谁,谁就是模块入口函数*/
module_init(module_fun_init);
/* 内核模块出口 module_exit 里面的参数,指向谁,谁就是模块出口函数*/
module_exit(module_fun_exit);
/*模块作者信息*/
MODULE_AUTHOR("YinFei.Hu <[email protected]>");
/*模块信息*/
MODULE_DESCRIPTION("Kernel module driver");
/*告诉内核愿意遵守gpl协议*/
MODULE_LICENSE("GPL");
- appwrite.c
/*****************************************************************
* Copyright (C) 2019 Sangfor Ltd. All rights reserved.
*
* 文件名称:appwrite.c
* 创 建 者:yinfei-hu
* 创建日期:2019年03月07日 星期4 20时22分05秒
* 功能描述:通过之后设备的write控制led
*
*****************************************************************/
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include "comm.h"
int main(void)
{
int ret;
int fd;
char buf[128];
fd = open("/dev/chdev0",O_RDWR);
if(fd <0){
perror("open err");
return -1;
}
strcpy(buf,"usr msg comes from write app");
ret = write(fd, buf,sizeof(buf));
if(ret <0){
perror("write err");
return -2;
}
sleep(1);
close(fd);
return 0;
}
- appread-signal.c
/*****************************************************************
* Copyright (C) 2019 Sangfor Ltd. All rights reserved.
*
* 文件名称:appread-signal.c
* 创 建 者:yinfei-hu
* 创建日期:2019年03月07日 星期4 20时22分05秒
* 功能描述:异步通知读
*
*****************************************************************/
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include "comm.h"
int fd;
void sigio_fun(int signo)
{
int ret;
char buf[128];
memset(buf,0,sizeof(buf));
ret = read(fd,buf,sizeof(buf));
if(ret <0){
perror("read err");
return ;
}
printf("int signal fun ,usr get :%s\n",buf);
}
int main(void)
{
int ret;
int val;
fd = open("/dev/chdev0",O_RDWR);
if(fd <0){
perror("open err");
return -1;
}
/* 注册信号 */
signal(SIGIO, sigio_fun);
/* 设置 fd的 文件属主, 告诉文件,你的主人进程是哪一个
当文件有了信号之后,转发给 属主进程
*/
fcntl(fd,F_SETOWN,getpid());
/*让文件支持 异步通知模式
file里面 f_flags, 其中某一个bit表示该文件支持 异步模式
*/
val = fcntl(fd,F_GETFL,0);
val |= FASYNC;
fcntl(fd,F_SETFL,val);
while(1){
sleep(1);
void do_something(void); //延时
}
close(fd);
return 0;
}