您的位置:首页 > 运维架构 > Linux

Linux OS内核 作业三:设备驱动与读写信号量

2017-02-28 20:38 302 查看
实验题目:

http://gauss.ececs.uc.edu/Courses/c4029/labs/lab6.html

大致内容就是写一个设备驱动,实现“开关读写”。然后加入读写信号量,实现互斥访问:可以多个人同时读,但只要有一个人写,那么其他人就不能读和写。

编写内核模块:实现对设备的访问

//"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内核 作业三:设备驱动与读写信号量
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: