前言
记录一些场面的命令或代码,不然老是要百度。
自旋锁
#include <linux/spinlock.h>
/* 静态初始化 */
static DEFINE_SPINLOCK(g_spin_lock);
/* 嵌入到结构体中 */
spinlock_t xxx_lock;
spin_lock_init(&xxx_lock);
// 没有反初始化函数
/* 只是关掉内核抢占,不会关中断 */
spin_lock(&xxx_lock);
spin_unlock(&xxx_lock);
/* 关内核抢占, 关本地中断 -> 所以任何情况下调用都是安全的,但是速度比 spin_lock慢一点点 */
spin_lock_irq(&xxx_lock);
spin_unlock_irq(&xxx_lock);
/* 上面的保存flags版本,建议用这个. */
spin_lock_irqsave(&xxx_lock, flags);
spin_unlock_irqrestore(&xxx_lock, flags);
互斥锁
#include <linux/mutex.h>
static DEFINE_MUTEX(g_mutex_lock);
struct mutex mutex;
mutex_init(&mutex);
mutex_destroy(&mutex);
mutex_lock(&mutex);
mutex_unlock(&mutex);
mutex_lock_interruptible(&mutex);
mutex_unlock(&mutex);
mutex_lock_killable(&mutex);
mutex_unlock(&mutex);
没啥好说的,顾名思义。
SYSRQ使用
用于在串口卡住的时候,进行调试。(比如 运行某个应用卡住了)
依赖于:
CONFIG_KGDB=y
CONFIG_MAGIC_SYSRQ=y
CONFIG_MAGIC_SYSRQ_DEFAULT_ENABLE=0x1
CONFIG_MAGIC_SYSRQ_SERIAL=y
# 打开
echo 1 > /proc/sys/kernel/sysrq
这里以 mobaxterm
为例:
ctrl+右键 -> special Command -> Break,然后输入 w
。打印出阻塞的task
也可以输入 h
查看支持的命令。
如:
root@TinaLinux:/# [ 1042.966141] sysrq: Show Blocked State
[ 1042.970259] task PC stack pid father
[ 1042.976134] usb-hardware-sc D 0 1054 2 0x00000000
[ 1042.982287] Backtrace:
[ 1042.985037] [<c07613d0>] (__schedule) from [<c07618bc>] (schedule+0xdc/0x110)
[ 1042.993039] r10:c3daa2dc r9:c7505ccc r8:c04efc20 r7:c0c02d00 r6:c3de0000 r5:c3de0000
[ 1043.001817] r4:c6d81600
[ 1043.004658] [<c07617e0>] (schedule) from [<c0764c5c>] (schedule_timeout+0xc0/0x100)
[ 1043.013245] r5:00000000 r4:0001225d
[ 1043.017252] [<c0764b9c>] (schedule_timeout) from [<c0764d34>] (schedule_timeout_uninterruptible+0x30/0x34)
[ 1043.028081] r7:c0d9def8 r6:c0d9def0 r5:c0d9def8 r4:c0c4b238
[ 1043.034428] [<c0764d04>] (schedule_timeout_uninterruptible) from [<c01812ac>] (msleep+0x28/0x30)
[ 1043.044282] [<c0181284>] (msleep) from [<c04efc54>] (usb_hardware_scan_thread+0x34/0x68)
[ 1043.053358] [<c04efc20>] (usb_hardware_scan_thread) from [<c013d1c8>] (kthread+0x12c/0x140)
[ 1043.062719] r7:c0d9def8 r6:c3daa540 r5:c3de0000 r4:c3daa2c0
[ 1043.069064] [<c013d09c>] (kthread) from [<c01010e8>] (ret_from_fork+0x14/0x2c)
[ 1043.077161] Exception stack(0xc3de1fb0 to 0xc3de1ff8)
[ 1043.082820] 1fa0: 00000000 00000000 00000000 00000000
[ 1043.091994] 1fc0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
[ 1043.101162] 1fe0: 00000000 00000000 00000000 00000000 00000013 00000000
[ 1043.108576] r10:00000000 r9:00000000 r8:00000000 r7:00000000 r6:00000000 r5:c013d09c
[ 1043.117357] r4:c3daa540 r3:00000000
[ 1394.817053] sysrq: HELP : loglevel(0-9) reboot(b) crash(c) terminate-all-tasks(e) memory-full-oom-kill(f) kill-all-tasks(i) thaw-filesystems(j) sak(k) show-backtrace-all-active-cpus(l) show-memory-usage(m) nice-all-RT-tasks(n) poweroff(o) show-registers(p) show-all-timers(q) unraw(r) sync(s) show-task-states(t) unmount(u) show-blocked-tasks(w) dump-ftrace-buffer(z)
内核线程
// 头文库
#include <linux/kthread.h>
// 返回值 struct task_struct *k
#define kthread_run(threadfn, data, namefmt, ...)
void kthread_bind(struct task_struct *k, unsigned int cpu);
void kthread_bind_mask(struct task_struct *k, const struct cpumask *mask);
// 注意: stop会阻塞等到线程退出才会返回, 所以线程需要 时不时要调用 kthread_should_stop 来判断是否需要退出
int kthread_stop(struct task_struct *k);
bool kthread_should_stop(void);
// 设置线程优先级
#include <linux/sched.h>
struct sched_param parm;
parm.sched_priority = MAX_RT_PRIO - 10;
sched_setscheduler(task, SCHED_FIFO, &parm);
等待队列
主要是用来等待一些条件的满足。
使用:
#include <linux/wait.h>
#include <linux/jiffies.h>
wait_queue_head_t wq;
init_waitqueue_head(&wq);
// 没有反初始化函数
wait_event_interruptible(wq, condition);
wait_event_interruptible_timeout(wq, condition, msecs_to_jiffies(ms))
wake_up_interruptible(&wq);
wake_up_interruptible_nr(&wq);
注意:
wait_event_*
这些是一个宏,wq
不需要&wq
, 宏内部会自动添加&
- 注意返回值:
> 0
:条件变成真,返回值是剩余时间(至少是1)= 0
:超时时,条件还是假< 0
:出错(被中断唤醒)
SKB使用
#include <linux/skbuff.h>
struct sk_buff_head queue; /* head */
struct sk_buff *skb; /* entry */
skb_queue_head_init(&queue);
/* ---------------- 添加skb, 假设数据源: src, len --------------- */
struct sk_buff *skb = alloc_skb(len, GFP_ATOMIC); /* 看情况, 也可以是 GFP_KERNEL */
/* skb_put 用于拓展skb的数据区,并返回拓展出的Buffer的首地址, 在后头添加数据 */
memcpy(skb_put(skb, len), src, len);
/* 在前头添加数据 */
memcpy(skb_push(skb, len), src, len);
/* sk_buff_head 自带一个spinlock,一般不需要自己额外添加lock */
skb_queue_tail(&queue, skb);
skb_queue_head(&queue, skb);
/* ---------------- 获取skb, 假设数据源: src, len --------------- */
while (1) {
if (skb_queue_empty(&ser->queue)) {
...
continue;
}
struct sk_buff *skb = skb_dequeue(&queue);
data = skb->data;
len = sbk->len;
// 处理数据
...
kfree_skb(skb);
}
IDR
用于ID分配,每个ID可以绑定一个void *
.
#include <linux/idr.h>
struct idr xxx;
idr_init(&xxx);
idr_destroy(&xxx);
/* 从 [start, end) 范围内分配一个id, */
idr_alloc(&xxx, priv, start, start + 1, GFP_KERNEL);
/* 通过id找到对应的priv指针 */
priv = idr_find(&xxx, id);
idr_remove(&xxx, id);
/* 后2个成员一个是void*, 一个是id */
idr_for_each_entry(&xxx, entry, id) {
...
}
IDA
如果仅需要分配ID,不需要绑定void *
#include <linux/idr.h>
static DEFINE_IDA(xxx);
struct ida xxx;
ida_init(&xxx);
ida_simple_get(&xxx, min, RPMSG_DEV_MAX, GFP_KERNEL);
ida_simple_remove(&xxx, id); /* 释放指定ID */
ida_destroy(&xxx); /* 释放所有ID */
sysfs attr
#include <linux/device.h>
# 静态属性初始化
static struct class *xxx_class;
static ssize_t xxx_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
...
}
static ssize_t name_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
...
}
DEVICE_ATTR_RW(xxx) /* 会定义结构体 struct device_attribute dev_attr_xxx */
static struct attribute *attrs[] = {
&dev_attr_xxx.attr,
...
};
/* 现在内核推荐使用 attr group 使用定义属性 */
static const struct attribute_group devgroup = {
.attrs = attrs
};
static const struct attribute_group *devgroups[] = {
&devgroup,
NULL
};
/* 静态定义class */
struct class xxx_class = {
.name = "xxxx",
.dev_groups = devgroups,
};
# 动态初始化
xxx_class = class_create(THIS_MODULE, "xxx");
class_destroy(xxx_class);
dev->class = xxx_class;
device_create_file(&dev, &dev_attr_xxx); /* 会在 dev->class下 创建对应目录 */
complete
完成量(在我看来就是是个二值信号量)
#include <linux/completion.h>
struct completion complete;
init_completion(&complete);
/* 如果调用 complete后, 还需要再使用complete,需要重新初始化 */
reinit_completion(¬ify->complete);
complete(&complete);
wait_for_completion_timeout(&complete, msecs_to_jiffies(500));
delay_work
延迟队列,定期执行某个函数。
注意不太准,CPU负载比较重的时候不一定会及时执行。
#include <linux/workqueue.h>
struct delayed_work work;
void work_func(struct work_struct *work)
{
/* delay_work是基于work封装的 */
struct delayed_work *p = to_delayed_work(work);
...
}
/* 初始化 */
INIT_DELAYED_WORK(&work, work_func);
/* 启动delay_work */
schedule_delayed_work(&work, DELAY_TIME);
/* 取消delay_work, free之前一定要调用这个,避免 use-after-free */
cancel_delayed_work(&work);
cancel_delayed_work_sync(&work);
打印栈回溯
调用dump_stack()
函数即可,不需要包含头文件。
目录大小
du -h --max-depth=1 .
coredump
内核配置:
CONFIG_ELF_CORE=y
CONFIG_ELFCORE=y
CONFIG_COREDUMP=y
CONFIG_BINFMT_ELF=y
CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS=y /* 可不选 */
配置coredump
文件路径:
ulimit -c unlimited /* 依赖于根文件系统配置 */
echo /tmp/%e.%t.%p.%s.core > /proc/sys/kernel/core_pattern
# 支持格式如下:
# %p: pis
# %P: 全局pid(初始化PID namespace)
# %i: tid
# %I: 全局tid(初始化PID namespace)
# %u: uid
# %g: gid
# %d: dump mode, 匹配PR_SET_DUMPABLE和/proc/sys/fs/suid_dumpable
# %s: 信号数
# %t: unix 时间
# %h: hostname
# %e: 可执行程序名(可能会缩短)
# %f: 可执行程序名
# %E: 可执行程序路径
# %c: core file的最大size, RLIMIT_CORE
应用内存泄露
编译应用时加上:
-fsanitize=leak
-fsanitize=address
-fno-omit-frame-pointer
链接时加上:-lasan
生成静态库
使用 ar
命令
一般使用:
xxx-ar rvs libname obj1 obj2 ... objn
若要生成的lib
仅仅只是中间文件,后续会合并成一个更大的lib
,可使用 thin-archive
生成一个轻量静态库(不保存实际的目标文件,仅仅保存执行目标文件的指针)来加上编译过程.
可在生成静态库时追加T
选项来生成轻量lib
最终可通过以下命令生成完整的lib
for lib in `find -name *.a`;
do
xxx-ar -t $lib | xargs ar rvs $lib.new && mv $lib.new $lib
done
note:这啥GNU的ar,与BSP冲突;且只记录前15个字符
头文件更新
遇到的问题:头文件更新,对应C文件不会重新编译
原因:没有依赖对应头文件
解决办法:可使用-MD
或 -MMD
来生产依赖文件
使用 -MD / -MMD
后,可以生成一个 xxx.d
文件,里面会列出所有依赖的文件,格式如下:
ads-path/to/file.o: \
depends file1 \
...
这种格式符合 Makefile
的规则格式,可以直接 include
SOURCES := $(wildcard *.cpp)
OBJECTS := $(patsubst %.cpp,%.o,$(SOURCES))
DEPENDS := $(patsubst %.cpp,%.d,$(SOURCES))
...
-include ($(DEPENDS))
...
-MD
和-MMD
区别:-MD
包含系统头文件如stdio.h
这种,-MMD
不包含
减小GIT仓库大小
# a - 将所有引用的东西打包到一个包中
# d - 打包之后,如果新创建的包使某些现有包冗余,移除冗余包
git repack -a -d --depth=250 --window=250
git prune-packed # 删除已经在包文件中的额外对象
或:
git gc --aggressive --prune
git prune
休眠
# 控制台不休眠
echo N > /sys/module/printk/parameters/console_suspend
# 打印调用时间
echo 1 > /sys/power/pm_print_times
#打印函数调用
echo Y > /sys/module/kernel/parameters/initcall_debug;
# 触发休眠
echo mem > /sys/power/state
# RTC唤醒
echo +10 > /sys/class/rtc/rtc0/wakealarm; # 10s后RTC唤醒
内存相关
SLAB调试
内核默认用的是 SLUB
如果存在SLUB
内存泄露,可以打开下面选项:
CONFIG_SLUB_DEBUG
CONFIG_SLUB_DEBUG_ON
CONFIG_SLUB_STATS
大概思路:
# 可先查看整体大小, 看看SLAB相关的内存是不是一直增加
cat /proc/meminfo
Slab: 32536 kB
SReclaimable: 9724 kB
SUnreclaim: 22812 kB
# 定期查看 是哪个 slab 一直增加, 确定是哪个slab泄露
cat /proc/slabinfo
# name <active_objs> <num_objs> <objsize> <objperslab> <pagesperslab> : tunables <limit> <batchcount> <sharedfactor> : slabdata <active_slabs> <num_slabs> <sharedavail>
...
kmalloc-8k 10 11 24576 1 8 : tunables 0 0 0 : slabdata 11 11 0
...
# 以 kmalloc-8k 为例
root@TinaLinux:/# cat /sys/kernel/slab/kmalloc-8k/alloc_calls
1 ubifs_mount+0x10a8/0x1408 age=89307 pid=1083 cpus=0
1 register_framebuffer+0x214/0x2d0 age=89742 pid=1 cpus=0
1 disp_init_lcd+0xb4/0x43c age=89744 pid=1 cpus=0
1 kzalloc+0x14/0x18 age=89807 pid=0 cpus=0
1 sunxi_dump_reg_probe+0x12c/0x17c age=89649 pid=1 cpus=0
2 kmalloc_array.constprop.39+0x2c/0x34 age=89756/89756/89756 pid=1 cpus=1
2 kzalloc.constprop.3+0x20/0x28 age=89514/89514/89514 pid=1 cpus=0
1 netlink_proto_init+0x40/0x148 age=89799 pid=1 cpus=0
root@TinaLinux:/# cat /sys/kernel/slab/kmalloc-8k/free_calls
10 <not-available> age=64244 pid=0 cpus=0
内存溢出
配置:
CONFIG_DEBUG_KMEMLEAK=y
CONFIG_DEBUG_KMEMLEAK_EARLY_LOG_SIZE=4000
使用方法:
1. cmd_line添加: kmemleak=off 或 kmemleak=on
2. mount -t debugfs nodev /sys/kernel/debug/
3. echo scan >/sys/kernel/debug/kmemleak
4. cat /sys/kernel/debug/kmemleak
5. Tracked的函数是:kmalloc,vmalloc、kmem_cache_alloc、per_cpu
KASAN
CONFIG_SLUB_DEBUG=y
CONFIG_SLUB_DEBUG_ON=y
CONFIG_SPARSEMEM_VMEMMAP=y
CONFIG_KASAN =y
CONFIG_KASAN_OUTLINE=y
打印相关
打印时间
# 作用: 打印出系统消逝的时间
CONFIG_PRINTK_TIME=y
PSTORE
# 与保留内存配合使用,记录内核最后crash相关信息
1. 内核配置
CONFIG_PSTORE=y
CONFIG_PSTORE_CONSOLE=y 所有内核信息
CONFIG_PSTORE_PMSG=y 所有用户态信息保留在/dev/pmsg0
CONFIG_PSTORE_RAM=y panic/oops信息
CONFIG_PSTORE_FTRACE=y ftrace信息
2. 预留内存
pstore_reserve_mem: pstore_reserve_mem_region@0 {
linux, reserve-contiguous-region;
linux, reserve-region;
linux, remove-completely;
reg =<0x0 startaddress 0x0 len>;
};
3. 查看上次异常的dmesg的消息
mount -t pstore -o kmsg_bytes=8000 - /sys/fs/pstore
锁相关
lock
# 使用场景:调试互斥锁发生异常调试
CONFIG_DEBUG_RT_MUTEXES=y
CONFIG_DEBUG_MUTEXES=y
# 使用场景:在SMP系统中,保护一段短小的临界区操作代码,保证这个临界区的操作是原子的,从而避免并发的竞争冒险
CONFIG_DEBUG_SPINLOCK=y
# 使用场景:进程长时间(系统默认配置 60 秒)处于TASK_RUNNING状态垄断CPU而不发生切换,一般情况下是进程关抢占或关中断后长时候执行任务、死循环,此时往往会导致多CPU间互锁,整个系统无法正常调度.
# 建议:开发阶段打开,量产关闭,严重影响性能
CONFIG_DEBUG_LOCK_ALLOC=y
CONFIG_DEBUG_LOCKDEP=y
死锁检测
使用场景:死锁的检测机制,抢占被长时间关闭而导致进程无法调度(soft lockup),和中断被长时间关闭而导致更严重的问题(hard lockup),表现现象可能是系统卡死。
# 内核配置
CONFIG_LOCKUP_DETECTOR=y
CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC=y
CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC_VALUE=1
# 使用方法
#设置,当发生softlockup时系统是否panic
/proc/sys/kernel/softlockup_panic
#当CPU发生IPI中断时,触发softlockup
/proc/sys/kernel/softlockup_all_cpu_backtrace
hunk相关
使用场景:进程长时间处于D状态,即TASK_UNINTERRUPTIBLE
状态,处于这种状态的进程不处理信号,所以kill不掉。
# 内核配置
CONFIG_DETECT_HUNG_TASK=y
CONFIG_DEFAULT_HUNG_TIMEOUT=120
CONFIG_BOOTPARAM_HUNG_TASK_PANIC=y
CONFIG_BOOTPARAM_HUNG_TASK_PANIC_VALUE=1
CONFIG_WQ_WATCHDOG=y
# 使用方法
# 设置发生hung task时系统是否panic
/proc/sys/kernel/hung_task_panic
# 设置默认timeout的阈值120s
/proc/sys/kernel/hung_task_timeout_secs
# watchdog异常等待时间
/proc/sys/kernel/watchdog_thresh
/proc/PID/task状态
主要是介绍下 /proc/PID/stat
这个文件。
具体可参考内核目录下的Documentation/filesystems/proc.txt
或直接指向 man proc 5
一般cat该文件会得到一堆数字:
root@(none):/# cat /proc/104/stat
104 (rproc-e907_rpro) S 2 0 0 0 -1 2130240 0 0 0 0 0 4461 0 0 -91 0 1 0 6 0 0 4294967295 0 0 0 0 0 0 0 2147483647 0 1 0 0 17 0 90 1 0 0 0 0 0 0 0 0 0 0 0
# 域
1:pid 2:(exec-file-name) 3:state 4:ppid 5:pgrp 6:session 7:tty_nr 8:tpgid 9 10 11 12 13 14:utime 15:stime 16:cutime 17:cstime 18 19 20 21 22:starttime 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43:guest_time 44 45 46 47 48 49 50 51 52
下面解释其各个域的含义:
- pid:顾名思义
- exec-file-name:顾名思义
- state:含义如下
- R - running
- S - 可中断休眠
- D - 不可中断休眠
- Z - 僵尸进程
- T - 被追踪或停止
- X - 死亡线程
- ppid:父进程ID
- pgrp:进程组ID
- session:会话ID
- tty_nr:进程控制终端设备号,对应主设备号bit[15:8],其余bit是从设备号
- tpgid:进程控制终端的前台进程组ID
- flags:对于每个bit的含义,可以参考
PF_*
的定义(include/linux/sched.h) - minflt
- cminflt
- majflt
- cmajflt
- utime:进程在用户模式下的运行时间,
进程总耗时= utime + stime
- stime:进程在内核模式下的运行时间,
进程总耗时= utime + stime
- cutime:子进程在用户模式下的运行时间
- cstime:子进程在内核模式下的运行时间,以时钟滴答为单位
- priority:优先级
- 对于实时进程,优先级显示为[-2:-100]对应实时优先级[1:99],越小优先级越高(-99是最高优先级)
- 对于非实时进程,优先级显示为[0:39],对应为线程的nice [-20:19],越小优先级越高
- nice:在[19:-20]范围内,用于动态调整非实时进程的优先级,19是最低,-20是最高
- num_threads:顾名思义
- itrealvalue:下一个
SIGALRM
的jiffies
时间,2.6之后,不再使用,固定为0 - starttime:进程启动的时间戳,相对于系统启动,以时钟滴答表示。要计算进程运行的时间量,请使用以下公式:
process-running-time(seconds) = system-uptime(seconds) - (starttime / USER_HZ)
USER_HZ
一般都是固定为100,定义在include/asm-generic/param.h
- vsize:虚拟内存大小
- rss:进程实际占用的page(text + data + stack),不包含按需加载或换出的页
- rsslim:rss的最大byte值,由
RLIMIT_RSS
定义 - startcode:代码段开始地址
- endcode:代码端结束地址
- startstack:栈的开始地址
- kstkesp:当前内核栈的栈指针
- kstkeip:当前
- signal
- blocked
- sigignore
- sigcatch
- wchan
- nswap
- cnswap
- exit_signal
- processor
- rt_priority
- policy:调度测量,定义在
include/uapi/linux/sched.h
- 0:SCHED_NORMAL
- 1:SCHED_FIFO
- 2:SCHED_RR
- 3:SCHED_BATCH
- 5:SCHED_IDLE
- 6:SCHED_DEADLINE
- delayacct_blkio_ticks
- guest_time
- cguest_time
- start_data
- end_data
- start_brk
- arg_start
- arg_end
- env_start
- env_end
- exit_code
获取进程的优先级和nice:
#/bin/bash
if [ $# -ne 1 ]; then
echo "Usage: parse_stat.sh PID"
exit
fi
stat=`cat /proc/$1/stat`
stat_info=(${stat// / })
#1. pid
echo "PID: "${stat_info[0]}
#2. exec-file-name
echo "exec: "${stat_info[1]}
#3. state
# R - running
# S - 可中断休眠
# D - 不可中断休眠
# Z - 僵尸进程
# T - 被追踪或停止
# X - 死亡线程
if [ ${stat_info[2]} == "R" ]; then
echo "state: Running"
elif [ ${stat_info[2]} == "S" ]; then
echo "state: Sleeping(interruptible)"
elif [ ${stat_info[2]} == "D" ]; then
echo "state: Sleeping(uninterruptible)"
elif [ ${stat_info[2]} == "Z" ]; then
echo "state: Zombie"
elif [ ${stat_info[2]} == "T" ]; then
echo "state: Stopped"
elif [ ${stat_info[2]} == "X" ]; then
echo "state: Dead"
else
echo "state: "${stat_info[2]}
fi
#4. ppid
echo "parent process ID: "${stat_info[3]}
#5. pgrp
echo "group process ID: "${stat_info[4]}
#6. session
echo "session ID: "${stat_info[5]}
#7. tty_nr
echo "tty_nr: "${stat_info[6]}
#8. tpgid
echo "terminal process ID: "${stat_info[7]}
#9. flags
printf "flags: 0x%x\n" ${stat_info[8]}
#10. minflt
echo "minflt: "${stat_info[9]}
#11. cminflt
echo "cminflt: "${stat_info[10]}
#12. majflt
echo "majflt: "${stat_info[11]}
#13. cmajflt
echo "cmajflt: "${stat_info[12]}
#14. utime
ms=`expr ${stat_info[13]} \* 10`
echo "user time: ${ms} ms"
#15. stime
ms=`expr ${stat_info[14]} \* 10`
echo "kernel time: ${ms} ms"
#16. cutime
ms=`expr ${stat_info[15]} \* 10`
echo "child user time: ${ms} ms"
#17. cstime
ms=`expr ${stat_info[16]} \* 10`
echo "child kernel time: ${ms} ms"
#18. priority
echo "priority: "${stat_info[17]}
#19. nice
echo "nice: "${stat_info[18]}
#20. num_threads
echo "num_threads: "${stat_info[19]}
#21. itrealvalue
echo "itrealvalue: "${stat_info[20]} "(废弃)"
#22. starttime
ms=`expr ${stat_info[21]} \* 10`
echo "starttime: ${ms} ms"
#23. vsize
echo "vsize: ${stat_info[22]} Bytes"
#24. rss (unit page)
size=`expr ${stat_info[23]} \* 4`
echo "rss: $size Kb"
#25. rsslim
echo "rsslim: ${stat_info[24]} Bytes"
#26. startcode
printf "startcode: 0x%x\n" ${stat_info[25]}
#27. endcode
printf "endcode: 0x%x\n" ${stat_info[26]}
#28. startstack
printf "startstack: 0x%x\n" ${stat_info[27]}
#29. kstkesp
printf "kstkesp: 0x%x\n" ${stat_info[28]}
#30. kstkeip
printf "kstkeip: 0x%x\n" ${stat_info[29]}
#31. signal
echo "signal(pending): "${stat_info[30]}
#32. blocked
echo "signal(blocked): "${stat_info[31]}
#33. sigignore
printf "signal(ignore): 0x%x\n" ${stat_info[32]}
#34. sigcatch
echo "signal(catch): "${stat_info[33]}
#35. wchan
echo "wchan: "${stat_info[34]}
#36. nswap
echo "swap pages: "${stat_info[35]}
#37. cnswap
echo "child swap pages: "${stat_info[36]}
#38. exit_signal
echo "exit_signal: "${stat_info[37]}
#39. processor
echo "processor: "${stat_info[38]}
#40. rt_priority
echo "rt_priority: "${stat_info[39]}
#41. policy
# 0:SCHED_NORMAL
# 1:SCHED_FIFO
# 2:SCHED_RR
# 3:SCHED_BATCH
# 5:SCHED_IDLE
# 6:SCHED_DEADLINE
if [ ${stat_info[40]} == "0" ]; then
echo "policy: SCHED_NORMAL"
elif [ ${stat_info[40]} == "1" ]; then
echo "policy: SCHED_FIFO"
elif [ ${stat_info[40]} == "2" ]; then
echo "policy: SCHED_RR"
elif [ ${stat_info[40]} == "3" ]; then
echo "policy: SCHED_BATCH"
elif [ ${stat_info[40]} == "5" ]; then
echo "policy: SCHED_IDLE"
elif [ ${stat_info[40]} == "6" ]; then
echo "policy: SCHED_DEADLINE"
else
echo "policy: "${stat_info[40]}
fi
#42. delayacct_blkio_ticks
ms=`expr ${stat_info[41]} \* 10`
echo "delayacct_blkio_ticks: $ms ms"
#43. guest_time
ms=`expr ${stat_info[42]} \* 10`
echo "guest_time: $ms ms"
#44. cguest_time
ms=`expr ${stat_info[43]} \* 10`
echo "child guest_time: $ms ms"
#45. start_data
printf "start_data: 0x%x\n" ${stat_info[44]}
#46. end_data
printf "end_data: 0x%x\n" ${stat_info[45]}
#47. start_brk
printf "start_brk: 0x%x\n" ${stat_info[46]}
#48. arg_start
printf "arg_start: 0x%x\n" ${stat_info[47]}
#49. arg_end
printf "arg_end: 0x%x\n" ${stat_info[48]}
#50. env_start
printf "env_start: 0x%x\n" ${stat_info[49]}
#51. env_end
printf "env_end: 0x%x\n" ${stat_info[50]}
#52. exit_code
echo "exit_code: "${stat_info[51]}
# cat /proc/[pid]/statm
statm=`cat /proc/$1/statm`
statm_info=(${statm// / })
# size (1) total program size
# (same as VmSize in /proc/[pid]/status)
ms=`expr ${statm_info[0]} \* 4`
echo "program size: $ms kB"
# resident (2) resident set size
ms=`expr ${statm_info[1]} \* 4`
echo "rss: $ms kB"
# share (3) shared pages (i.e., backed by a file)
ms=`expr ${statm_info[2]} \* 4`
echo "share: $ms kB"
# text (4) text (code)
ms=`expr ${statm_info[3]} \* 4`
echo "text: $ms kB"
# lib (5) library (unused in Linux 2.6)
# data (6) data + stack
ms=`expr ${statm_info[5]} \* 4`
echo "data: $ms kB"
# dt (7) dirty pages (unused in Linux 2.6)
查看GPIO
cat sys/kernel/debug/pinctrl/pio/pinmux-pins
Pinmux settings per pin
Format: pin (name): mux_owner gpio_owner hog?
pin 0 (PA0): (MUX UNCLAIMED) (GPIO UNCLAIMED)
...
pin 64 (PC0): spi0 (GPIO UNCLAIMED) function spi0 group PC0
pin 65 (PC1): spi0 (GPIO UNCLAIMED) function spi0 group PC1
...
pin 238 (PH14): (MUX UNCLAIMED) pio:238
# (MUX UNCLAIMED) (GPIO UNCLAIMED) 表示当前该IO无人申请
# spi0 (GPIO UNCLAIMED)表示该IO被某一个组申请, 如 PC0属于SPI0, 当前的复用功能是 spi0
# (MUX UNCLAIMED) pio:238 表示该IO被pio管理,但无人申请
Linux信号
查看所有信号:
kill -l
1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP
6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR1
11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM
16) SIGSTKFLT 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP
21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ
26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO 30) SIGPWR
31) SIGSYS 34) SIGRTMIN 35) SIGRTMIN+1 36) SIGRTMIN+2 37) SIGRTMIN+3
38) SIGRTMIN+4 39) SIGRTMIN+5 40) SIGRTMIN+6 41) SIGRTMIN+7 42) SIGRTMIN+8
43) SIGRTMIN+9 44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13
48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12
53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9 56) SIGRTMAX-8 57) SIGRTMAX-7
58) SIGRTMAX-6 59) SIGRTMAX-5 60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-2
63) SIGRTMAX-1 64) SIGRTMAX
其中:
- SIGKILL 和 SIGSTOP 信号不可被捕获或忽略。
- 前32个是非实时信号(1-31),即多次发送,进程只能收到一次
- 后面是实时信号,支持排队,信号不会丢失
- 信号会在程序从内核态返回用户态时候处理
常用信号:
- SIGHUP:挂起
- SIGINT:中断(ctrl+c)
- SIGQUIT:退出
- SIGILL:非法指令
- SIGTRAP:断电或陷阱指令
- SIGABRT:abort发出的信号
- SIGBUS:非法内存访问
- SIGFPE:浮点异常
- SIGKILL:kill信号
- SIGUSR1:用户信号1
- SIGSEGV:无效内存访问
- SIGUSR2:用户信号2
- SIGPIPE:管道破损,没有读端的管道写数据
- SIGALRM:闹钟信号
- SIGTERM:终止信号
- SIGSTKFLT:栈溢出
- SIGCHLD:子进程退出(默认忽略)
- SIGCONT:进程继续
- SIGSTOP:进程停止(不能被忽略、处理和阻塞)
- SIGTSTP:进程停止
- SIGTTIN:进程停止,后台进程从终端读数据时
- SIGTTOU:进程停止,后台进程想终端写数据时
- SIGURG:I/O有紧急数据到达当前进程(默认忽略)
- SIGXCPU:进程的CPU时间片到期
- SIGXFSZ:文件大小的超出上限
- SIGVTALRM:虚拟时钟超时
- SIGPROF:profile时钟超时
- SIGWINCH:窗口大小改变(默认忽略)
- SIGIO:IO信号
- SIGPWR:关机(默认忽略)
- SIGSYS:系统调用异常
C文件操作
// 头文件
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
/*
* filgs:
* O_RDONLY, O_WRONLY, O_RDWR 这3个互斥,只能任选其一
* 下面的flags可通过'|'同时使用
* O_CREAT: 不存在则创建
* O_NOCTTY: 打开的文件为终端机设备时, 则不会将该终端机当成进程控制终端机
* O_TRUNC: 若文件存在并以可写的方式打开时, 会令文件长度清为0, 而原来数据会消失.
* O_APPEND: 追加
* O_NONBLOCK: 非阻塞打开文件
* O_NDELAY: 功能同上
* O_SYNC: 同步打开
* O_NOFOLLOW: 如果打开文件为符号连接,打开失败
* O_DIRECTORY: 如果打开文件不是目录,打开失败
* mode:
* 权限说明: x:1 w:2 r:4 从左到右 owner,group,other
* S_IRWXU: 0700, 所有者可读写执行
* S_IRUSR/S_IREAD: 0400 所有者可读
* S_IWUSR/S_IWRITE: 0200 所有者可写
* S_IXUSR/S_IEXEC: 0100 所有者可执行
* S_IRWXG: 0070
* S_IRGRP: 0040
* S_IWGRP: 0020
* S_IXGRP: 0010
* S_IRWXO: 0007
* S_IROTH: 0004
* S_IWOTH: 0002
* S_IXOTH: 0001
* 返回值:
* 成功返回0, 否则负数错误码
*/
int open(const char * pathname, int flags);
int open(const char * pathname, int flags, mode_t mode);
/*
* 成功返回读取字节数 >= 0
* 失败返回-1, errno保存负数错误码
* EINTR: 此调用被信号所中断
* EAGAIN: 使用非阻塞I/O 时(O_NONBLOCK), 若无数据可读取则返回此值
* EBADF: fd 非有效的文件描述词, 或该文件已关闭
*/
ssize_t read(int fd, void * buf, size_t count);
/*
* 成功返回读取字节数 >= 0
* 失败返回-1, errno保存负数错误码
* EINTR 此调用被信号所中断.
* EAGAIN 当使用不可阻断I/O 时 (O_NONBLOCK), 若无数据可读取则返回此值.
* EADF 参数fd 非有效的文件描述词, 或该文件已关闭.
*/
ssize_t write (int fd, const void * buf, size_t count);
repo仓库回退
回退到某个特定时间点之前:
repo forall -c 'commitID=`git log --before "2017-03-17 07:00" -1 --pretty=format:"%H"`; git reset --hard $commitID'
C可变参数
宏:
#define xxx_debug(fmt, ...) \
(printf(__FILE__, __func__, __LINE__, fmt, ##__VA_ARGS__))
函数:
#include <stdarg.h>
int my_printf(const char *fmt, ...)
{
va_list args;
va_start(args, fmt); // 第二个参数必须是 ... 的前一个参数
while (*fmt) {
...
switch (ch) {
case 'd': num = va_arg(args, int); break;
case 's': num = va_arg(args, char *); break;
...
}
}
va_end(args);
return 0;
}
timer_stats
文件位置:/proc/timer_stats
依赖选项:CONFIG_TIMER_STATS
可选:CONFIG_DEBUG_OBJECTS_TIMERS
# enable
echo 1 > /proc/timer_stats
# disable
echo 0 > /proc/timer_stats
# 也可在stop前 cat, 无具体要求
cat /proc/timer_stats
Timer Stats Version: v0.3
Sample period: 4.778 s
Collection: inactive
次数 PID 进程名 调用地点(回调函数)
1, 0 swapper/0 tick_nohz_restart (tick_sched_timer)
21, 0 swapper/1 tick_nohz_restart (tick_sched_timer)
1194, 0 swapper/0 tick_nohz_restart (tick_sched_timer)
5, 1476 netifd schedule_hrtimeout_range_clock (hrtimer_wakeup)
471, 1492 swupdate-progre do_nanosleep (hrtimer_wakeup)
592, 7 rcu_preempt rcu_gp_kthread (process_timeout)
359, 0 swapper/1 __tick_nohz_idle_enter (tick_sched_timer)
36, 614 kworker/0:1 queue_delayed_work_on (delayed_work_timer_fn)
RCU警告
可参考文档:Documentation/RCU/stallwarn.txt
sys/module/rcupdate/parameters/rcu_cpu_stall_suppress
参数用于配置是否使能RCU的CPU停顿检测机制- 该机制默使能
- 默认超时时间是10s(宽限周期)
打印信息
INFO: rcu_sched_state detected stall on CPU 5 (t=2500 jiffies)
CPU5产生了停顿,随后会有CPU5打印出栈回溯
如果停顿的CPU没办法及时地打印数据,将有其他CPU帮忙打印,会出现如下的INFO:
INFO: rcu_bh_state detected stalls on CPUs/tasks: { 3 5 } (detected by 2, 2502 jiffies)
后面的信息如下:
INFO: rcu_preempt detected stall on CPU
0: (63959 ticks this GP) idle=241/3fffffffffffffff/0 softirq=82/543
(t=65000 jiffies)
(63959 ticks this GP) 表示超过了宽限周期多少个调度tick
idle= 部分 打印 dyntick-idle 状态。
1. 241: dyntick计数器的低12bit, 如果CPU处理dyntikc-idle模式它会是一个偶数,否则是奇数
2. 3fffffffffffffff:16进制嵌套值,如果在空闲循环会是一个很小的证书,否则是一个很大的正数
softirq= 部分
1. 82:最后一次记录宽限周期时,softirq执行的次数
2. 543: CPU启动到当前时间执行的softsirq次数
如果这个数字在多次RCU的消息中不变,可能说明softirq不能在该CPU上运行(正在持有自旋锁?)
或在 -rt 内核中 如果高优先级进程正在挨饿
引发RCU警告的几种原因:
- CPU 在 RCU 读端临界区中循环
- CPU 在 关中断 情况下循环,造成
RCU-sche
和RCU-bh
停顿 - CPU 在 关调度 情况下循环,造成
RCU-sche
和RCU-bh
停顿 - CPU 在 关闭下半部 情况下循环,造成
RCU-sche
和RCU-bh
停顿 - 任何阻塞RCU的宽限周期线程运行
- 实时任务恰好抢占 RCU 读取端临界区中间的低优先级任务。 如果不允许低优先级任务在任何其他 CPU 上运行,下一个 RCU 宽限期将永远无法完成,导致系统内存不足并挂起
vfork与fork区别
区别:
- vfork会挂起父进程,直到子进程执行完毕,fork父子进程会同时执行
- vfork与父进程共享地址空间
FD_CLOEXEC标志
使用场景:fork出的子进程在执行exec时,关闭父进程的文件句柄
缘由:
- fork出的子进程共享父进程的文件描述符
- 通常子进程会执行exec,将新的程序替换原来的进程正文,堆栈等
- 子进程在调用exec后,保存的文件描述符就会丢失,从而无法关闭无用的文件描述符,甚至在复杂的程序中,子进程不知道父进程打开了几个文件
- FD_CLOEXEC(close on exec)表示会使得文件在子进程调用exec时关闭
#include <unistd.h>
#include <fcntl.h>
int fd=open("foo.txt",O_RDONLY);
int flags = fcntl(fd, F_GETFD);
flags |= FD_CLOEXEC;
fcntl(fd, F_SETFD, flags);
Linux内存预留
cmdline加上 memblock=debug bootmem_debug=1
,启动的时候就会把预留的内存信息打印出来。
trace
数据格式说明:
irqs-off:‘d’:关中断,‘.’ 其他情况。
need-resched:
- ‘N’:TIF_NEED_RESCHED 和 PREEMPT_NEED_RESCHED 均被设置
- ‘n’:只有 TIF_NEED_RESCHED 被置位
- ‘p’:只有 PREEMPT_NEED_RESCHED 被置位
- ‘.’:其他情况
hardirq/softirq:
- ‘H’:硬件中断抢占了软件中断
- ‘h’:硬件中断运行中
- ‘s’:软件中断运行中
- ‘.’:其他情况
preempt-depth:数字,表示 preempt_disabled 的级别
配置:
Kernel hacking --->
[*] Tracers --->
[*] Kernel Function Tracer
[*] Kernel Function Graph Tracer
[*] enable/disable function tracing dynamically
[*] Kernel function profiler
function_trace
文件简短说明:
- current_tracer:用于设置或显示当前使用的跟踪器
- available_tracers:记录了当前编译进内核的跟踪器的列表
- trace:查看缓冲器数据
- set_graph_function:设置要清晰显示调用关系的函数(PS:注意一旦触发,所有子函数都会被trace)
- tracing_on:控制跟踪的暂停
- set_ftrace_filter / set_ftrace_notrace:缺省为可以跟踪所有内核函数
- set_event:跟踪的事件类型
- available_events:当前编译进内核的可以监控的事件
echo function_graph > current_tracer
#echo n_tty_receive_buf2 > set_graph_function
echo n_tty_receive_signal_char > set_graph_function
echo '*tty*' > set_ftrace_filter
echo '*sig*' >> set_ftrace_filter
echo '*pgrp*' >> set_ftrace_filter
echo '*spin*' > set_ftrace_notrace
echo '*preempt*' >> set_ftrace_notrace
echo '*rcu*' >> set_ftrace_notrace
echo > trace
echo 1 > tracing_on
ping 127.0.0.1
echo 0 > tracing_on
cat trace
trace_event
选中:
CONFIG_FTRACE
CONFIG_FUNCTION_TRACER
使用:
mount -t tracefs nodev /sys/kernel/tracing
cd /sys/kernel/tracing
# 查看所有支持的 events
cat available_events
# set_event不支持 hrtimer_*
echo hrtimer_expire_entry > set_event
echo hrtimer_expire_exit >> set_event
echo 'irq:*' > /sys/kernel/debug/tracing/set_event
# 早期启动可以使用 cmdline 来添加event
trace_event=event1,event2,...
# 使能事件
echo 1 > /sys/kernel/debug/tracing/events/sched/sched_wakeup/enable 使能特定事件
echo 1 > /sys/kernel/debug/tracing/events/sched/enable # 使能特定组的事件
echo 1 > /sys/kernel/debug/tracing/events/enable # 使能所有
event_filter
用于过滤事件。
- 一个filter expression由多个 predicates 组成
- 使用 ‘&&’ 或 ‘||’ 来组合
- 将日志事件中包含的字段的值与常量值进行比较,并根据字段值匹配(1)或不匹配(0)返回0或1
- 圆括号可以用来提供任意的逻辑分组,并且可以使用双引号防止shell将操作符解释为shell元字符
- 支持的数字类操作符:==, !=, <, <=, >, >=, &
- 支持的字符类操作符:==, != , ~
约等于操作符(~)接受通配符形式 (*,?)和字符类 ([),如:
prev_comm ~ "*sh"
prev_comm ~ "sh*"
prev_comm ~ "*sh*"
prev_comm ~ "ba*sh"
例子:
# 关闭过滤器
echo 0 > filter
# 使能过滤器
cd /sys/kernel/debug/tracing/events/sched/sched_wakeup
echo "common_preempt_count > 4" > filter
cd /sys/kernel/debug/tracing/events/signal/signal_generate
echo "((sig >= 10 && sig < 15) || sig == 17) && comm != bash" > filter
# 顶级文件夹下的set_event_pid 文件,可以给所有event配置PID过滤
cd /sys/kernel/debug/tracing
echo $$ > set_event_pid # 只追踪当前进程
echo 1 > events/enable
event_trigger
触发器,根据事件触发相应的事件。
# 语法, PS: 只支持 '>', 等价于 >>
echo 'command[:count] [if filter]' > trigger
echo '!command[:count] [if filter]' > trigger
支持的命令有:
enable_event/disable_event
命令格式:
enable_event:<system>:<event>[:count]
disable_event:<system>:<event>[:count]
# 当一个read系统调用进入时, kmalloc events被trace. :1 表明该行为只发生一次
# echo 'enable_event:kmem:kmalloc:1' > \
/sys/kernel/debug/tracing/events/syscalls/sys_enter_read/trigger
2. stacktrace: 触发时打印堆栈
echo 'stacktrace' > /sys/kernel/debug/tracing/events/kmem/kmalloc/trigger
# 在kmalloc请求bytes_req >= 65536的前5次打印出堆栈
echo 'stacktrace:5 if bytes_req >= 65536' > \
/sys/kernel/debug/tracing/events/kmem/kmalloc/trigger
stacktrace
stacktrace: 触发时打印堆栈
命令格式:
stacktrace[:count]
echo 'stacktrace' > /sys/kernel/debug/tracing/events/kmem/kmalloc/trigger
# 在kmalloc请求bytes_req >= 65536的前5次打印出堆栈
echo 'stacktrace:5 if bytes_req >= 65536' > \
/sys/kernel/debug/tracing/events/kmem/kmalloc/trigger
snapshot
# 以下命令每次创建一个snapshot当block request queue被unplugged并且depth > 1
# 如果你想trace一系列的events or functions,快照trace buffer将会抓住这些events,在 trigger event发生时:
echo 'snapshot if nr_rq > 1' > \
/sys/kernel/debug/tracing/events/block/block_unplug/trigger
traceon/traceoff
echo 'traceoff:1 if nr_rq > 1' > \
/sys/kernel/debug/tracing/events/block/block_unplug/trigger
kprobe_event
pthread
任务创建
int max = sched_get_priority_max(SCHED_FIFO);
int min = sched_get_priority_min(SCHED_FIFO);
pthread_attr_t attr;
struct sched_param sparam;
pthread_attr_init(&attr); /* 初始化成默认值 */
pthread_attr_getschedparam(&attr, &sparams); /* 获取对应的参数 */
sparams.sched_priority = (max + min) / 2;
pthread_attr_setschedparam(&attr, &sparams); /* 设置优先级 */
pthread_attr_setschedpolicy(&attr, SCHED_FIFO); /* 设置调度策略 */
pthread_attr_setstacksize(&attr, 16384); /* 设置栈大小 */
/* 成功返回0 */
pthread_create(&task_id, &attr, task, priv);
/* 等待线程退出并回收线程资源 */
pthread_join(task_id, NULL);
mutex
#include <pthread.h>
// 静态定义
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
// 参数2, 填0即可
int pthread_mutex_init(pthread_mutex_t *restrict mutex,
const pthread_mutexattr_t *restrict attr);
int pthread_mutex_destroy(pthread_mutex_t *mutex);
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_trylock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);
condition
需要和mutex
配置来达到效果:
// 静态定义
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
// 动态
pthread_cond_t cond;
pthread_mutex_t mutex;
pthread_cond_init(&cond, NULL);
pthread_mutex_init(&mutex, NULL);
/* 生产者(改变条件) */
pthread_mutex_lock(&mutex);
pthread_cond_broadcast(&cond); //如果条件符合,则通知消费者线程
pthread_cond_signal(&cond);
pthread_mutex_unlock(&mutex);
/* 消费者(等待条件改变) */
pthread_mutex_lock(&mutex);
/*
* 1. 条件不符合, unlock mutex -> 等待
* 2. 条件符合 , lock mutex -> 返回0
* 3. 因为惊群效应, 即使被唤醒了,也不一定条件满足.
*/
while (condition)
pthread_cond_wait(&cond, &mutex);
pthread_mutex_unlock(&mutex);
休眠唤醒
# 控制台不休眠
echo N > /sys/module/printk/parameters/console_suspend
# 打印调用时间
echo 1 > /sys/power/pm_print_times
# 打印函数调用
echo Y > /sys/module/kernel/parameters/initcall_debug;
# 到期自动唤醒
echo +5 > /sys/class/rtc/rtc0/wakealarm
# 配置同步休眠,
echo 0 > /sys/power/pm_async
# 休眠
echo mem > /sys/power/state
启动时间抓取
- cmdline添加 initcall_debug=1
- 把dmesg结果保存报服务器,如dmesg.txt
- 执行:cat dmesg.txt | perl scripts/bootgraph.pl > output.svg
内核读写文件
Linu
前提:需要选中 CONFIG_SET_FS
#include <linux/fs.h>
#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 10, 0)
{
mm_segment_t old_fs;
old_fs = get_fs();
set_fs(KERNEL_DS);
fp = filp_open(name, O_RDWR | O_CREAT | O_EXCL, 0755);
set_fs(old_fs);
}
#else
{
fp = filp_open(name, O_RDWR | O_CREAT | O_EXCL, 0755);
}
#endif
kernel_write(fp, buffer, size, &fp->f_pos);
kernel_read(fp, buffer, size, &fp->f_pos);
filp_close(fp, NULL);
中断绑核
# 1. 命令行
echo 1 > /proc/irq/10/smp_affinity # 绑到CPU0