实验题目:
大致内容就是写一个设备驱动,实现“开关读写”。然后加入读写信号量,实现互斥访问:可以多个人同时读,但只要有一个人写,那么其他人就不能读和写。
编写内核模块:实现对设备的访问
//"RW_module.c"
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/timer.h>
#include <linux/fs.h>
#include <linux/sched.h>
#include <linux/rwsem.h>
#include <linux/cdev.h>
#include <linux/wait.h>
#include <linux/sched.h>
#include <linux/rwsem.h>
#include <asm/uaccess.h>
#include <linux/wait.h>
MODULE_LICENSE("GPL");
#define DEVICE_NAME "guan_device"
static int MAX_BUF_LEN=1024;
static char drv_buf[1024];
//方法声明,在下面具体实现
ssize_t read(struct file *filp, char *buff, size_t count, loff_t *offp);
ssize_t write(struct file *filp, const char *buff, size_t count, loff_t *offp);
int open(struct inode *inode, struct file *filp);
int release(struct inode *inode, struct file *filp);
struct file_operations fops = {
.read = read,
.write = write,
.open = open,
.release = release
};
struct cdev *kernel_cdev; /* declared globally */
dev_t dev_no; /* declared globally */
int Major; /* declared globally */
//定义信号量
struct rw_semaphore sem_guan;
wait_queue_head_t queue;
/**
struct rw_semaphore sem_guan = {
long count;
raw_spinlock_t wait_lock;
struct list_head wait_list;
};
void init_rwsem(struct rw_semaphore); //Initialize the semaphore
void down_read(struct rw_semaphore *sem); //Hold semaphore for reading, sleep if not available
void up_read(struct rw_semaphore *sem); //Release semaphore for reading
void down_write(struct rw_semaphore *sem); //Hold semaphore for writing, sleep if not available
void down_write_trylock(struct rw_semaphore *sem); //Hold semaphore for writing, error if not available
void up_write(struct rw_semaphore *sem); //Release semaphore for writing
*/
//===========================================================================
//用于延迟等待,模拟耗时操作
void dely_guan(void)
{
//delay for 20 seconds like this:
wait_event_timeout(queue, 0, 20*HZ);
}
//========================================
/**
*加载内核模块时
*/
int init_module( void ) {
int ret;
kernel_cdev = cdev_alloc();
kernel_cdev->ops = &fops;
ret = alloc_chrdev_region(&dev_no , 0, 1, DEVICE_NAME);//动态分配设备编号,DEVICE_NAME
//add (register) the character device interface to (with) the operating system
int dev;
Major = MAJOR(dev_no);
dev = MKDEV(Major,0);
ret = cdev_add(kernel_cdev, dev, 1);
//初始化信号量
init_rwsem(&sem_guan);
init_waitqueue_head(&queue);
return 0;
}
/**
*
*/
void cleanup_module( void ) {
//注销设备
cdev_del(kernel_cdev);
unregister_chrdev_region(dev_no, 1);
unregister_chrdev(Major, DEVICE_NAME);
printk(KERN_INFO "ReadWrite module uninstalling\n");
return;
}
//===============以下是“开关读写”操作的具体实现=============================================
ssize_t read(struct file *filp, char *buff, size_t count, loff_t *offp)
{
//加锁
down_read(&sem_guan);
//把数据传到“用户空间”,即“读”
copy_to_user(buff, drv_buf,count);
printk(KERN_INFO "user read data from driver:\t%s\n",buff);
//模拟等待
dely_guan();
//释放
up_read(&sem_guan);
return count;
}
ssize_t write(struct file *filp, const char *buff, size_t count, loff_t *offp)
{
//加锁
down_write(&sem_guan);
//把数据从“用户空间”传到“内核空间”,即“写”到设备上
copy_from_user(drv_buf , buff, count);
printk(KERN_INFO "user write data to driver:\t%s\n",buff);
dely_guan();
//释放锁
up_write(&sem_guan);
return count;
}
int open(struct inode *inode, struct file *filp)
{
printk(KERN_INFO "open device");
return 0;
}
int release(struct inode *inode, struct file *filp)
{
printk(KERN_INFO "release device");
return 0;
}
- 安装
sudo insmod RW_module.ko #这里是内核模块的名字
cat /proc/devices | grep guan_device #这里是内核模块中定义的设备名称
#get the major number:得到设备编号
sudo mknod /dev/interface c 247(设备编号) 0 #创建设备
sudo chmod a+w /dev/guan_device #更改权限
- 卸载
sudo rmmod RW_module #卸载模块
sudo rm -f /dev/guan_device #删除设备
注:
1. 关于显示运行时间的问题,可以参考我的另一篇博文:http://blog.csdn.net/u013806583/article/details/58127067
2. 关于如何编译、加载和卸载内核模块,可以参考《Linux OS内核 作业一》的方法:http://blog.csdn.net/u013806583/article/details/58604378
用户编写自定义程序,访问设备
源码:app.c
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
int MAX_LEN=32;
int fd;
char read_buf[100];
void write_guan(char* str)
{
printf("start to write ...\n");
//调用函数,完成“写”操作
write(fd, str, sizeof(str));
}
void read_guan()
{
printf("start to read ...\n");
//调用函数,完成“读”操作
read(fd, read_buf, sizeof(read_buf));
//输出独到的内容
printf("%s\n",read_buf );
}
void main(int argc, char const *argv[])
{
fd = open("/dev/guan_device", O_RDWR);//DEVICE_NAME
/**
*O_RDONLY 只读打开。
O_WRONLY 只写打开。
O_RDWR 读、写打开。
*/
char order_input[5];
char content_input[100];
scanf("%s",order_input);
if (strcmp(order_input,"read")==0)
{
read_guan();
}else if (strcmp(order_input,"write")==0)
{
scanf("%s",content_input);
write_guan(content_input);
}else{
printf("input ERROR !\n");
}
return ;
}
makefile:
main: app.o
gcc -o app app.o -lm
clean:
rm -f app app.o
- 编译
- 使用
- 按照第一部分方法,安装内核模块
- 编译本源码app.c
- 运行
./app #回车,然后程序就等待输入
read #读操作:输入read命令,并回车
#或
write abcdefg... #写操作:输入write命令和写入的内容,并回车
测试
开启三个终端,分别运行 ./app 。其中一个运行写命令,其余两个运行读命令。
结果:可以看到当程序在写时,读操作要等待。当写操作完成时,两个读操作几乎同时进行。
系列博客:
相信当你需要其中一个的时候,也一定需要剩下的两个
Linux OS内核 作业一:kthread和workqueue
Linux OS内核 作业二:多线程访问
Linux OS内核 作业三:设备驱动与读写信号量