Linux IO模型(结合linux内核字符驱动)

01_IO模型的类型

在linux内核字符驱动的框架理解输入输出的读写。字符设备文件操作集file_operations可以重写read、write、epoll。类型涉及:阻塞、非阻塞、异步通知、IO多路复用。

02_举例描述

02_01非阻塞与阻塞

例如函数read(fd,buf,sizeof(buf)),是把fd文件读取到buf中,实现了把字符设备文件的东西读到用户程序自己创建的缓冲数组,从而打印查看。在内核重写的.read=myread中,用copy_to_user(ubuf,kbuf,sizeof(kuf)),把内核缓冲区的值传给用户缓冲区。由于copy_to_user的本质是memcpy函数,即一个字符一个字符的拷贝。那么这个read函数的实现是非阻塞的:拷贝失败,则为空。

void* My_memcpy(void* dest,const void* src, size_t num)
{
	assert(dest && src);
	void* ret = dest;
	while (num--)
	{
		*(char*)dest = *(char*)src;
		dest = (char*)dest + 1;
		src = (char*)src + 1;
	}
	return ret;
}

如果要实现read的阻塞效果,就需要用到工作队列:

//定义等待队列

wait_queue_head_t my_queue;

//初始化等待队列   

init_waitqueue_head(&my_queue);

//等待事件

wait_event_interruptible(queue,conditon)

//唤醒队列

wake_up_interruptible(wait_queue_head_t *queue);

应用程序:

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>

static int fd = -1;
static char wbuf[16]="1234567890";
static char rbuf[16]={0};
void* fun0(void* arg)
{
    sleep(3);
    printf("write ok");
    write(fd,wbuf,16);
}

int main()
{
    fd = open("/dev/mydev",O_RDWR);
    if(fd<0)
    {
        printf("fd is %d\n",fd);
        return -1;    
    }
    pthread_t t0;
    pthread_create(&t0,NULL,fun0,NULL);
    pthread_detach(t0);
    
    char rbuf[16]={0};
    read(fd,rbuf,16);               
    printf("rbuf is %s\n",rbuf);
    
    close(fd);
    return 0;
}

驱动: 

//头文件
#include <linux/init.h>
#include <linux/module.h>

#include<linux/kdev_t.h>
#include<linux/cdev.h>
#include<linux/fs.h>
#include"cmd.h"
#include<asm/uaccess.h>
#include<asm/io.h>
#include<linux/delay.h>
#include<linux/sched.h>
#define MA 300
#define MI 0

static dev_t no;
static unsigned count = 1;
static const char * name  = "mydev";
static struct cdev mydev;

static char kbuf[16]={0};
static int ret = -1;
static int flag = 0;
static wait_queue_head_t my_queue;

int myopen(struct inode *pi,struct file *pf)
{
    printk("myopen success\n");
    return 0;
}
int myrelease(struct inode *pi,struct file *pf)
{
     printk("myrelease success\n");
     return 0;
}
ssize_t myread(struct file pf,char __user *ubuf, size_t len, loff_t *poff)
{
    int ret = -1;
    while(flag==0)
    {
        msleep(1);    
    }
    wait_event_interruptible(my_queue,flag>0)
    ret = copy_to_user(ubuf,kbuf,len);
    if(ret!=0)
        return -1;
    flag = 0;
    return len;
}
ssize_t mywrite(struct file pf,char __user *ubuf, size_t len, loff_t *poff)
{
    int ret = -1;
    ret = copy_from_user(kbuf,ubuf,len);
    if(ret!=0)
        return -1;
    flag = len;
    wake_up_interruptible(&my_queue);
    return len;
}
static const struct file_operations fops={
    .open = myopen,
    .release = myrelease,
    .read = myread,
    .open = myopen,
};
//函数init exit
static int myinit(void)
{
    //向上: 内核相关
    //1.注册
    no = MKDEV(MA,MI);
    ret = register_chrdev_region(no,count,name);
    if(0!=ret)
        return ret;
    //2.初始化
    cdev_init(&mydev,&fops);
    //3.添加
    ret = cdev_add(&mydev,no,count);
    if(0!=ret)
    {
         unregister_chrdev_region(no,count);
         return ret;
    } 
    init_waitqueue_head(&my_queue);
     printk("init ok\n");
     return 0;
}
static void myexit(void)
{
     cdev_del(&mydev);
     unregister_chrdev_region(no,count);
     printk("exit ok\n");
     return;
}
//注册入口函数
module_init(myinit)
module_exit(myexit);
//模块信息描述
MODULE_LICENSE("GPL");

