Linux内核驱动入门之阻塞操作实验:glob

首先,先来了解一下设备的阻塞与非阻塞操作以及实现阻塞操作的方法:

1.设备的阻塞与非阻塞操作:

阻塞操作是指,在执行设备操作时,若不能获得资源,则进程被挂起直到满足可操作的条件再进行操作。非阻塞操作是指,当进程不能进行设备操作时,并不挂起,它或者放弃,或者不停地查询,直到可以进行操作为止。

2.实现阻塞操作的方法:

在linux驱动程序中,可以使用等待队列(wait queue)来实现阻塞访问。

一,glob字符设备驱动程序的编写,把文件名命名为glob.c,源代码如下:


#include <linux/module.h>

#include <linux/init.h>

#include <linux/fs.h>

#include <asm/uaccess.h>

#include <linux/wait.h>  //有关等待队列的头文件

#include <linux/semaphore.h> //有关信号量的头文件

#include <linux/sched.h>

MODULE_LICENSE("GPL");

#define MAJOR_NUM 1400

#define DEVICE_NAME "glob"

static int glob_var = 0;

static struct semaphore sem; //定义信号量

static wait_queue_head_t outq;  //定义一个等待队列头 

static int flag = 0;

 

//*******************定义read方法****************************

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

{

      //等待数据可获得

      //wait_event_interruptible的返回一个整数值,非零值表示休眠被某个信号中断

      //wait_event_interruptible中第一个参数是等待队列头,第二个参数是一个布尔表达式,在条件为真之前,进程会保持休眠

    if (wait_event_interruptible(outq, flag != 0))

    {

          return - ERESTARTSYS;

    }

    //down_interruptible 函数返回非零值,表示操作被中断,调用者拥有信号量失败

    if (down_interruptible(&sem))

    {

          return - ERESTARTSYS;

    }

    flag = 0;

    //将内核空间中的数据移动到用户空间

    if (copy_to_user(buf, &glob_var, sizeof(int)))

    {

          up(&sem); //移动数据的操作不完全成功也需要释放信号量

          return - EFAULT;

    }

    up(&sem);//移动数据成功,释放信号量

    return sizeof(int);

}

 

//************************定义write方法******************************

//glob_write函数中,flip是文件指针,buf是指向用户空间的缓冲区,len表示请求传输数据的长度,

//off指向一个长偏移量类型对象的指针,这个对象指明用户在文件中进行存储操作的位置 

static ssize_t glob_write(struct file *filp, const char *buf, ssize_t len,loff_t *off)

{

    if (down_interruptible(&sem))

    {

          return - ERESTARTSYS;

    }

    //将用户空间的数据移动到内核空间

    if (copy_from_user(&glob_var, buf, sizeof(int)))

    {

        up(&sem); //移动数据不完全成功也需要释放信号量

        return - EFAULT;

    }

    up(&sem); //移动数据成功,释放信号量

    flag = 1;

    //通知数据可获得

    wake_up_interruptible(&outq); //唤醒休眠进程       

    return sizeof(int);

}

 

//************初始化file_operations结构体*************

struct file_operations glob_fops =

{

    .owner = THIS_MODULE,

    .read = glob_read,

    .write = glob_write,

};

 

//*******模块初始化函数*********

static int __init glob_init(void)

{

    int ret;

    ret = register_chrdev(MAJOR_NUM, DEVICE_NAME, &glob_fops);

    if (ret)

    {

          printk("glob register failure");

    }

    else

    {

        printk("glob register success");

        //init_MUTEX(&sem);

        sema_init(&sem,1); //初始化一个互斥锁,把信号量sem的值设置为1

        init_waitqueue_head(&outq); //初始化等候队列头     

    }

    return ret;

}

 

//************模块卸载函数**************

static void __exit glob_exit(void)

{

    unregister_chrdev(MAJOR_NUM, DEVICE_NAME);

    printk("glob unregister success!\n");

}

 

module_init(glob_init);

module_exit(glob_exit);

二,Makefile文件的编写,源代码如下:

12345

obj-m:=glob.o

default:

$(MAKE) -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules

clean:

$(MAKE) -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean

三,编译模块:

把上面的glob.c和Makefile两个文件放在同一个文件夹下,我这里的文件夹是“glob阻塞操作实验”,然后进入文件夹,打开终端,登录root,输入指令make,便开始进行模块的编译了,遇到编译错误,多百度,积累经验。

相关推荐