前言
本文主要是写几个内核模块来使用 tracepoint ,用的是 Linux 内核源码的的 sample 例程,由于目前内核已经不提倡手动创建tracepoint,因此将tracpoint-sample从内核sample代码中删除,于是我用的是Linux 2.6.32 内核源码下的例程。
因此目前推荐用: TRACE_EVENT使用的是 tracepoint 机制,kernel的绝大部分tracepoint都是trace event在使用。
TRACE_EVENT(name)
而不是:
DECLARE_TRACE(name)
DEFINE_TRACE(name)
一、代码例程
我在 centos 7下测试,由于centos 7内核版本是 3.10.0 ,而例程用的是 2.6.32版本的 sample ,相应的API有一点小变动,稍加修改,如下所示:
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/file.h>
#include <linux/dcache.h>
#include "samples-trace.h"
DEFINE_TRACE(subsys_event);
struct proc_dir_entry *pentry_sample;
static void probe_subsys_event_1(void *ignore, struct inode *inode, struct file *file)
{
path_get(&file->f_path);
dget(file->f_path.dentry);
printk(KERN_INFO "Event is encountered with filename: %s\n", file->f_path.dentry->d_name.name);
dput(file->f_path.dentry);
path_put(&file->f_path);
}
static void probe_subsys_event_2(void *ignore, struct inode *inode, struct file *file)
{
printk(KERN_INFO "Event is encountered with inode number: %lu\n", inode->i_ino);
}
static int sample_open(struct inode *inode, struct file *file)
{
printk(KERN_INFO "open /proc/tracepoint-sample file\n");
trace_subsys_event(inode, file);
return -EPERM;
}
static int sample_release(struct inode *inode, struct file *file)
{
printk(KERN_INFO "release /proc/tracepoint-sample file\n");
trace_subsys_event(inode, file);
return -EPERM;
}
static const struct file_operations mark_ops = {
.owner = THIS_MODULE,
.open = sample_open,
.release = sample_release,
};
static int __init sample_init(void)
{
int ret;
printk(KERN_ALERT "sample init\n");
pentry_sample = proc_create("tracepoint-sample", 0644, NULL, &mark_ops);
if (!pentry_sample)
return -EPERM;
ret = register_trace_subsys_event(probe_subsys_event_1, NULL);
WARN_ON(ret);
ret = register_trace_subsys_event(probe_subsys_event_2, NULL);
WARN_ON(ret);
return 0;
}
static void __exit sample_exit(void)
{
printk(KERN_ALERT "sample exit\n");
unregister_trace_subsys_event(probe_subsys_event_1, NULL);
unregister_trace_subsys_event(probe_subsys_event_2, NULL);
remove_proc_entry("tracepoint-sample", NULL);
}
module_init(sample_init)
module_exit(sample_exit)
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Tracepoint sample");
#ifndef _TP_SAMPLES_TRACE_H
#define _TP_SAMPLES_TRACE_H
#include <linux/proc_fs.h>
#include <linux/tracepoint.h>
DECLARE_TRACE(subsys_event,
TP_PROTO(struct inode *inode, struct file *file),
TP_ARGS(inode, file));
#endif
obj-m := sample.o
all:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
cat 命令会调用 open 函数,结果显示:
[root@localhost tracepoints]# insmod sample.ko
[root@localhost tracepoints]# cat /proc/tracepoint-sample
cat: /proc/tracepoint-sample: Operation not permitted
[root@localhost tracepoints]# dmesg -c
[138944.621698] sample init
[138961.491927] open /proc/tracepoint-sample file
[138961.491936] Event is encountered with filename: tracepoint-sample
[138961.491941] Event is encountered with inode number: 4026532284
[root@localhost tracepoints]# ls -li /proc/tracepoint-sample
4026532284 -rw-r--r--. 1 root root 0 Dec 23 21:27 /proc/tracepoint-sample
执行 open 函数的时候调用 跟踪点:subsys_event,依次执行注册的两个 probe 函数。
[root@localhost tracepoints]# strace -e trace=open cat /proc/tracepoint-sample
......
open("/proc/tracepoint-sample", O_RDONLY) = -1 EPERM (Operation not permitted)
......
二、文件操作
上述例程设计到一点文件系统相关知识.
2.1 proc_create
pentry_sample = proc_create("tracepoint-sample", 0644, NULL, &mark_ops);
proc_create内核函数用来在 procfs 文件系统创建一个文件,我们不能直接手动调用 touch 在 /proc下创建文件,通用 sysfs文件系统也是如此:
[root@localhost ~]# touch /proc/my_test
touch: cannot touch ‘/proc/my_test’: Permission denied
[root@localhost ~]# touch /sys/my_test
touch: cannot touch ‘/sys/my_test’: Permission denied
这两个文件系统都只能通过相应的内核函数才能创建文件。
对于proc_create,内核中很多,比如:
static int __init proc_cpuinfo_init(void)
{
proc_create("cpuinfo", 0, NULL, &proc_cpuinfo_operations);
return 0;
}
module_init(proc_cpuinfo_init);
static int __init proc_meminfo_init(void)
{
proc_create("meminfo", 0, NULL, &meminfo_proc_fops);
return 0;
}
module_init(proc_meminfo_init);
static int __init proc_modules_init(void)
{
proc_create("modules", 0, NULL, &proc_modules_operations);
return 0;
}
module_init(proc_modules_init);
proc_create函数原型:
static inline struct proc_dir_entry *proc_create(
const char *name, umode_t mode, struct proc_dir_entry *parent,
const struct file_operations *proc_fops)
{
return proc_create_data(name, mode, parent, proc_fops, NULL);
}
name:文件名 mode:文件权限 parent:文件父目录 proc_fops:文件对象操作
如果 parent 等于 NULL,那就默认为:/proc
struct proc_dir_entry proc_root = {
.low_ino = PROC_ROOT_INO,
.namelen = 5,
.mode = S_IFDIR | S_IRUGO | S_IXUGO,
.nlink = 2,
.count = ATOMIC_INIT(1),
.proc_iops = &proc_root_inode_operations,
.proc_fops = &proc_root_operations,
.parent = &proc_root,
.name = "/proc",
};
2.2 struct file_operations
struct file_operations为文件操作表:
struct file_operations {
struct module *owner;
loff_t (*llseek) (struct file *, loff_t, int);
ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
int (*readdir) (struct file *, void *, filldir_t);
unsigned int (*poll) (struct file *, struct poll_table_struct *);
long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
int (*mmap) (struct file *, struct vm_area_struct *);
int (*open) (struct inode *, struct file *);
int (*flush) (struct file *, fl_owner_t id);
int (*release) (struct inode *, struct file *);
int (*fsync) (struct file *, loff_t, loff_t, int datasync);
int (*aio_fsync) (struct kiocb *, int datasync);
int (*fasync) (int, struct file *, int);
int (*lock) (struct file *, int, struct file_lock *);
ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
int (*check_flags)(int);
int (*flock) (struct file *, int, struct file_lock *);
ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);
ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);
int (*setlease)(struct file *, long, struct file_lock **);
long (*fallocate)(struct file *file, int mode, loff_t offset,
loff_t len);
int (*show_fdinfo)(struct seq_file *m, struct file *f);
};
我们的内核模块用到的 open 函数指针:
int (*open) (struct inode *, struct file *);
将文件对象 struct file 与其相应的索引节点 struct inode 关联起来,由系统调用 open 调用。
参考资料
Linux 3.10.0
https://blog.csdn.net/jasonactions/article/details/123470620 https://blog.csdn.net/Rong_Toa/article/details/116602224 https://blog.csdn.net/Rong_Toa/article/details/108879240
|