Linux实验四:字符类型设备的驱动程序实验

1 实验目的

内容1:
参考下面内容,完成实验
编写Makefile文件,使之具备如下功能:
输入make,将自动编译scull.c和scull_test.c两个文件,并生成scull.o和scull_test文件
输入make clean-all,将清除生成的所有文件
输入make driver和make clean-driver,则分别实现生成和删除scull.o文件
输入make test和make clean-test,则分别实现生成和删除scull_test文件编写一个简单的字符设备驱动程序
要求实现如下5个基本操作
scull_open()
scull_write()
scull_read()
scull_ioctl()
scull_release()
编写一个测试程序用来测试用户所编写的字符设备驱动程序

2 实验过程

一、 在tmp目录下创建test3文件夹用来装实验内容文件
在这里插入图片描述
二、 进行scull.h scull.c scull_test.c三个文件的编写
依次使用vi scull.h vi scull.c vi scull_test.c进行文件编写
在这里插入图片描述
scull.h:在这里插入图片描述

#ifndef _SCULL_H
#define _SCULL_H

struct scull_dev {
        void *data;   
        int quantum;  // the current quantum size
        int qset;  // the current array size
        unsigned long size;
        unsigned int access_key;  // used by sculluid and scullpriv
        unsigned int usage;  // lock the device while using it
        unsigned int new_msg;
        struct scull_dev *next;
};

struct scull_dev scull;

#include <linux/ioctl.h>

#define SCULL_MAJOR 111
#define SCULL_NAME "scull"
#define DEVICE_FILE "/dev/scull"

#define SCULL_MAGIC SCULL_MAJOR
#define SCULL_RESET _IO(SCULL_MAGIC,0)  // reset the data
#define SCULL_QUERY_NEW_MSG _IO(SCULL_MAGIC,1)  // check for new message
#define SCULL_QUERY_MSG_LENGTH _IO(SCULL_MAGIC,2)  // get message length
#define IOC_NEW_MSG 1

#endif

scull.c:在这里插入图片描述

#ifndef __KERNEL__
#define __KERNEL__
#endif
#ifndef MODULE
#define MODULE
#endif
#include <linux/version.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/sched.h>
#include <linux/ioport.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <asm/io.h>
#include <asm/segment.h>
#include <asm/uaccess.h>
#include "scull.h"

MODULE_LICENSE("GPL");
int new_msg;

static int Device_Open = 0;

int scull_open(struct inode *inode, struct file *filp){
        Device_Open++;
        printk("Char device %s is in open\n", SCULL_NAME);
        try_module_get(THIS_MODULE);
        return 0;
}

ssize_t scull_write(struct file *filp, const char *buffer, size_t count, loff_t *off){
	int cfu;

	if(count < 0)
		return -EINVAL;
	if(scull.usage || scull.new_msg)
		return -EBUSY;
	scull.usage = 1;
        	kfree(scull.data);
	scull.data = kmalloc(sizeof(char) * (count + 1), GFP_KERNEL);
	if(!scull.data){
		return -ENOMEM;
	}
	cfu = copy_from_user(scull.data, buffer, count+1);
	scull.usage=0;
	scull.new_msg=1;
	return count;
}

ssize_t scull_read(struct file *filp, char *buffer, size_t count, loff_t *off){
	int length, ctu;
	if(count < 0)
		return -EINVAL;
	if(scull.usage)
		return -EBUSY;
	scull.usage=1;
	if(count == 0)
		return 0;
	length = strlen(scull.data);
	if(length < count)
		count = length;
	ctu = copy_to_user(buffer, scull.data, count+1);
	scull.new_msg = 0;
	scull.usage = 0;
	return count;
}

int scull_release(struct inode *inode, struct file *filp){
        Device_Open--;
        printk("Char device %s is in release\n", SCULL_NAME);
        module_put(THIS_MODULE);

        return 0;
}

