kernel misc
kernel misc
. include/linux/string.h
在kernel里使用string类的函数,比如strstr()等,这些函数声明在include/linux/string.h,include <linux/string.h>即可
. /dev/tty, tty driver blog
http://www.mysixue.com/?p=127#devconsole_devtty0_devtty1
. log ratelimit
ratelimit_state_init()
例如如下ratelimit_state_init()是设置5s内打印10条msg:
#define DEFAULT_RATELIMIT_INTERVAL (5 * HZ)
#define DEFAULT_RATELIMIT_BURST 10
static inline void ratelimit_default_init(struct ratelimit_state *rs)
{
return ratelimit_state_init(rs, DEFAULT_RATELIMIT_INTERVAL,
DEFAULT_RATELIMIT_BURST);
}
. kernel makefile desc
https://blog.51cto.com/weiguozhihui/1591397
. set_task_comm(tsk, "kthreadd");
. android在init.rc里将panic_on_oops默认设置为了1
write /proc/sys/kernel/panic_on_oops 1
. __ffs/fls
获取二进制形式数值第一个非0bit,从低位往高位检查,index从0开始:
unsigned long bitmap = 0x90911010;
/* first bit */
printk("Bitmap: %#lx __ffs(): %ld\n", bitmap, __ffs(bitmap));
执行结果:
Bitmap: 0x90911010 __ffs(): 4
如果是ffs(),它和__ffs功能一样,结果只差1,比如上述__ffs结果为4,则ffs结果是5.
与ffs功能相对,fls是找到最后一个bit被设置为1的bit
fls(0)返回的结果是0
size_t/ssize_t type definition
4.19\include\linux\types.h #ifndef _SIZE_T #define _SIZE_T typedef __kernel_size_t size_t; #endif #ifndef _SSIZE_T #define _SSIZE_T typedef __kernel_ssize_t ssize_t; #endif
4.19\include\uapi\asm-generic\posix_types.h #ifndef __kernel_size_t #if __BITS_PER_LONG != 64 typedef unsigned int __kernel_size_t; typedef int __kernel_ssize_t; typedef int __kernel_ptrdiff_t; #else typedef __kernel_ulong_t __kernel_size_t; typedef __kernel_long_t __kernel_ssize_t; typedef __kernel_long_t __kernel_ptrdiff_t; #endif #endif
CONFIG_PRINTK_SAFE_LOG_BUF_SHIFT
CONFIG_PRINTK_SAFE_LOG_BUF_SHIFT: Temporary per-CPU printk log buffer size (12 => 4KB, 13 => 8KB)
The Linux kernel configuration item CONFIG_PRINTK_SAFE_LOG_BUF_SHIFT
:
- prompt: Temporary per-CPU printk log buffer size (12 => 4KB, 13 => 8KB)
- type: int
- depends on:
CONFIG_PRINTK
- defined in init/Kconfig
- found in Linux kernels: 4.11–4.20, 5.0–5.13, 5.13+HEAD
Help text
Select the size of an alternate printk per-CPU buffer where messages printed from usafe contexts are temporary stored. One example would be NMI messages, another one - printk recursion. The messages are copied to the main log buffer in a safe context to avoid a deadlock. The value defines the size as a power of 2.
Those messages are rare and limited. The largest one is when a backtrace is printed. It usually fits into 4KB. Select 8KB if you want to be on the safe side.
Examples: 17 => 128 KB for each CPU 16 => 64 KB for each CPU 15 => 32 KB for each CPU 14 => 16 KB for each CPU 13 => 8 KB for each CPU 12 => 4 KB for each CPU
from: https://cateee.net/lkddb/web-lkddb/PRINTK_SAFE_LOG_BUF_SHIFT.html
Linux kernel中的IS_ENABLED
首先在Kconfig中选中某个选项为y或m时, 在.config文件中就会由一个CONFIG_XXXXX=y或CONFIG_XXXXX=m, 并且会自动生成一个头文件autoconfig.h. 当选中为y时, 头文件中包含#define CONFIG_XXXXX 1, 当选中为m时, 头文件中包含#define CONFIG_XXXXX_MODULE 1, 当不选中是, 头文件中不包含相关语句.
from: https://blog.csdn.net/gngshn/article/details/69790798
在kernel里读一个文件(比如一个ko)
使用kernel_read_file_from_fd(),根据fd来read:
SYSCALL_DEFINE3(finit_module, int, fd, const char __user *, uargs, int, flags) { struct load_info info = { }; void *hdr = NULL; int err; err = may_init_module(); if (err) return err; pr_debug("finit_module: fd=%d, uargs=%p, flags=%i\n", fd, uargs, flags); if (flags & ~(MODULE_INIT_IGNORE_MODVERSIONS |MODULE_INIT_IGNORE_VERMAGIC)) return -EINVAL; err = kernel_read_file_from_fd(fd, 0, &hdr, INT_MAX, NULL, READING_MODULE); if (err < 0) return err; info.hdr = hdr; info.len = err; return load_module(&info, uargs, flags); }
kernel延时函数
mdelay()
这个属于忙等待,不会让出CPU
msleep()
这个调用了后会让出CPU,不会忙等待
usleep_range(min, max)
这个调用了后会让出CPU,不会忙等待
/proc/kallsyms
如果只开了CONFIG_KALLSYMS,CONFIG_KALLSYMS_ALL没开,则/proc/kallsyms里将只会有函数symbol,全局变量symbol将没有。
kernel里读memory,比如dump一个寄存器指向的memory前后的数据
如下是一个示例,一次读16byte:
unsigned char buf[16] = {0}; if(!probe_kernel_read(buf,(const void *)task_struct_addr,sizeof(buf)))
list_for_each_entry()相关的操作都需要放在这个循环体里,如果是遍历了一遍,它会返回head,此时如果指向回了head,此循环将会结束,此时如果在循环体外有对p的相关操作,会导致debug异常call到brk_handler:
list_for_each_entry(p, &mali_cmar_files_list, node) { if(p->pid == tsk->pid) { if(once) { once = 0; pr_info("unregister 0x%px files_struct ptr.\n", &tsk->files); unregister_wp_bp((unsigned long)&tsk->files); } list_del_init(&p->node); kfree(p); p = NULL; break; } }
打印调用者
printk("caller is %pS\n", __builtin_return_address(0));
打印结果:
[ 102.549872] caller is exit_files+0x150/0x310
module_param_named
#include <linux/moduleparam.h> #include <linux/module.h> bool inode_debug_ctrl = 0; module_param_named(inode_debug_ctrl, dcache_debug_ctrl, bool, S_IRUGO | S_IWUSR);
比如我在4.19/fs/inode.c里加上上述这段后,开机后在如下路径下就有了dcache_debug_ctrl文件了:
console:/sys/module # find -name dcache_debug_ctrl
./inode/parameters/dcache_debug_ctrl
module_param
module_param(ignore_loglevel, bool, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(ignore_loglevel, "ignore loglevel setting (prints all kernel messages to the console)");
根据pid获取对应的task_struct
系统中所有线程pid是唯一的,可以用如下API来获取对应线程的task_struct
find_task_by_pid_ns()
get caller in kernel
__builtin_return_address(0)
GENMASK_ULL(h,l)
这个macro的功能是产生一个bits mask,比如GENMASK_ULL(43,0),将产生一个bit43-bit0均为1其它bit63-bit44为0的bits mask,BITS_PER_LONG_LONG的值定义为64:
#define GENMASK_ULL(h, l) \ (((~0ULL) - (1ULL << (l)) + 1) & \ (~0ULL >> (BITS_PER_LONG_LONG - 1 - (h))))
在一个函数里加控制打印次数的log
如下示例,这部分控制打印这句log3秒内打印一次
static DEFINE_RATELIMIT_STATE(ratelimit_state, 3 * HZ, 1); if (__ratelimit(&ratelimit_state)) pr_info("in cpufreq_update_util: curr cpu is %d, cpu_of(rq) is %d.\n", raw_smp_processor_id(), cpu_of(rq));
%pS/%pS/%pF等指针格式打印符解析code位置
lib/vsprintf.c
int vsnprintf(char *buf, size_t size, const char *fmt, va_list args) case FORMAT_TYPE_PTR: str = pointer(fmt, str, end, va_arg(args, void *), spec);
static noinline_for_stack char *pointer(const char *fmt, char *buf, char *end, void *ptr, struct printf_spec spec) { switch (*fmt) { case 'F': case 'f': case 'S': case 's': ptr = dereference_symbol_descriptor(ptr); /* Fallthrough */ case 'B': return symbol_string(buf, end, ptr, spec, fmt);
KBUILD_EXTMOD
如果是源码内编译模块,KBUILD_EXTMOD变量为空。模块在编译过程中,如果依赖一个外部模块,需要使用KBUILD_EXTMOD来指定外部模块的符号文件Module.symvers,否则也会产生编译错误
from: https://zhuanlan.zhihu.com/p/409648724
添加一个新的kernel module Kconfig步骤
1.添加Kconfig文件,比如下面这样
config SYS_MISC tristate "SYS MISC" help sys misc.
2.如果是创建了新目录,在其父目录的Kconfig里添加source此Kconfig,这里的路径要从kernel根目录开始
3.在kernel config file里添加一个config,比如在arch/arm64/configs/里的一个config文件里添加CONFIG_SYS_MISC=m
printk()默认等级
printk()没有指定loglevel,则将使用默认loglevel,默认loglevel是KERN_WARNING(4)
打印一块buffer数据函数print_hex_dump()
void print_hex_dump(const char *level, const char *prefix_str, int prefix_type, int rowsize, int groupsize, const void *buf, size_t len, bool ascii)
level: kernel打印level
prefix_str: 自定义标识字串
prefix_type: 有如下几种,PREFIX_ADDRESS将打印地址
enum { DUMP_PREFIX_NONE, DUMP_PREFIX_ADDRESS, DUMP_PREFIX_OFFSET };
rowsize: 一行打印多少字节,比如一行打印16 byte
groupsize: 表示一组几个自己,比如8,表示打印一个数值会有8byte
buf: 需要dump的buffer地址
ascii: 表示是否要将打印的数据的ascii码打印出来,1将打印,0则不打印
xchg()
xchg(*ptr, x){ ret = *ptr; *ptr = x; return ret;}
它干了两件事,一是给一个指针赋值,二是获取了这个指针在赋值前的值。