这是操作系统第五次上机,主要是实现为MyLinux添加一个字符设备。
这次环境用的是自己制定的RedHat 2.6.18的内核
废话不多说,首先看基本知识:
系统调用是操作系统内核和应用程序之间的接口,而设备驱动程序是操作系统内核和机器硬件之间的接口。
设备驱动程序为应用程序屏蔽了硬件的细节,这样在应用程序看来,硬件设备只是一个设备文件, 应用程序可以像操作普通文件一样对硬件设备进行操作。
设备驱动程序是内核的一部分,它完成以下的功能:
(1) 对设备初始化和释放.
(2) 把数据从内核传送到硬件和从硬件读取数据.
(3) 读取应用程序传送给设备文件的数据和回送应用程序请求的数据.
(4) 检测和处理设备出现的错误.
Linux支持三中不同类型的设备:字符设备(character devices)、块设备(block devices)和网络设备(network interfaces)。
字符设备和块设备的主要区别是:在对字符设备发出读/写请求时,实际的硬件I/O一般就紧接着发生了。
块设备则不然,它利用一块系统内存作缓冲区,当用户进程对设备请求能满足用户的要求,就返回请求的数据,如果不能,就调用请求函数来进行实际的I/O操作。块设备是主要针对磁盘等慢速设备设计的,以免耗费过多的CPU时间来等待.
用户进程是通过设备文件来与实际的硬件打交道,每个设备文件都都有其文件属性(c/b),表示是字符设备还是块设备。
另外每个文件都有两个设备号,第一个是主设备号,标识驱动程序,第二个是从设备号,标识使用同一个设备驱动程序的不同的硬件设备,
比如有两个软盘,就可以用从设备号来区分他们.设备文件的的主设备号必须与设备驱动程序在登记时申请的主设备号一致,否则用户进程将无法访问到驱动程序.。
设备驱动程序工作的基本原理:
用户进程利用系统调用对设备进行诸如read/write操作,系统调用通过设备文件的主设备号找到相应的设备驱动程序,然后读取这个数据结构相应的函数指针,接着把控制权交给该函数。
最后,在用户进程调用驱动程序时,系统进入核心态,这时不再是抢先式调度。也就是说,系统必须在你的驱动程序的子函数返回后才能进行其他的工作。
如果你的驱动程序陷入死循环,你只有重新启动机器了。
编写自己的驱动程序源文件mydriver.c,这次上机主要就是看他的了~
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/uaccess.h>
#if CONFIG_MODVERSIONS == 1
#define MODVERSIONS
#include <linux/version.h>
#endif
#define DEVICE_NUM 0
#define REQ_CLEAR 0x909090
#define REQ_GETLEN 0x808080
#define REQ_GETCOUNT 0x707070
static int device_num = 0;
int isread=1;
int len=8;
static int total=0;
static char buffer[1024] = "你的学号";
static int open_nr = 0;
static int mydriver1_open(struct inode *inode, struct file *filp);
static int mydriver1_release(struct inode *inode, struct file* filp);
static ssize_t mydriver1_read(struct file *file, char __user *buf, size_t count, loff_t *f_pos);
static ssize_t mydriver1_write(struct file *file, const char __user *buf, size_t count, loff_t *f_pos);
static int mydriver1_ioctl ( struct inode *inode, struct file * filep,unsigned int cmd, unsigned long arg );
static struct file_operations mydriver1_fops = {
.read = mydriver1_read,
.write = mydriver1_write,
.open = mydriver1_open,
.release = mydriver1_release,
.ioctl = mydriver1_ioctl,
};
static int mydriver1_open(struct inode *inode, struct file *filp){
printk("\nMain device is %d, and the slave device is %d\n", MAJOR(inode->i_rdev), MINOR(inode->i_rdev));
if (open_nr == 0) {
open_nr++;
try_module_get(THIS_MODULE);
return 0;
}
else {
printk(KERN_ALERT "Another process open the char device.\n");
return -1;
}
}
static ssize_t mydriver1_read(struct file *file, char __user *buf, size_t count, loff_t *f_pos){
if (buf == NULL||isread==0)
return 0;
if (copy_to_user(buf, buffer, len)){
return -1;
}
return len;
}
static ssize_t mydriver1_write(struct file *file, const char __user *buf, size_t count, loff_t *f_pos){
if (buf == NULL)
return 0;
if(count>1024)
len=1024;
else
len=count;
if (copy_from_user(buffer, buf,len))
return -1;
total++;
isread=1;
return len;
}
static int mydriver1_release(struct inode *inode, struct file* filp){
open_nr--;
printk("The device is released!\n");
module_put(THIS_MODULE);
return 0;
}
static int mydriver1_ioctl (struct inode *inode, struct file * filep,
unsigned int cmd, unsigned long arg )
{
if ( cmd ==REQ_GETLEN){
return len;
};
if(cmd==REQ_CLEAR){
isread=0;
len=0;
return 0;
}
if(cmd==REQ_GETCOUNT)
return total;
}
static int __init mydriver1_init(void){
int result;
printk(KERN_ALERT "Begin to init Char Device!");
result=register_chrdev(device_num,"mydriver1",&mydriver1_fops);
if (result < 0) {
printk(KERN_WARNING "mydriver1: register failure\n");
return -1;
}
else {
printk("mydriver1: register success!\n");
device_num = result;
return 0;
}
}
static void __exit mydriver1_exit(void){
printk(KERN_ALERT "Unloading...\n");
unregister_chrdev(device_num, "mydriver1");
printk("unregister success!\n");
}
module_init(mydriver1_init);
module_exit(mydriver1_exit);
MODULE_LICENSE("GPL");
编译该设备驱动代码 然后将设备驱动源文件复制到/usr/src/linux-2.6.18/drivers/misc下,修改misc目录下的Makefile文件,只要在最后添加一句即可:obj-m +=mydriver.o。
在/usr/src/linux-2.6.18/drivers/misc路径下执行命令:make -C /usr/src/linux SUBDIRS=$PWD modules编译成功将得到mydriver.ko文件。
可以在misc目录下观察得到了mydriver.ko文件。
继续执行insmod ./mydriver.ko命令挂载内核中的模块。
然后通过lsmod命令可以看到增加的设备模块mydriver。
此时进入/proc/devices文件会看到在字符设备中有253 mydriver1。前面的是系统分配的主设备号,后面是设备注册名。
进入在/dev路径下,执行命令:
mknod /dev/mydriver1 c 253 0
第一个参数是新建设备文件的地址和名字。
第二个参数是指创建的是字符设备文件。
第三个参数是主设备号。
第四个参数是从设备号,自己随便取。
在这之后在将老师的测试文件编译执行,比如:./test /dev/mydriver1 "你的学号"
程序会自动给你打分的哦~