long int unlocked_ioctl(struct file *filp,unsigned int cmd,unsigned long arg){
	switch(cmd){
		case SCULL_RESET:
			kfree(scull.data);
			scull.data = NULL;
			scull.usage = 0;
			scull.new_msg = 0;			
			break;
		case SCULL_QUERY_NEW_MSG:
			if(scull.new_msg)
				return IOC_NEW_MSG;
			break;
		case SCULL_QUERY_MSG_LENGTH:
			if(scull.data == NULL) {
				return 0;
			} else {
				return strlen(scull.data);
			}
			break;
		default:
			return -ENOTTY;
	}

	return 0;
}

struct file_operations scull_chops={
	read: scull_read,
	write: scull_write,
	unlocked_ioctl: unlocked_ioctl,
	open: scull_open,
	release: scull_release
};

int init_scull(void){
	int result;
	printk("Initializing char device %s.\n", SCULL_NAME);
	result=register_chrdev(SCULL_MAJOR, SCULL_NAME, &scull_chops);
	if(result < 0){
		printk("Scull: Can't get major number!\n");
		return result;
	}

	return 0;
}

void cleanup_scull(void){
	unregister_chrdev(SCULL_MAJOR, SCULL_NAME);
	printk("Cleanup char device %s.\n", SCULL_NAME);
}


module_init(init_scull);
module_exit(cleanup_scull);

scull_test.c:在这里插入图片描述

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include "scull.h"

void write_proc(void);
void read_proc(void);

int main(int argc, char **argv) {
	if(argc == 1) {
		puts("Usage: scull_test [write|read]");
		exit(0);
	}

	if(!strcmp(argv[1], "write")) {
		write_proc();
	}
	else if(!strcmp(argv[1], "read")) {
		read_proc();
	}
	else {
		puts("scull_test: invalid command!");
	}

	return 0;
}

void write_proc() {
	int fd, len, quit = 0;
	char buf[100];
	fd = open(DEVICE_FILE, O_WRONLY);
	if(fd <= 0) {
		printf("Error opening device file %s for writing!\n", DEVICE_FILE);
		exit(1);
	}
	printf("input 'exit' to exit!");
	while(!quit) {
		printf("\n write>>   ");
		fgets(buf, 100, stdin);
		if(!strcmp(buf, "exit\n"))
			quit = 1;
		while(ioctl(fd, SCULL_QUERY_NEW_MSG))
			usleep(1000);
		len=write(fd, buf, strlen(buf));
		if(len < 0) {
			printf("Error writing to device %s!\n", SCULL_NAME);
			close(fd);
			exit(1);
		}
		printf("%d bytes written to device %s!\n", len - 1, SCULL_NAME);
	}
	//free(buf);
	close(fd);
}

void read_proc() {
	int fd, len, quit = 0;
	char *buf = NULL;
	fd = open(DEVICE_FILE, O_RDONLY);
	if(fd<0) {
		printf("Error opening device file %s for reading!\n", DEVICE_FILE);
		exit(1);
	}
	while(!quit) {
		printf("\n read<<   ");
		while(!ioctl(fd, SCULL_QUERY_NEW_MSG))
			usleep(1000);
		// get the msg length
		len=ioctl(fd, SCULL_QUERY_MSG_LENGTH, NULL);
		if(len) {
			if(buf!=NULL)
				free(buf);
			buf = malloc(sizeof(char) * (len+1));
			len = read(fd, buf, len);
			if(len < 0) {
				printf("Error reading from device %s!", SCULL_NAME);
			}
			else {
				if(!strcmp(buf, "exit\n")) {
					ioctl(fd, SCULL_RESET);     // reset
					quit = 1;
					printf("%s\n",buf);
				}
				else 
					printf("%s\n",buf);
			}
		}
	}
	free(buf);
	close(fd);
}

三、 编写创建Makefile文件
使用vim Makefile指令进行Makefile文件的编写
在这里插入图片描述
Makefile:在这里插入图片描述

obj-m+=scull.o
KDIR=/lib/modules/$(shell uname -r)/build
PWD:=$(shell pwd)

scull.o: scull.c
	make -C $(KDIR) M=${PWD} modules  # 在内核源码环境下编译scull.c
	gcc -o scull_test.o scull_test.c  # 编译scull_test.c 

	insmod scull.ko  # 加载scull这个字符驱动设备

	mknod /dev/scull c 111 0  # 创建一个设备文件

