Linux作業系統程式設計 實驗四 字元裝置實驗
阿新 • • 發佈:2020-12-21
實驗目的
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\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
點選檢視詳細內容
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,檢視日誌內容