驱动框架见Linux下字符设备驱动框架-CSDN博客 

02_02异步通知

阻塞和非阻塞是主动获取资源,异步通知是被动获取资源,通过收到信号,执行相应的信号处理。

异步通知的步骤:

1.绑定信号和回调函数.

2.将进程号和设备绑定,添加到内核f_owner。(每个进程的用户空间都能操作设备文件,在内核空间的f_owner是共享的,只有一个)

3.fcntl将fd 的设备,FASYNC参数(像ioctl的arg),初始化 异步通知队列

4.信号进入异步通知队列,队列发送到信号处理函数,执行回调函数

异步通知框架:

 

//异步结构定义
struct fasync_struct *async_queue;
//异步初始化
static int myfasync(int fd,struct file *filp,int mode)
{
    return fasync_helper(fd,filp,mode,&async_queue);
}
//发送信号
kill_fasync(&async_queue,SIGIO,POLL_IN);

 用应用程序发送信号和驱动实现异步通知

应用程序:

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <signal.h>
#include "cmd.h"

static char wbuf[16]="abcdef";
static char rbuf[16]={0};
static int fd = -1;


void handler(int no)
{
    read(fd,rbuf,16);
    printf("rubf is %s\n",rbuf);
}

int main(int argc, char *argv[])
{
    int oflags = 0;
    fd = open("/dev/mydev",O_RDWR);
    if(fd<0)
    {
        printf("fd is %d\n",fd);
        return -1;
    }
    
    signal(SIGIO, handler);
    fcntl(fd, F_SETOWN, getpid());
    oflags = fcntl(fd, F_GETFL); 
    fcntl(fd, F_SETFL, oflags | FASYNC);

    sleep(3);
    write(fd,wbuf,16);

    while(1);

    close(fd);
    return 0;
} 

    

驱动:

//头文件
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kdev_t.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <asm/signal.h>
#include "cmd.h"
#include <asm-generic/siginfo.h>

#define MA 300
#define MI 0

static dev_t no;
static unsigned count = 1;
static const char * name  = "mydev";
static struct cdev mydev;

static char kbuf[16]={0};
static int ret = -1;
static wait_queue_head_t my_queue;

static struct fasync_struct *async_queue;
static int myfasync(int fd,struct file *filp ,int mode)
{
    return fasync_helper(fd,filp,mode,&async_queue);
}

int myopen(struct inode *pi,struct file *pf)
{
    printk("myopen success\n");
    return 0;
}
int myrelease(struct inode *pi,struct file *pf)
{
     printk("myrelease success\n");
     return 0;
}
ssize_t myread(struct file pf,char __user *ubuf, size_t len, loff_t *poff)
{
    int ret = -1;
    ret = copy_to_user(ubuf,kbuf,len);
    if(ret!=0)
        return -1;
    return len;
}
ssize_t mywrite(struct file pf,char __user *ubuf, size_t len, loff_t *poff)
{
    int ret = -1;
    ret = copy_from_user(kbuf,ubuf,len);
    if(ret!=0)
        return -1;
    kill_fasync(&async_queue,SIGIO,POLL_IN);
    return len;
}