clean-all:
	rm -rf *.ko *.mod.c *.o modules.* Module.symvers /dev/scull
driver:
	make -C $(KDIR) M=${PWD} modules  # 在内核源码环境下编译scull.c
clean-driver:
	rm -f scull.o
test:
	gcc -o scull_test.o scull_test.c  # 编译scull_test.c 
clean-test:
	rm -f scull_test.o

.PHONY: clean clean-all driver clean-driver

四、 模块加载运行与卸载
首先获取管理员权限,以防因权限问题导致的错误
输入sudo su,再输入密码即可获取权限

执行make指令
在这里插入图片描述
使用make test,生成可执行文件scull_test.o,然后输入./scull_test.o write./scull_test.o read运行它,输入exit退出程序在这里插入图片描述在这里插入图片描述
输入rmmod scull卸载模块,并输入lsmod确认正在运行的模块中没有scull在这里插入图片描述在这里插入图片描述
最后使用dmesg | grep scull,查看日志内容在这里插入图片描述

  • 7
    点赞
  • 36
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
### 回答1: 实验主要是通过编写Linux设备驱动程序来学习Linux系统的设备驱动开发,加深对Linux内核机制的理解以及了解驱动程序的核心模块、初始化、卸载等操作。在实验过程中,我们先了解了Linux系统下设备的分类,比如字符设备和块设备,并参照示例程序实现了一个字符设备驱动程序。在编写驱动程序的过程中,我们学习了设备文件的创建、设备初始化函数、文件操作函数、I/O控制函数等相关概念和知识,并通过实践掌握了一些具体技能,如打印调试信息、使用Sysfs文件系统等。 通过本次实验,我们深入了解了Linux设备驱动程序的工作原理和编写方法,能够熟练掌握设备文件的创建、设备初始化函数、文件操作函数、驱动模块的加载和卸载等基本概念及应用方法,从而可以为Linux系统开发相关的设备驱动。同时,我们还了解了Linux内核中常用的调试技巧和工具,这些对我们日后的开发和调试工作都将有很大的帮助。通过本次实验,我们进一步提高了对Linux系统的认识和理解,为日后的系统开发奠定了坚实的基础。 ### 回答2: Linux设备驱动程序开发是基于Linux系统的硬件设备的管理和操作,这个过程需要开发者理解设备特性、熟悉Linux内核的结构。本实验旨在让学生学习Linux设备驱动程序的基础知识和基本操作,了解内核模块的加载和卸载,设备驱动程序的注册和注销等。 本次实验的难点在于学生需要理解设备的工作原理,选取合适的设备进行开发并且需要掌握相关的编程技巧。除此之外,还需要学习工具链使用技巧,包括gcc编译器、makefile生成器、gdb调试器、insmod等命令,此外还需要熟悉Linux内核模块的编写和调试。这些技能都是Linux驱动程序开发必备的技能。 通过本次实验,学生将会了解到Linux驱动程序编写流程、编程模式、数据结构等基础知识,掌握设备驱动程序的注册和卸载方法,了解内核模块机制,熟悉常见的硬件设备文件系统接口的使用。这些知识将对于学生今后的Linux系统开发和驱动开发有很大的帮助。 ### 回答3: 本次实验的目标是学习linux设备驱动程序的开发。在实验中,我们首先学习了内核模块的编写和加载,了解了内核模块的结构和用法,并通过编写简单的内核模块来巩固理解。 之后,我们学习了字符设备驱动程序编写,包括了设备文件的创建和使用以及驱动程序的基本结构和函数。通过实现一个简单的字符设备驱动程序,我们了解了驱动程序与用户空间的交互方式和驱动程序的工作原理。 此外,本次实验还包括了中断处理程序的开发,我们学习了中断的触发和处理方式,并通过在驱动程序中实现中断处理程序来巩固理解。 通过本次实验,我们掌握了linux设备驱动程序的开发方法,更深入地了解了操作系统中内核部分的工作原理,对于深入理解操作系统有着重要的意义。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值