基础知识
主设备号和次设备号
#include <linux/types.h>
dev_t /* 主设备号:12bit,次设备号:20bit */
MAJOR(dev_t dev);/* 获取主设备号 */
MINOR(dev_t dev);/* 获取次设备号 */
MKDEV(int major, int minor);/* 组合设备编号 */
#include <linux/fs.h>
int register_chrdev_region(dev_t first, unsigned int count, char *name);
int alloc_chrdev_region(dev_t *dev, unsigned int firstminor, unsigned int count, char *name);
void unregitster_chrdev_region(dev_t first, unsigned int count);
if (scull_major) {
dev = MKDEV(scull_major, scull_minor);
result = register_chrdev_region(dev, scull_nr_devs, "scull");
} else {
result = alloc_chrdev_region(&dev, scull_minor, scull_nr_devs, "scull");
scull_major = MAJOR(dev);
}
if (result < 0) {
printk(KERN_WARNING "scull: can't get major %d\n", scull_major);
return result;
}
重要数据结构
#include <linux/fs.h>
inode
dev_t i_rdev;
struct cdev *i_cdev;
- 内核用inode结构在内部表示文件;
- 和file结构不同,file结构表示打开的文件描述符;
- 对单个文件,可能会有许多个表示打开的文件描述符的file结构,但他们都指向单个inode结构
unsigned int iminor(struct inode *inode);
unsigned int imajor(struct inode *inode);
fops → struct file_operations
sturct module *owner; /* THIS_MODULE <linux/module.h> */
int (*open)(struct inode *, struct file *);
- 检查设备特定错误;
- 初始化首次打开的设备;
- 如有必要,更新`f_op`指针;
- 分配并填写置于`filp->private_data`里的数据结构;
#include <linux/kernel.h>
container_of(pointer, container_type, container_field);
int (*release)(struct inode *, struct file *);
ssize_t (*read)(struct file *, char __user *, size_t, loff_t *);
ssize_t (*write)(struct file *, char __user *, size_t, loff_t *);
#include <asm/uaccess.h>
#include <linux/uaccess.h>
unsigned long copy_to_user(void __user *to, const void *from, unsigned long count);
unsigned long copy_from_user(void *to, const void __user *from, unsigned long count);
filp → struct file
mode_t f_mode;
lofft_t f_pos;
unsigned int f_flags;
struct file_operations *f_op;
void *private_data;
struct dentry *f_dentry;
字符设备的注册及移除
方式一
#include <linux/cdev.h>
void cdev_init(struct cdev *cdev, struct file_operation *fops);
int cdev_add(struct cdev *dev, dev_t num, unsigned int count);
void cdev_del(struct cdev *dev);
方式二
int register_chrdev(unsigned int major, const char *name, struct file_operations *fops);
int unregister_chrdev(unsigned int major, const char *name);
字符设备驱动编写要点
- 获取设备编号;
/*
* Get a range of minor numbers to work with, asking for a dynamic
* major unless directed otherwise at load time.
*/
if (scull_major) {
dev = MKDEV(scull_major, scull_minor);
result = register_chrdev_region(dev, scull_nr_devs, "scull");
} else {
result = alloc_chrdev_region(&dev, scull_minor, scull_nr_devs,
"scull");
scull_major = MAJOR(dev);
}
if (result < 0) {
printk(KERN_WARNING "scull: can't get major %d\n", scull_major);
return result;
}
- 注册字符设备;
/*
* Set up the char_dev structure for this device.
*/
static void scull_setup_cdev(struct scull_dev *dev, int index)
{
int err, devno = MKDEV(scull_major, scull_minor + index);
cdev_init(&dev->cdev, &scull_fops);
dev->cdev.owner = THIS_MODULE;
dev->cdev.ops = &scull_fops;
err = cdev_add (&dev->cdev, devno, 1);
/* Fail gracefully if need be */
if (err)
printk(KERN_NOTICE "Error %d adding scull%d", err, index);
}
example
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/kernel.h> /* printk() */
#include <linux/fs.h> /* everything... */
#include <linux/errno.h> /* error codes */
#include <linux/types.h> /* size_t */
#include <linux/cdev.h>
#include <linux/uaccess.h> /* copy_*_user */
int example_major = 0;
int example_minor = 0;
static unsigned int example_nr_devs = 1;
typedef struct EXAMPLE_CDEV
{
struct cdev cdev;
}EXAMPLE_CDEV_STRU;
static EXAMPLE_CDEV_STRU example_cdev;
module_param(example_major, int, S_IRUGO);
module_param(example_minor, int, S_IRUGO);
MODULE_LICENSE("Dual BSD/GPL");
static int example_open(struct inode *inode, struct file *filp)
{
printk("example: open\n");
return 0;
}
static int example_close(struct inode *inode, struct file *filp)
{
printk("example: close\n");
return 0;
}
static ssize_t example_read(struct file *filp, char __user *buf, size_t size, loff_t *f_pos)
{
size_t count;
uint8_t byte;
char data[] = "0123456789\r\n";
printk("example: read (size=%zu)\n", size);
for(count = 0; (count < size) && (*f_pos) < strlen(data); ++(*f_pos), ++count) {
byte = data[*f_pos];
if(copy_to_user(buf + count, &byte, 1) != 0) {
break;
}
printk("example: read (buf[%zu]=%c)\n", count, (unsigned)byte);
}
return count;
}
static ssize_t example_write(struct file *filp, const char __user *buf, size_t size, loff_t *f_pos)
{
size_t count;
uint8_t byte;
printk("example: write (size=%zu)\n", size);
for(count = 0; count < size; ++count) {
if(copy_from_user(&byte, buf + count, 1) != 0) {
break;
}
printk("example: write (buf[%zu]=%c)\n", count, (unsigned)byte);
}
return count;
}
static struct file_operations example_fops = {
.open = example_open,
.release = example_close,
.read = example_read,
.write = example_write,
};
static void example_setup_cdev(EXAMPLE_CDEV_STRU *dev, int index)
{
int err, devno = MKDEV(example_major, example_minor + index);
cdev_init(&dev->cdev, &example_fops);
dev->cdev.owner = THIS_MODULE;
dev->cdev.ops = &example_fops;
err = cdev_add (&dev->cdev, devno, 1);
/* Fail gracefully if need be */
if (err)
printk(KERN_NOTICE "Error %d adding example-%d", err, index);
}
static int example_init(void)
{
dev_t dev;
int result;
printk("example: init\n");
if (example_major) {
dev = MKDEV(example_major, example_minor);
result = register_chrdev_region(dev, example_nr_devs, "example");
} else {
result = alloc_chrdev_region(&dev, example_minor, example_nr_devs,
"example");
example_major = MAJOR(dev);
}
if (result < 0) {
printk(KERN_WARNING "example: can't get major %d\n", example_major);
return result;
}
example_setup_cdev(&example_cdev, 0);
printk("example: %s driver(major %d) installed.\n", "example", example_major);
return 0;
}
static void example_exit(void)
{
dev_t dev = MKDEV(example_major, example_minor);
printk("example: exit\n");
/* Delete the character device driver from system. */
cdev_del(&example_cdev.cdev);
/* Unregister the allocated character device. */
unregister_chrdev_region(dev, example_nr_devs);
printk("example: %s driver removed.\n", "example");
}
module_init(example_init);
module_exit(example_exit);
#!/bin/sh
# $Id: example_load,v 0.1 2018/11/15 $
module="example"
echo "-------begin--------"
insmod ./$module.ko
dmesg | tail -2
major=$(awk "\$2==\"$module\" {print \$1}" /proc/devices)
#echo $major
sudo rm -f /dev/$module
sudo mknod /dev/$module c $major 0
echo -n "hello world" > /dev/$module
dmesg | tail -n 14
cat /dev/$module
dmesg | tail -n 17
echo "-------end--------"
其他
tail
tail [ -f ] [ -c Number | -n Number | -m Number | -b Number | -k Number ] [ File ]
参数解释:
-f 该参数用于监视File文件增长。
-c Number 从 Number 字节位置读取指定文件
-n Number 从 Number 行位置读取指定文件。
-m Number 从 Number 多字节字符位置读取指定文件,比方你的文件假设包括中文字,假设指定-c参数,可能导致截断,但使用-m则会避免该问题。
-b Number 从 Number 表示的512字节块位置读取指定文件。
-k Number 从 Number 表示的1KB块位置读取指定文件。
File 指定操作的目标文件名称
上述命令中,都涉及到number,假设不指定,默认显示10行。Number前面可使用正负号,表示该偏移从顶部还是从尾部開始计算。
tail可运行文件一般在/usr/bin/以下。
dmesg
3.png
echo
4.png
mknode
mknod [options] name {bc} major minor
mknod [options] name p
awk
awk 'Pattern {Action}' filename
-其中Pattern用来指定判断条件
-{}中包含的是awk的动作,也就是awk对记录的操作
major=$(awk "\$2==\"example\" {print \$1}" /proc/devices)
网友评论