static const struct file_operations fops={
    .open = myopen,
    .release = myrelease,
    .read = myread,
    .write = mywrite,
    .fasync = myfasync,
};
//函数init exit
static int myinit(void)
{
    //向上: 内核相关
    //1.注册
    no = MKDEV(MA,MI);
    ret = register_chrdev_region(no,count,name);
    if(0!=ret)
        return ret;
    //2.初始化
    cdev_init(&mydev,&fops);
    //3.添加
    ret = cdev_add(&mydev,no,count);
    if(0!=ret)
    {
         unregister_chrdev_region(no,count);
         return ret;
    } 
    init_waitqueue_head(&my_queue);
     printk("init ok\n");
     return 0;
}
static void myexit(void)
{
     cdev_del(&mydev);
     unregister_chrdev_region(no,count);
     printk("exit ok\n");
     return;
}
//注册入口函数
module_init(myinit)
module_exit(myexit);
//模块信息描述
MODULE_LICENSE("GPL");

 02_03IO多路复用

 用户程序的select,poll,epoll在内核中都是调用poll函数。poll函数用到了等待队列,POLLIN是读,POLLOUT是写。

用户程序:

#include <sys/select.h>
int main()
{
    int fd = open("/dev/mydev",O_RDWR);
    if(fd<0)
    {
        perror("fd is %d\n",fd);
        return -1;    
    }
    
    
    fd_set = rfds;
    fd_set = wfds;
    while(1)
    {
        FD_ZERO(&rfds);
        FD_ZERO(&wfds);
        FD_SET(fd,&rfds);
        FD_SET(fd,&wfds);
        
        select(fd+1, &rfds, &wffs, NULL, 0);
        
        if(FD_ISSET(fd,&rfds))
        {
            read(fd,rbuf,16);
            printf("rbuf is %s\n",buf);    
        }
        if(FD_ISSET(fd,&wfds))
        {
            write(fd,wbuf,16);
            printf("write ok\n");    
        }
    }
}

驱动

//头文件
#include <linux/init.h>
#include <linux/module.h>

#include<linux/kdev_t.h>
#include<linux/cdev.h>
#include<linux/fs.h>
#include"cmd.h"
#include<asm/uaccess.h>
#define MA 300
#define MI 0

static dev_t no;
static unsigned count = 1;
static const char * name  = "mydev";
static struct cdev mydev;

static char kbuf[16]={0};
static int ret = -1;
static wait_queue_head_t my_queue;
static int flag = 0;

int myopen(struct inode *pi,struct file *pf)
{
    printk("myopen success\n");
    return 0;
}
int myrelease(struct inode *pi,struct file *pf)
{
     printk("myrelease success\n");
     return 0;
}
ssize_t myread(struct file pf,char __user *ubuf, size_t len, loff_t *poff)
{
    int ret = -1;
    spin_lock(&lock);
    ret = copy_to_user(ubuf,kbuf,len);
   spin_unlock(&lock);
   if(ret!=0)
        return -1;
   flag = 1;/
   return len;
}
ssize_t mywrite(struct file pf,char __user *ubuf, size_t len, loff_t *poff)
{
    int ret = -1;
    spin_lock(&lock);
    ret = copy_from_user(kbuf,ubuf,len);
   spin_unlock(&lock);
    if(ret!=0)
        return -1;
   return len;
}
unsigned int mypoll(struct file *pf, struct poll_table_struct *pt)
{
    unsigned int mask = 0;
    poll_wait (pf,&my_queue,pt);
    if(flag==1)
    {
         mask |= POLLIN | POLLRDNORM;
    }
    if(flag==0)
    {
         mask |= POLLOUT | POLLRDNORM;   
    }
    return mask;
}
static const struct file_operations fops={
    .open = myopen,
    .release = myrelease,
    .read = myread,
    .open = myopen,
    .poll = mypoll
};
//函数init exit
static int myinit(void)
{
    //向上: 内核相关
    //1.注册
    no = MKDEV(MA,MI);
    ret = register_chrdev_region(no,count,name);
    if(0!=ret)
        return ret;
    //2.初始化
    cdev_init(&mydev,&fops);
    //3.添加
    ret = cdev_add(&mydev,no,count);
    if(0!=ret)
    {
         unregister_chrdev_region(no,count);
         return ret;
    } 
    init_waitqueue_head(&my_queue);
     printk("init ok\n");
     return 0;
}
static void myexit(void)
{
     cdev_del(&mydev);
     unregister_chrdev_region(no,count);
     printk("exit ok\n");
     return;
}
//注册入口函数
module_init(myinit)
module_exit(myexit);
//模块信息描述
MODULE_LICENSE("GPL");

  • 21
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值