读Linux设备驱动第三版一书 练习笔记
思路:注册一个设备,当用户调用读取操作时返回给用户当前的进程列表
hello.c //源文件
#include //指定初始化和清理函数Module init和Module exit宏
#include //moudle.h 包含了大量加载模块需要的函数和符号的定义
#include //内核代码的 printk,输出优先级等内核代码
#include //kmalloc,kfree需要这个文件引入
#include //进程数据结构和操作
#include //错误代码
#include //文件操作;file_operation 结构,struct file,inode
#include //字符设备结构,注册与销毁设备操作
#include //dev_t 内核里代表设备号的类型.int MAJOR(dev_t dev)等
#include //copy_to_user和copy_from_user
MODULE_AUTHOR("UserName");
MODULE_LICENSE("GPL");
static int hello_major = 0; //设备文件主编号
static int hello_minor = 0; // 设备文件次编号
static char *dev_name = "hello"; //显示的设备名称前缀
static int dev_count = 4; //设备数量
static struct cdev **pDev = NULL; //设备结构 集合
int hello_proc_open(struct inode *inode, struct file *filp);
ssize_t hello_proc_read(struct file *filp, char __user *buf, size_t count,loff_t *f_pos);
ssize_t hello_proc_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos);
static struct file_operations hello_proc_ops = {
.owner = THIS_MODULE, //所属模块
.open = hello_proc_open, //systemcall open
.read = hello_proc_read, //systemcall read
.write = hello_proc_write //systemcall write
}; //这里只进行打开,读取和写入操作示例
static int __init hello_init(void)
{
dev_t dev = 0;
int result = 0;
// char* buf = NULL;
int index;
printk(KERN_ALERT "hello,kernel world\n");
/*buf = kmalloc(BUFSIZE,0);
if(buf != NULL)
{
memset(buf,0,BUFSIZE);
memcpy(buf,"kmalloc memory successfully\n",strlen("kmalloc memory successfully\n"));
printk(KERN_ALERT "%s\n", buf);
kfree(buf);
printk(KERN_ALERT "kfree memory successfully\n");
} */
//获取字符设备主编号
if (hello_major) {
dev = MKDEV(hello_major, hello_minor);
register_chrdev_region(dev, dev_count, dev_name);
}
else {
result = alloc_chrdev_region(&dev, hello_minor, dev_count,dev_name);
hello_major = MAJOR(dev);
}
if (result < 0) {
printk(KERN_WARNING "%s: can't get major %d\n", dev_name,hello_major);
return result;
}
//为设备申结构请内存空间
pDev = kmalloc(dev_count * sizeof(struct cdev*), GFP_KERNEL);
//注册每个设备
for (index = 0; index< dev_count; index++)
{
int err;
int devno = MKDEV(hello_major, hello_minor + index); //取得设备编号
//cdev_init(pDev[index], &hello_proc_ops);
pDev[index] = cdev_alloc(); //建立设备
pDev[index]->owner = THIS_MODULE;
pDev[index]->ops = &hello_proc_ops;
err = cdev_add (pDev[index], devno, 1); //注册设备
if (err)
printk(KERN_NOTICE "Error %d adding hello%d\n", err, index);
else
printk(KERN_NOTICE "Success %d adding hello%d\n", err, index);
}
return 0;
}
static void __exit hello_exit(void)
{
int i;
dev_t devno = MKDEV(hello_major, hello_minor);
/* Get rid of our char dev entries */
if (pDev) {
for (i = 0; i < dev_count; i++) {
cdev_del(pDev[i]); //删除设备
}
kfree(pDev); //释放设备结构所占内存
}
unregister_chrdev_region(devno, dev_count); //释放设备编号
printk(KERN_ALERT "GoodBy Kernel World\n");
}
int hello_proc_open(struct inode *inode, struct file *filp)
{
return 0;
}
ssize_t hello_proc_read(struct file *filp, char __user *buf, size_t count,loff_t *f_pos)
{
int len = 0;
unsigned long offset = 0;
int remain = 0;
struct task_struct *task; //进程或称任务结构
for_each_process(task){ //遍历当前的所有进程
printk("[%s]%d\n",task->comm,task->pid);
len = strlen(task->comm);
remain = len;
while (remain != 0)
{
remain = copy_to_user(buf + offset + len - remain ,task->comm + len - remain,remain); //将进程名拷贝到用户空间
}
offset += len;
remain = 1;
while (remain != 0)
{
remain = copy_to_user(buf + offset + 1-remain,"\n", remain);
}
offset += 1;
}
remain = 1;
while (remain != 0)
{
remain = copy_to_user(buf + offset + 1-remain,"\0",remain);
}
printk("write to user count = [%lu]\n",offset + 1);
return offset + 1;
}
ssize_t hello_proc_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos)
{
return 0;
}
module_init(hello_init);
module_exit(hello_exit);
Makefile
# Comment/uncomment the following line to disable/enable debugging
#DEBUG = y
# Add your debugging flag (or not) to CFLAGS
ifeq ($(DEBUG),y)
DEBFLAGS = -O -g -Dhello_DEBUG # "-O" is needed to expand inlines
else
DEBFLAGS = -O2
endif
CFLAGS += $(DEBFLAGS)
CFLAGS += -I$(LDDINC)
ifneq ($(KERNELRELEASE),)
# call from kernel build system
#hello-objs := hello.o
obj-m := hello.o
else
KERNELDIR ?= /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
modules:
$(MAKE) -C $(KERNELDIR) M=$(PWD) LDDINC=$(PWD)/../include modules
endif
clean:
rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions
depend .depend dep:
$(CC) $(CFLAGS) -M *.c > .depend
ifeq (.depend,$(wildcard .depend))
include .depend
endif
加载hello设备脚本 hello_load
# invoke insmod with all arguments we got
# and use a pathname, as insmod doesn't look in . by default
/sbin/insmod ./$module.ko $* || exit 1
# retrieve major number
major=$(awk "\$2==\"$module\" {print \$1}" /proc/devices)
# Remove stale nodes and replace them, then give gid and perms
# Usually the script is shorter, it's hello that has several devices in it.
rm -f /dev/${device}[0-3]
mknod /dev/${device}0 c $major 0
mknod /dev/${device}1 c $major 1
mknod /dev/${device}2 c $major 2
mknod /dev/${device}3 c $major 3
ln -sf ${device}0 /dev/${device}
chgrp $group /dev/${device}[0-3]
chmod $mode /dev/${device}[0-3]
卸载hello设备脚本 hello_unload
#!/bin/sh
module="hello"
device="hello"
# invoke rmmod with all arguments we got
/sbin/rmmod $module $* || exit 1
# Remove stale nodes
rm -f /dev/${device} /dev/${device}[0-3]
测试代码test.c:
#include
#include
#include
#include
#include
int main()
{
int fd = 0;
char buf[65535];
if((fd = open("/dev/hello1", O_CREAT|O_RDWR, 0644)) > 0)
{
printf("open success.\n");
read(fd,buf,65535);
printf("%s\n",buf);
close(fd);
}
else
{
printf("open failed.\n");
}
return 0;
}
测试步骤:
编译后运行脚本hello_load,然后运行test,最后运行脚本 hello_unload,千万不要测试诸如cp /dev/hello1 a.txt, 因为该例子不完全,会导致无限循环。