把自己认为有保留价值的几个代码片断给放在这里,以供参考!
1.进行proc文件系统编程,在内核中维护一张表格(表格记录了学生的姓名和学号),可以向表格添加记录,显示记录和删除记录,并编写一个用户程序进行测试
老师给的答案(好像有问题):
/*
* a mini module do nothing
* test passed on linux redhat 9 'kernel 2.4.20-8'
* Compile:
* gcc -c -I/usr/src/linux/include -Wall procf.c
*/
//#define __NO_VERSION__
#define LINUX
#define __KERNEL__
#define MODULE
#include <linux/kernel.h> /* program in kernel */
#include <linux/module.h> /* program as module */
#include <linux/proc_fs.h>
#include <asm/uaccess.h>
#define KBUILD_MODNAME procf
char content[100] = "this my file content";
int len = 20;
int my_read_proc(char *buf, char **start, off_t offset, int xlen, int *eof, void *data)
{
// copy_to_user(buf, content, len);
int i;
for(i=0; i<len+1; i++) buf[i] = content[i];
return len;
}
int my_write_proc(struct file *file, const char *buf,
unsigned long count, void *data)
{
int i;
if( count > 99 ) count = 99;
// copy_from_user(content, buf, count);
for(i=0; i<count; i++) content[i] = buf[i];
len = i;
content[i] = '/0';
return len;
}
struct proc_dir_entry my_entry = {
0,
6, /* unsigned short namelen */
"myname", /* const char *name */
S_IFREG | S_IRUGO, /* mode_t mode */
1, /* nlink_t nlink */
0, /* uid_t uid */
0, /* gid_t gid */
2, /* unsigned long size */
NULL, /* struct inode_operations *ops */
NULL,
NULL,
NULL, NULL, NULL,
NULL,
my_read_proc,
my_write_proc,
NULL,
0,
0
};
int init_module()
{
int i;
for(i=49, len =0; i<59; i++){
content[i-49] = i;
len ++;
}
content[i] = '/0';
proc_register(&proc_root, &my_entry);
printk("Hello world/n");
return 0;
}
void cleanup_module()
{
proc_unregister(&proc_root, my_entry.low_ino);
printk("bye/n");
}
MODULE_LICENSE("GPL");
自己改造过的答案:
#define LINUX
#define __KERNEL__
#define MODULE
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/proc_fs.h>
#include <linux/sched.h>
#include <asm/uaccess.h>
//#include <string.h>
//#include <stdio.h>
#define NAMELEN 10
#define IDLEN 10
#define STRINGLEN 100
struct person_information{
char name[NAMELEN];
char id[IDLEN];
};
char global_buffer[1024];
struct person_information global_buffer_student[STRINGLEN];
//struct proc_dir_entry student_file = {
// 0,
// strlen("student_file"), /*unsigned short namelen*/
// "student_file", /*const char* name*/
// S_IFREG|S_IRUGO, /*mode_t mode*/
// 1, /*nlink_t nlink*/
// 0, /*uid_t uid*/
// 0, /*gid_t gid*/
// 2, /*unsigned long size*/
// NULL, /*struct inode_operation *pos*/
// NULL,
// NULL,
// NULL,NULL,NULL,
// NULL,
// proc_read_student,
// proc_write_student,
// NULL,
// 0,
// 0
//};
struct proc_dir_entry *student_file;
struct proc_dir_entry *example_dir;
struct proc_dir_entry *current_file;
struct proc_dir_entry *hello_file;
struct proc_dir_entry *symlink;
int proc_read_student(char* page,char** start,off_t off,int count,int* eof,void* data);
int proc_write_student(struct file *file,const char *buffer,unsigned long count,void* data);
int proc_read_current(char* page,char** start,off_t off,int count,int *eof,void* data);
int proc_read_hello(char* page,char** start,off_t off,int count,int* eof,void* data);
int proc_write_hello(struct file *file,const char *buffer,unsigned long count,void* data);
void init_student(){
int i=0;
for(i;i<STRINGLEN;++i){
memset(global_buffer_student[i].name,'0'+i,NAMELEN);
memset(global_buffer_student[i].id,i,IDLEN);
}
}
int proc_read_student(char* page,char** start,off_t off,int count,int* eof,void* data){
int len;
MOD_INC_USE_COUNT;
len = sprintf(page,"golbal_buffer==%s/n",global_buffer);
int i;
//init_student();
i=0;
// for(i=0;i<STRINGLEN;++i){
printk("name==%s,id==%s/n",global_buffer_student[i].name,global_buffer_student[i].id);
// }
MOD_DEC_USE_COUNT;
return len;
}
int proc_write_student(struct file *file,const char *buffer,unsigned long count,void* data){
int len;
MOD_INC_USE_COUNT;
if(count>=STRINGLEN)
len = STRINGLEN-1;
else
len = count;
copy_from_user(global_buffer_student,buffer,len);
global_buffer[len]='/0';
MOD_DEC_USE_COUNT;
return 0;
}
int init_module(){
example_dir = proc_mkdir("proc_test",NULL);
example_dir->owner = THIS_MODULE;
current_file = create_proc_read_entry("current",666,example_dir,proc_read_current,NULL);
current_file->owner = THIS_MODULE;
hello_file = create_proc_entry("hello",666,example_dir);
strcpy(global_buffer,"this is a global_buffer/n");
hello_file->read_proc = proc_read_hello;
hello_file->write_proc = proc_write_hello;
hello_file->owner = THIS_MODULE;
student_file = create_proc_entry("student",666,example_dir);
student_file->read_proc = proc_read_student;
student_file->write_proc = proc_write_student;
student_file->owner = THIS_MODULE;
symlink = proc_symlink("current_symlink",example_dir,"current");
symlink->owner = THIS_MODULE;
init_student();
//proc_register(&proc_root,&proc_read_student);
//int i=0;
//for(i;i<STRINGLEN;++i){
// memset(global_buffer_student[i].name,0,NAMELEN);
// memset(global_buffer_student[i].id,0,IDLEN);
//}
printk("init_module msg form tested_proc.c/n");
return 0;
}
int proc_read_current(char* page,char** start,off_t off,int count,int *eof,void* data){
int len;
MOD_INC_USE_COUNT;
len = sprintf(page,"current process usages:/nname:%s/ngid:%d/npid:%d/n",current->comm,current->pgrp,current->pid);
MOD_DEC_USE_COUNT;
return len;
}
//void proc_write_information(){
//}
int proc_read_hello(char* page,char** start,off_t off,int count,int* eof,void* data){
int len;
MOD_INC_USE_COUNT;
len = sprintf(page,"hello message:/n %s write: %s /n",current->comm,global_buffer);
MOD_DEC_USE_COUNT;
return len;
}
int proc_write_hello(struct file *file,const char *buffer,unsigned long count,void* data){ int len;
MOD_INC_USE_COUNT;
if(count >= 1024)
len = 1024-1;
else
len = count;
copy_from_user(global_buffer,buffer,len);
global_buffer[len]='/0';
MOD_DEC_USE_COUNT;
return len;
}
void cleanup_module(){
remove_proc_entry("current_symlink",example_dir);
remove_proc_entry("hello",example_dir);
remove_proc_entry("student",example_dir);
remove_proc_entry("current",example_dir);
remove_proc_entry("proc_test",NULL);
//proc_unregister(&proc_root,proc_read_student.low_ino);
printk("cleanup_module msg form tested_proc.c/n");
}
MODULE_LICENSE("GPL");
2.示例如何使用模块
/* rwbuf: read/write a buffer
* a demo on how to build a device in a kernel module
* test pass with Mandrake 7(Kernel 2.2.14) in vmare, 2005/12
* test pass with RedHat 9(Kernel 2.4.20-8) in vmware, 2005/12
*
* how to test ?
* so :
*
* $ first you must be ROOT
compile in Kernel 2.4
* # gcc -I/usr/src/linux/include/ -D KERNEL2_4 -c rwbuf.c
compile in Kernel 2.2
* # gcc -I/usr/src/linux/include/ -c rwbuf.c
* # insmod rwbuf.o // you should see that 'rwbuf' in there
* # lsmod
* # mknod /dev/rwbuf c 60 0 // this can do only once
* # // now you can use /dev/rwbuf
*/
#define LINUX
#define __KERNEL__
#define MODULE
#define KBUILD_MODNAME rwbuf
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
#ifdef KERNEL2_4
#define HAVE_MEMCPY /* no export 'memcpy' in kernel 2.2 */
#define HAVE_MOD_LIC /* no Module License in kernel 2.2 */
#endif
#ifndef HAVE_MEMCPY
#include <asm/uaccess.h> /* copy_to_user, copy_from_user */
#endif
#define RWBUF_prefix rwbuf // not used yet
#define RWBUF_NAME "rwbuf" // device's symbol name
#define RWBUF_ID 60 // device's major no
#define RW_CLEAR 0x909090 // IO ctl command
#define rwbuf_size 1000 // 1k enough
static unsigned char rwbuf[rwbuf_size+1]; //
static int rwlen=0; // how many bytes used in <rwbuf>
static int inuse=0; // only one process permited at the same time
static int rwbuf_open(struct inode *inode,struct file * filep)
{
if (inuse==1) // the buf is used by another process, so fail to open
return -1;
inuse=1;
MOD_INC_USE_COUNT; // don't ask what this means
return 0;
}
static int rwbuf_close(struct inode *inode,struct file * filep)
{
inuse=0;
MOD_DEC_USE_COUNT;
return 0;
}
static ssize_t rwbuf_write(struct file * filep,
const char *buf, size_t count, loff_t * ppos)
{
if (count>rwbuf_size)
count = rwbuf_size;
#ifndef HAVE_MEMCPY /* no export 'memcpy' in kernel 2.2 */
copy_from_user(rwbuf, buf, count);
#else
memcpy(rwbuf, buf, count); // as you see
#endif
rwlen = count;
printk("user-to-kernel: %s/n", rwbuf);
return count;
}
static ssize_t rwbuf_read(struct file* filep,
char * buf, size_t count, loff_t* ppos)
{
if (count>rwlen)
count = rwlen;
#ifndef HAVE_MEMCPY /* no export 'memcpy' in kernel 2.2 */
copy_to_user(buf, rwbuf, count);
#else
memcpy(buf, rwbuf, count);
#endif
printk("kernel-to-user: %s/n", buf);
return count;
}
static int rwbuf_ioctl(struct inode *inode,struct file * filep,
unsigned int cmd,unsigned long arg)
{
switch (cmd)
{
case RW_CLEAR:
rwlen = 0; // clear buf by set its len to zero
printk("rwbuf in kernel zero-ed/n");
break;
default:
break;
};
return 0;
}
static struct file_operations rwbuf_fops =
{
open: rwbuf_open,
release: rwbuf_close,
read: rwbuf_read,
write: rwbuf_write,
ioctl: rwbuf_ioctl,
};
int init_module()
{
printk("Hello world/n");
if (register_chrdev(RWBUF_ID, RWBUF_NAME, &rwbuf_fops))
printk("register error/n");
else
printk("register ok/n");
return 0;
}
void cleanup_module()
{
if (unregister_chrdev(RWBUF_ID, RWBUF_NAME)!=0)
printk("unreg err/n");
else
printk("unreg ok/n");
printk("bye/n");
}
#ifdef HAVE_MOD_LIC
MODULE_LICENSE("GPL");
#endif
3.了解和熟悉Linux中设备驱动程序的概念和设计,能独立编写一个简单的字符型或是块型设备驱动程序
------------------------radimo.h---------------------------------------
#define RADIMO_MAJOR 42
#define RADIMO_TIMER_DELAY 60*HZ
/* msg masks */
#define RADIMO_OPEN 1
#define RADIMO_IOCTL 2
#define RADIMO_INFO 4
#define RADIMO_REQUEST 8
#define RADIMO_TIMER 16
#define RADIMO_ERROR 32
#ifndef MSG_MASK
#define MSG_MASK ( RADIMO_IOCTL | RADIMO_INFO | RADIMO_ERROR | RADIMO_REQUEST | RADIMO_OPEN)
#endif
#define MSG(mask, string, args...) /
if (MSG_MASK & mask) printk(KERN_DEBUG "radimo: " string, ##args)
------------------------radimo.c---------------------------------------
/*
* Sample Ram DIsk Module, Radimo
*
*/
#include <linux/module.h>
#if defined(CONFIG_SMP)
#define __SMP__
#endif
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/timer.h>
#include <linux/fs.h>
#include <linux/vmalloc.h>
#include <asm/uaccess.h>
#include "radimo.h"
/*以上定义了一些常用的头文件*/
#define MAJOR_NR RADIMO_MAJOR // 定义了RADIMO的主设备号
#define DEVICE_NAME "radimo" // 定义了RADIMO的设备名
#define DEVICE_NR(device) (MINOR(device))
#define DEVICE_ON(device)
#define DEVICE_OFF(device)
#define DEVICE_NO_RANDOM
#include <linux/blk.h>
#define RADIMO_HARDS_BITS 9 /* 2**9 byte hardware sector */
#define RADIMO_BLOCK_SIZE 1024 /* block size */
//定义了一个块的大小,以字节为单位
#define RADIMO_TOTAL_SIZE 2048 /* size in blocks */
//定义了这个虚拟盘的容量,以块为单位
/* the storage pool */
static char *radimo_storage; //这个指针是全局变量,指向用于虚拟盘的内存
static int radimo_hard = 1 << RADIMO_HARDS_BITS;
static int radimo_soft = RADIMO_BLOCK_SIZE;
static int radimo_size = RADIMO_TOTAL_SIZE;
static int radimo_readahead = 4;
struct timer_list radimo_timer;
//定义了一个定时器,这个程序中我们并没有用到。这里作为一个使用定时器的例子
/* forward declarations for _fops */
static int radimo_open(struct inode *inode, struct file *file);
static int radimo_release(struct inode *inode, struct file *file);
static int radimo_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg);
//定义了三个常用的接口函数
static struct block_device_operations radimo_fops = {
open: radimo_open,
release: radimo_release,
ioctl: radimo_ioctl,
};
//用一个结构把三个接口函数组装起来,以便通知操作系统
static int radimo_request(request_queue_t *request_queue, int rw, struct buffer_head *sbh)
{ //【重要】这是块设备驱动程序处理读写请求的函数,是本程序中最重要的部分
unsigned long offset, total;
//int *make_oops = NULL; // CR: access to this pointer will cause Oops, for debug only.
MSG(RADIMO_REQUEST, "%s sector rsector = %lu, blocknr = %lu/n",
rw == READ ? "read" : "write",
sbh->b_rsector,
sbh->b_blocknr);
//MSG宏的定义在radimo.h中,相当于printk,
//用于在log文件中打印一行,以便调试
offset = sbh->b_rsector * radimo_hard;
total = (unsigned long)sbh->b_size;
//计算需要访问的地址和大小
/* access beyond end of the device */
if (total+offset > radimo_size * (radimo_hard << 1)) {
MSG(RADIMO_REQUEST, "Error: access beyond end of the device");
/* error in request */
buffer_IO_error(sbh);
return 0;
}
//判断访问地址是否越界
MSG(RADIMO_REQUEST, "offset = %lu, total = %lu/n", offset, total);
if (rw == READ || rw==READA) { //如果是读操作,从虚拟盘的内存中复制数据到缓冲区中
memcpy(bh_kmap(sbh), radimo_storage+offset, total);
} else if (rw == WRITE) { //如果是写操作,从缓冲区中复制数据到虚拟盘的内存中
memcpy(radimo_storage+offset, bh_kmap(sbh), total);
} else { /* can't happen */
MSG(RADIMO_ERROR, "cmd == %d is invalid/n", rw);
}
/* successful */
sbh->b_end_io(sbh,1); //结束读写操作
return 0;
}
void radimo_timer_fn(unsigned long data)
{ //这是用于定时器处理的函数,本程序中没有用到,但是可以作为使用定时器的框架
/* set it up again */
radimo_timer.expires = RADIMO_TIMER_DELAY + jiffies;
add_timer(&radimo_timer);
}
static int radimo_release(struct inode *inode, struct file *file)
{ //关闭设备时调用
MSG(RADIMO_OPEN, "closed/n");
MOD_DEC_USE_COUNT; //减少引用计数
return 0;
}
static int radimo_open(struct inode *inode, struct file *file)
{ //打开设备时调用
MSG(RADIMO_OPEN, "opened/n");
MOD_INC_USE_COUNT; //增加引用计数
/* timer function needs device to invalidate buffers. pass it as
data. */
radimo_timer.data = inode->i_rdev;
radimo_timer.expires = RADIMO_TIMER_DELAY + jiffies;
radimo_timer.function = &radimo_timer_fn;
if (!timer_pending(&radimo_timer))
add_timer(&radimo_timer);
//以上几行是设置定时器,本程序中没有用到定时器
return 0;
}
static int radimo_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
{ //响应一些特殊的操作,这些操作可以自己定义
unsigned int minor;
if (!inode || !inode->i_rdev)
return -EINVAL;
minor = MINOR(inode->i_rdev);
switch (cmd) {
case BLKFLSBUF: { //将缓冲写回存储区的操作
/* flush buffers */
MSG(RADIMO_IOCTL, "ioctl: BLKFLSBUF/n");
/* deny all but root */
if (!capable(CAP_SYS_ADMIN))
return -EACCES;
fsync_dev(inode->i_rdev);
invalidate_buffers(inode->i_rdev);
break;
}
case BLKGETSIZE: { //得到设备容量的操作
/* return device size */
MSG(RADIMO_IOCTL, "ioctl: BLKGETSIZE/n");
if (!arg)
return -EINVAL;
return put_user(radimo_size*2, (long *) arg);
}
case BLKRASET: { //设置设备预读值的操作
/* set read ahead value */
int tmp;
MSG(RADIMO_IOCTL, "ioctl: BLKRASET/n");
if (get_user(tmp, (long *)arg))
return -EINVAL;
if (tmp > 0xff)
return -EINVAL;
read_ahead[RADIMO_MAJOR] = tmp;
return 0;
}
case BLKRAGET: { //得到设备预读值的操作
/* return read ahead value */
MSG(RADIMO_IOCTL, "ioctl: BLKRAGET/n");
if (!arg)
return -EINVAL;
return put_user(read_ahead[RADIMO_MAJOR], (long *)arg);
}
case BLKSSZGET: { //得到设备块大小的操作
/* return block size */
MSG(RADIMO_IOCTL, "ioctl: BLKSSZGET/n");
if (!arg)
return -EINVAL;
return put_user(radimo_soft, (long *)arg);
}
default: { //其他操作
MSG(RADIMO_ERROR, "ioctl wanted %u/n", cmd);
return -ENOTTY;
}
}
return 0;
}
int init_module(void)
{ //在模块被加载的时候调用
int res;
/* 块大小必须是扇区大小的整数倍 */
if (radimo_soft & ((1 << RADIMO_HARDS_BITS)-1)) {
MSG(RADIMO_ERROR, "Block size not a multiple of sector size/n");
return -EINVAL;
}
/* 分配存储空间 */
radimo_storage = (char *) vmalloc(1024*radimo_size);
if (radimo_storage == NULL) {
MSG(RADIMO_ERROR, "Not enough memory. Try a smaller size./n");
return -ENOMEM;
}
memset(radimo_storage, 0, 1024*radimo_size);
/* 【重要】向系统注册块设备 */
res = register_blkdev(RADIMO_MAJOR, "radimo", &radimo_fops);
if (res) {
MSG(RADIMO_ERROR, "couldn't register block device/n");
return res;
}
init_timer(&radimo_timer);
/* 在系统中注册块的大小、存储容量等参数 */
hardsect_size[RADIMO_MAJOR] = &radimo_hard;
blksize_size[RADIMO_MAJOR] = &radimo_soft;
blk_size[RADIMO_MAJOR] = &radimo_size;
/* 在系统中注册响应读写请求的函数 */
blk_queue_make_request(BLK_DEFAULT_QUEUE(RADIMO_MAJOR), &radimo_request);
read_ahead[RADIMO_MAJOR] = radimo_readahead;
MSG(RADIMO_INFO, "loaded/n");
MSG(RADIMO_INFO, "sector size of %d, block size of %d, total size = %dKb/n",
radimo_hard, radimo_soft, radimo_size);
printk("init_module from radimo.c ");
return 0;
}
void cleanup_module(void)
{ //在模块被卸载的时候调用
unregister_blkdev(RADIMO_MAJOR, "radimo");
del_timer(&radimo_timer);
invalidate_buffers(MKDEV(RADIMO_MAJOR,0));
/* remove our request function */
blk_dev[RADIMO_MAJOR].request_queue.request_fn = 0;
vfree(radimo_storage);
MSG(RADIMO_INFO, "unloaded/n");
printk("cleanup_module from radimo.c ");
}
MODULE_LICENSE("GPL");
------------------------Makefile---------------------------------------
# standards
INCLUDE = /usr/src/linux-2.4/include
CC = gcc
CFLAGS = -D__KERNEL__ -I$(INCLUDE) -DMODULE -Wall -O2
TARGET = radimo
SRC = radimo.c
all: $(TARGET).o
clean:
rm -f *.o *~ core