操作系统之设备管理

一、实验目的

       1、通过实验,进一步了解设备独立性的概念;

2、探索、分析、理解并掌握 Linux 设备驱动的设计模型、实现机制和编程要旨。;

3、掌握Linux下可装入模块的设计与实现方法。

二、实验内容

1、编写一个可读写的字符设备驱动程序,并作为可装入模块加载到系统中去。

2、设计相应的示例程序,在用户进程中使用该设备驱动程序进行字符数据的读写。

三、设计原理(或方案)及相关算法

1.申请设备

2.删除设备

 3.释放设备

四、结果分析(可根据需要附加页)

1.执行make命令后

  

查看生成的文件

 2.加载设备驱动程序

加载失败的情况:

 加载成功的情况:

 3.建立设备文件节点

先查看主设备号

 

 分析:从中可以看出mychardev的主设备号为236。

建立设备节点和查看设备文件内容。

 查看设备节点建立的情况。

 

 查看/proc 下 proc 文件系统记录的设 备模块文件中 modules 中有无加载的模块

4.运行read_dev程序

 

 分析:换行符也计算在内。

五、源程序

1.mychardev.c

/* mychardev.c 创建一个只读的文件系统 */
#ifndef MODULE
#define MODULE
#endif
/* 字符设备所需*/
#include "linux/kernel.h"
#include "linux/module.h"
#include "linux/fs.h"
#include "linux/init.h"
#include "linux/types.h"
#include "linux/errno.h"
#include "linux/uaccess.h"
#include "linux/kdev_t.h"
#define SUCCESS 0
/* 设备定义 **********************/
/* 设备名, 它将出现在 /proc/devices */
#define DEVICE_NAME "mychardev"
/* 该设备最大信息长度 */
#define BUF_LEN 80
/* 设备否打开?利用它防止当前进程使用同一设备*/
static int Device_Open = 0;
/* 提示信息 */
char Message[BUF_LEN];
/* 进程读取的信息的指针 */
char *Message_Ptr;
/* 当进程试图打开设备文件时调用该函数*/
static int device_open( struct inode *inode, struct file *file )
{
	static int counter = 0;
#ifdef DEBUG
	printk( "device_open(%p,%p)\n", inode, file );
#endif
	/* 两个进程不得同时对同一设备操作.*/
	if ( Device_Open )
		return(-EBUSY);
	Device_Open++;
	/* 初始化信息. */
	sprintf( Message,
		 "If I told you once, I told you %d times - Hello, world!\n",
		 counter++ );


	/* 仅在输出信息的最大长度大于 BUF_LEN, (这儿是 80)使用 sprintf.
	 * 注意不得超过缓冲区长度,尤其是在内核中!! */
	Message_Ptr = Message;
	try_module_get( THIS_MODULE );
	return(SUCCESS);
}


/* 当一个进程要关闭这个设备时,该函数被调用,这个调用不允许失败.*/
static int device_release( struct inode *inode, struct file *file )
{
#ifdef DEBUG
	printk( "device_release(%p,%p)\n", inode, file );
#endif

	Device_Open--;
	/* 设备文件使用计数器减 1*/
	module_put( THIS_MODULE );
	return(0);
}


/* 当一进程要从已打开的设备文件读数据时该函数被调用.*/
static ssize_t device_read( struct file *file,
			    char *buffer,                       /* 获得填充数据的缓冲区*/
			    size_t length, loff_t *fops )       /*
                                                                 * 缓冲区中的数据长度 (绝对不能越界!) */
{
	/* 已写入到缓冲区 buffer 中的确切字节数*/
	int	bytes_read = 0;
	char	*ptr;
	int	len = 0;
#ifdef DEBUG
	printk( "device_read(%p,%p,%p,%d)\n",
		inode, file, buffer, length );
#endif
	/* 如果已位于信息尾部,返回 0*/
	if ( *Message_Ptr == 0 )
		return(0);
	ptr = Message_Ptr;
	len = 0;
	/*取字符串的长度*/
	while ( *(ptr++) )
		len++;
	if ( length < len )
		len = length;
	copy_to_user( buffer, Message_Ptr, len );
	/* 准确地把数据送到缓冲区 buffer*/

	bytes_read = len;
#ifdef DEBUG
	printk( "Read %d bytes, %d left\n",
		bytes_read, length );
#endif
	/* 返回读取的实际字节数据 */
	return(bytes_read);
}


/* 当进程向这设备写(目前不支持)时调用本函数*/
static ssize_t device_write( struct file *file,
			     const char *buffer,
			     size_t length, loff_t *fops )
{
#ifdef DEBUG
	printk( "device_write(%p,%p,%s,%d)",
		inode, file, buffer, length );
#endif
	int len;
	if ( length > 80 )
		len = 80;
	else
		len = length;
	copy_from_user( Message_Ptr, buffer, len );
	return(-EINVAL);
}


/* 模块定义 ********************** */
MODULE_LICENSE("GPL");/*“GPL” 是指明了这是GNU General Public License的任意版本*/

/* 设备的主设备号*/
static int Major;

/* 当进程要对创建的设备进行某些操作时,这个结构存放了要调用的函数的入口
 * 这个结构有系统设备表的指针指向。NULL 表示未实现该功能。*/
struct file_operations Fops = {
	read: device_read,
	write: device_write,
	open: device_open,
	release:device_release /* a.k.a. close */
};
/* 初始化模块--注册字符设备 */
int init_module()
{
	/* 注册字符设备(至少一次) */
	Major = register_chrdev( 0,
				 DEVICE_NAME,
				 &Fops );
	/* 负值意味出错 */
	if ( Major < 0 )
	{
		printk( "Sorry, registering the character device failed with %d\n",
			Major );
		return(Major);
	}
	printk( "Registeration is a success. The major device number is %d.\n",
		Major );
	printk( "If you want to talk to the device driver, you'll have to\n" );
	printk( "create a device file. We suggest you use:\n" );
	printk( "mknod <name> c %d 0\n", Major );
	return(0);
}


/* 清除模块 --从 /proc 中注销合适的文件*/
void cleanup_module()
{
	/* 注销设备 */
	unregister_chrdev( Major, DEVICE_NAME );
	printk( "Device released!\n" );
}

2.Makefile

obj-m := mychardev.o #这里是上面所创建的 c 文件名.o 
PWD := $(shell pwd) 
KERNELDIR := /lib/modules/$(shell uname -r)/build # directory for include head file 
all:
	$(MAKE) -C $(KERNELDIR) M=$(PWD) modules 
.PHONY: clean 
clean: 
	rm -rf *.o .$(basename $(obj-m)).* *~ core *.ko *.mod.c modules.order Module.symvers

3.read_dev.c
/*read_dev.c 使用用户设备驱动程序的示例*/ 
#include <stdio.h> 
#include <linux/fs.h> 
main() 
{ 
	int i,j,num; 
    int f; 
	char buffer[80];
	f=open("/dev/mychardev",0); 
    printf("How many chars do you read:(1-80)"); 
    scanf("%d",&num); 
    i=read(f,buffer,num); 
    j=0; 
    while (j<i)
    { 
    	printf("%c",buffer[j]); 
    	j++; 
	} 
    close(f); 
    printf("\n"); 
}    

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值