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

实验题目:

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
  • 编译
  • 使用
    1. 按照第一部分方法,安装内核模块
    2. 编译本源码app.c
    3. 运行
 ./app #回车,然后程序就等待输入
    read #读操作:输入read命令,并回车
    #或
    write abcdefg... #写操作:输入write命令和写入的内容,并回车

测试

开启三个终端,分别运行 ./app 。其中一个运行写命令,其余两个运行读命令。

这里写图片描述

结果:可以看到当程序在写时,读操作要等待。当写操作完成时,两个读操作几乎同时进行。


系列博客:
相信当你需要其中一个的时候,也一定需要剩下的两个

Linux OS内核 作业一:kthread和workqueue


Linux OS内核 作业二:多线程访问


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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值