字符设备驱动第八课----file->flag判是否阻塞

概述

设备驱动中的操作方法集中所有的函数,第一个参数都是struct file *filp。这个结构体中有个成员,专门标识阻塞和非阻塞。

1.file结构体剖析:

<include/linux/fs.h>


struct file {
    union {
        struct llist_node   fu_llist;
        struct rcu_head     fu_rcuhead;
    } f_u;
    struct path     f_path;   //路径
    struct inode        *f_inode;   /* cached value */
    const struct file_operations    *f_op;  //操作方法集

    /*
     * Protects f_ep_links, f_flags.
     * Must not be taken from IRQ context.
     */
    spinlock_t      f_lock;
    atomic_long_t       f_count;
    unsigned int        f_flags;   //是否阻塞
    fmode_t         f_mode;
    struct mutex        f_pos_lock;
    loff_t          f_pos;
    struct fown_struct  f_owner;
    const struct cred   *f_cred;
    struct file_ra_state    f_ra;

    u64         f_version;
#ifdef CONFIG_SECURITY
    void            *f_security;
#endif
    /* needed for tty driver, and maybe others */
    void            *private_data;

#ifdef CONFIG_EPOLL
    /* Used by fs/eventpoll.c to link all the hooks to this file */
    struct list_head    f_ep_links;
    struct list_head    f_tfile_llink;
#endif /* #ifdef CONFIG_EPOLL */
    struct address_space    *f_mapping;
} __attribute__((aligned(4)));  /* lest something weird decides that 2 is OK */

2.flags标识:

非阻塞:

O_NONBLOCK
if(filp->f_flags & O_NONBLOCK)

阻塞:

O_BLOCK
if(filp->f_flags & O_BLOCK)

工程实例

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/errno.h>

#include <asm/current.h>
#include <linux/sched.h>

#include <linux/uaccess.h>

#include <asm/atomic.h>
#include <linux/mutex.h>

#include <linux/wait.h>

#include <linux/device.h>
static struct class *cls = NULL;    //设备类,为自动创建设备

static int major = 0;
static int minor = 0;
const  int count = 6;

#define DEVNAME "demo"

static struct cdev *demop = NULL;   //字符型设备句柄,为自动创建设备
static atomic_t tv;              //定义一个原子操作
static wait_queue_head_t wq;    //定义一个等待队列

static int counter = 0;

//打开设备
static int demo_open(struct inode *inode, struct file *filp)
{
    //get major and minor from inode
    printk(KERN_INFO "(major=%d, minor=%d), %s : %s : %d\n",
        imajor(inode), iminor(inode), __FILE__, __func__, __LINE__);

    /*这个判断确保了设备首次打开不会报错,但不能被重复打开*/
    if(!atomic_dec_and_test(&tv)){      //原子操作,atomic_dec_and_test减一并判断是否为0
        atomic_inc(&tv);    //原子操作,加一
        return -EBUSY;
    }

    return 0;
}

//关闭设备
static int demo_release(struct inode *inode, struct file *filp)
{
    //get major and minor from inode
    printk(KERN_INFO "(major=%d, minor=%d), %s : %s : %d\n",
        imajor(inode), iminor(inode), __FILE__, __func__, __LINE__);

    atomic_inc(&tv);    //因为打开时(见上)if条件判断中对其减了1,故这里要加1,才能确保为原来的初始值1
    return 0;
}

//读设备
//ssize_t read(int fd, void *buf, size_t count)
static ssize_t demo_read(struct file *filp, char __user *buf, size_t size, loff_t *offset)
{
    int err = 0;
    struct inode *inode = filp->f_path.dentry->d_inode;     //为imajor准备好参数
    //get major and minor from inode
    printk(KERN_INFO "(major=%d, minor=%d), %s : %s : %d\n",
        imajor(inode), iminor(inode), __FILE__, __func__, __LINE__);

    if(!counter){
        if(filp->f_flags & O_NONBLOCK){     //filp->f_flags中有标志位可以知道是否为阻塞模式
            return -EAGAIN;     //如果无东西可读,且为非阻塞模式则拒绝本次读,下次再试,
                //若不这样做他不会像普通read那样阻塞等,阻塞读时就无需如此
        }

        err = wait_event_interruptible(wq, (0 != counter));//若“counter不为0”这个条件成立,则返回值为0且往下走,否则阻塞在此处(但这种阻塞状态可以被中断)
        if(err){
            return err; //若counter为0,则上一条返回的是非零值,执行此语句,返回错误码
        }
    }

    //...

    counter = 0;    //读完清零,模拟缓冲区已被读空
    return 0;
}

//写设备
static ssize_t demo_write(struct file *filp, const char __user *buf, size_t size, loff_t *offset)
{
    struct inode *inode = filp->f_path.dentry->d_inode;
    //get major and minor from inode
    printk(KERN_INFO "(major=%d, minor=%d), %s : %s : %d\n",
        imajor(inode), iminor(inode), __FILE__, __func__, __LINE__);

    counter = 10;   //模拟“有东西写入了”这个事件
    wake_up_interruptible(&wq);//唤醒等待队列,以便等待着醒来读取

    return 0;
}

static struct file_operations fops = {
    .owner  = THIS_MODULE,
    .open   = demo_open,
    .release= demo_release,
    .read   = demo_read,
    .write  = demo_write,
};

static int __init demo_init(void)
{
    dev_t devnum;
    int ret, i;

    struct device *devp = NULL;

    //get command and pid
    printk(KERN_INFO "(%s:pid=%d), %s : %s : %d\n",
        current->comm, current->pid, __FILE__, __func__, __LINE__);

    //1. alloc cdev obj
    demop = cdev_alloc();
    if(NULL == demop){
        return -ENOMEM;
    }

    //2. init cdev obj
    cdev_init(demop, &fops);

    ret = alloc_chrdev_region(&devnum, minor, count, DEVNAME);//动态申请设备号
    if(ret){
        goto ERR_STEP;
    }
    major = MAJOR(devnum);

    //3. register cdev obj
    ret = cdev_add(demop, devnum, count);
    if(ret){
        goto ERR_STEP1;
    }

    cls = class_create(THIS_MODULE, DEVNAME);   //创建设备类,自动创建设备
    if(IS_ERR(cls)){
        ret = PTR_ERR(cls);
        goto ERR_STEP1;
    }

    for(i = minor; i < (count+minor); i++){
        devp = device_create(cls, NULL, MKDEV(major, i), NULL, "%s%d", DEVNAME, i);//创建设备
        if(IS_ERR(devp)){
            ret = PTR_ERR(devp);
            goto ERR_STEP2;
        }
    }

    // init atomic_t
    atomic_set(&tv, 1); //  初始化原子操作,设置原子量的初值为1

    init_waitqueue_head(&wq);   //初始化等待队列

    //get command and pid
    printk(KERN_INFO "(%s:pid=%d), %s : %s : %d - ok.\n",
        current->comm, current->pid, __FILE__, __func__, __LINE__);
    return 0;

ERR_STEP2:
    for(--i; i >= minor; i--){
        device_destroy(cls, MKDEV(major, i));
    }
    class_destroy(cls);

ERR_STEP1:
    unregister_chrdev_region(devnum, count);

ERR_STEP:
    cdev_del(demop);

    //get command and pid
    printk(KERN_INFO "(%s:pid=%d), %s : %s : %d - fail.\n",
        current->comm, current->pid, __FILE__, __func__, __LINE__);
    return ret;
}

static void __exit demo_exit(void)
{
    int i;
    //get command and pid
    printk(KERN_INFO "(%s:pid=%d), %s : %s : %d - leave.\n",
        current->comm, current->pid, __FILE__, __func__, __LINE__);

    for(i=minor; i < (count+minor); i++){
        device_destroy(cls, MKDEV(major, i));
    }
    class_destroy(cls);

    unregister_chrdev_region(MKDEV(major, minor), count);
    cdev_del(demop);
}

module_init(demo_init);
module_exit(demo_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Farsight");
MODULE_DESCRIPTION("Demo for kernel module");
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

xxgui1992

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值