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,便开始进行模块的编译了,遇到编译错误,多百度,积累经验。