zoukankan      html  css  js  c++  java
  • Linux操作系统编程 实验四 字符设备实验

    实验目的

    1、了解Linux操作系统中的设备驱动程序的组成
    2、编写简单的字符设备驱动程序并进行测试
    3、理解Linux操作系统的设备管理管理机制
    4、实验内容:
    编写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()

    编写一个测试程序用来测试用户所编写的字符设备驱动程序

    实验记录

    我的虚拟机版本Ubuntu 20.04.1 x64,内核版本5.4.0-42-generic。

    切换到root权限,随后编写scull.h、scull.c、scull_test.c和Makefile

    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
    ", 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
    ", 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.
    ", SCULL_NAME);
    	result=register_chrdev(SCULL_MAJOR, SCULL_NAME, &scull_chops);
    	if(result < 0){
    		printk("Scull: Can't get major number!
    ");
    		return result;
    	}
    
    	return 0;
    }
    
    void cleanup_scull(void){
    	unregister_chrdev(SCULL_MAJOR, SCULL_NAME);
    	printk("Cleanup char device %s.
    ", 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!
    ", DEVICE_FILE);
    		exit(1);
    	}
    	printf("input 'exit' to exit!");
    	while(!quit) {
    		printf("
     write>>   ");
    		fgets(buf, 100, stdin);
    		if(!strcmp(buf, "exit
    "))
    			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!
    ", SCULL_NAME);
    			close(fd);
    			exit(1);
    		}
    		printf("%d bytes written to device %s!
    ", 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!
    ", DEVICE_FILE);
    		exit(1);
    	}
    	while(!quit) {
    		printf("
     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
    ")) {
    					ioctl(fd, SCULL_RESET);     // reset
    					quit = 1;
    					printf("%s
    ",buf);
    				}
    				else 
    					printf("%s
    ",buf);
    			}
    		}
    	}
    	free(buf);
    	close(fd);
    }
    

    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
    

    实验过程

    执行make命令

    使用make test,生成可执行文件scull_test.o,然后输入./scull_test.o write和./scull_test.o read运行它,输入exit退出程序

    输入rmmod scull卸载模块,并确认正在运行的模块中没有scull

    最后使用dmesg | grep scull,查看日志内容

  • 相关阅读:
    十六进制转十进制
    十进制转十六进制
    历届试题 高僧斗法
    历届试题 错误票据
    历届试题 大臣的旅费
    历届试题 九宫重排/八数码问题
    Skip the Class
    历届试题 剪格子
    leetcode 337. House Robber III
    猿辅导 2019年 校招提前批笔试
  • 原文地址:https://www.cnblogs.com/ast935478677/p/14167484.html
Copyright © 2011-2022 走看看