Linux常用代码

前言

记录一些场面的命令或代码,不然老是要百度。

自旋锁

#include 

/* 静态初始化 */
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);
spin_unlock_irqrestore(&xxx_lock);

互斥锁

#include 

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

这里以 mobaxterm 为例:

右键 -> 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] [] (__schedule) from [] (schedule+0xdc/0x110)
[ 1042.993039]  r10:c3daa2dc r9:c7505ccc r8:c04efc20 r7:c0c02d00 r6:c3de0000 r5:c3de0000
[ 1043.001817]  r4:c6d81600
[ 1043.004658] [] (schedule) from [] (schedule_timeout+0xc0/0x100)
[ 1043.013245]  r5:00000000 r4:0001225d
[ 1043.017252] [] (schedule_timeout) from [] (schedule_timeout_uninterruptible+0x30/0x34)
[ 1043.028081]  r7:c0d9def8 r6:c0d9def0 r5:c0d9def8 r4:c0c4b238
[ 1043.034428] [] (schedule_timeout_uninterruptible) from [] (msleep+0x28/0x30)
[ 1043.044282] [] (msleep) from [] (usb_hardware_scan_thread+0x34/0x68)
[ 1043.053358] [] (usb_hardware_scan_thread) from [] (kthread+0x12c/0x140)
[ 1043.062719]  r7:c0d9def8 r6:c3daa540 r5:c3de0000 r4:c3daa2c0
[ 1043.069064] [] (kthread) from [] (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 
// 返回值 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 

struct sched_param parm;
parm.sched_priority = MAX_RT_PRIO - 10;
sched_setscheduler(task, SCHED_FIFO, &parm);

等待队列

主要是用来等待一些条件的满足。

使用:

#include 
#include 

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);

注意:

  1. wait_event_* 这些是一个宏,wq不需要 &wq, 宏内部会自动添加&
  2. 注意返回值:
    1. > 0:条件变成真,返回值是剩余时间(至少是1)
    2. = 0:超时时,条件还是假
    3. < 0:出错(被中断唤醒)

SKB使用

#include 

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 

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 

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 

# 静态属性初始化
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 

struct completion complete;

init_completion(&complete);
/* 如果调用 complete后, 还需要再使用complete,需要重新初始化 */
reinit_completion(&notify->complete);
complete(&complete);
wait_for_completion_timeout(&complete, msecs_to_jiffies(500));

delay_work

延迟队列,定期执行某个函数。

注意不太准,CPU负载比较重的时候不一定会及时执行。

#include 

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         : tunables    : slabdata   
...
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

打印相关

打印时间

# 作用: 打印出系统消逝的时间
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

锁相关

死锁检测

使用场景:死锁的检测机制,抢占被长时间关闭而导致进程无法调度(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

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

下面解释其各个域的含义:

  1. pid:顾名思义
  2. exec-file-name:顾名思义
  3. state:含义如下
    1. R - running
    2. S - 可中断休眠
    3. D - 不可中断休眠
    4. Z - 僵尸进程
    5. T - 被追踪或停止
    6. X - 死亡线程
  4. ppid:父进程ID
  5. pgrp:进程组ID
  6. session:会话ID
  7. tty_nr:进程控制终端设备号,对应主设备号bit[15:8],其余bit是从设备号
  8. tpgid:进程控制终端的前台进程组ID
  9. flags:对于每个bit的含义,可以参考PF_* 的定义(include/linux/sched.h)
  10. minflt
  11. cminflt
  12. majflt
  13. cmajflt
  14. utime:进程在用户模式下的运行时间,进程总耗时= utime + stime
  15. stime:进程在内核模式下的运行时间,进程总耗时= utime + stime
  16. cutime:子进程在用户模式下的运行时间
  17. cstime:子进程在内核模式下的运行时间,以时钟滴答为单位
  18. priority:优先级
    1. 对于实时进程,优先级显示为[-2:-100]对应实时优先级[1:99],越小优先级越高(-99是最高优先级)
    2. 对于非实时进程,优先级显示为[0:39],对应为线程的nice [-20:19],越小优先级越高
  19. nice:在[19:-20]范围内,用于动态调整非实时进程的优先级,19是最低,-20是最高
  20. num_threads:顾名思义
  21. itrealvalue:下一个SIGALRMjiffies时间,2.6之后,不再使用,固定为0
  22. starttime:进程启动的时间戳,相对于系统启动,以时钟滴答表示。要计算进程运行的时间量,请使用以下公式:process-running-time(seconds) = system-uptime(seconds) - (starttime / USER_HZ)
    1. USER_HZ一般都是固定为100,定义在include/asm-generic/param.h
  23. vsize:虚拟内存大小
  24. rss:进程实际占用的page(text + data + stack),不包含按需加载或换出的页
  25. rsslim:rss的最大byte值,由 RLIMIT_RSS 定义
  26. startcode:代码段开始地址
  27. endcode:代码端结束地址
  28. startstack:栈的开始地址
  29. kstkesp:当前内核栈的栈指针
  30. kstkeip:当前内核指令指针
  31. signal:pending的信号,十进制显示。过时的,因为其不提供实时的信号信息
  32. blocked:blocked的信号,十进制显示。过时的,因为其不提供实时的信号信息
  33. sigignore:忽略的信号,十进制显示。过时的,因为其不提供实时的信号信息
  34. sigcatch:捕获到的信号,十进制显示。过时的,因为其不提供实时的信号信息
  35. wchan:内核正在休眠的代码位置
  36. nswap:交换的页数(未维护)
  37. cnswap:子进程的累积 nswap(未维护)
  38. exit_signal:进程死后发送父进程的信号。
  39. processor:最后运行的CPU编号
  40. rt_priority:如果是实时现场,范围[1:99],如果是非实时,则=0
  41. policy:调度测量,定义在 include/uapi/linux/sched.h
    1. 0:SCHED_NORMAL
    2. 1:SCHED_FIFO
    3. 2:SCHED_RR
    4. 3:SCHED_BATCH
    5. 5:SCHED_IDLE
    6. 6:SCHED_DEADLINE
  42. delayacct_blkio_ticks:汇总 I/O 块延迟,以时钟滴答(1/100秒)为单位
  43. guest_time:进程的来宾时间,以时钟滴答(1/100秒)为单位
  44. cguest_time:子进程的来宾时间,以时钟滴答(1/100秒)为单位
  45. start_data:data段的开始地址
  46. end_data:data段的结束地址
  47. start_brk:brk的开始地址,上面的地址可以用来拓展程序的堆
  48. arg_start:程序命令行参数 (argv) 的开始地址
  49. arg_end:程序命令行参数 (argv) 的结束地址
  50. env_start:程序环境变量的开始地址
  51. env_end:程序环境变量的开始地址
  52. exit_code:waitpid(2) 报告的线程退出状态

简易脚本:

#/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

其中:

  1. SIGKILLSIGSTOP 信号不可被捕获或忽略。
  2. 前32个是非实时信号(1-31),即多次发送,进程只能收到一次
  3. 后面是实时信号,支持排队,信号不会丢失
  4. 信号会在程序从内核态返回用户态时候处理

常用信号:

  1. SIGHUP:挂起
  2. SIGINT:中断(ctrl+c)
  3. SIGQUIT:退出
  4. SIGILL:非法指令
  5. SIGTRAP:断电或陷阱指令
  6. SIGABRT:abort发出的信号
  7. SIGBUS:非法内存访问
  8. SIGFPE:浮点异常
  9. SIGKILL:kill信号
  10. SIGUSR1:用户信号1
  11. SIGSEGV:无效内存访问
  12. SIGUSR2:用户信号2
  13. SIGPIPE:管道破损,没有读端的管道写数据
  14. SIGALRM:闹钟信号
  15. SIGTERM:终止信号
  16. SIGSTKFLT:栈溢出
  17. SIGCHLD:子进程退出(默认忽略)
  18. SIGCONT:进程继续
  19. SIGSTOP:进程停止(不能被忽略、处理和阻塞)
  20. SIGTSTP:进程停止
  21. SIGTTIN:进程停止,后台进程从终端读数据时
  22. SIGTTOU:进程停止,后台进程想终端写数据时
  23. SIGURG:I/O有紧急数据到达当前进程(默认忽略)
  24. SIGXCPU:进程的CPU时间片到期
  25. SIGXFSZ:文件大小的超出上限
  26. SIGVTALRM:虚拟时钟超时
  27. SIGPROF:profile时钟超时
  28. SIGWINCH:窗口大小改变(默认忽略)
  29. SIGIO:IO信号
  30. SIGPWR:关机(默认忽略)
  31. SIGSYS:系统调用异常

C文件操作

// 头文件
#include 
#include 
#include 
#include 

/*
 * 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 

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

# 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

  1. sys/module/rcupdate/parameters/rcu_cpu_stall_suppress参数用于配置是否使能RCU的CPU停顿检测机制
  2. 该机制默使能
  3. 默认超时时间是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警告的几种原因:

  1. CPU 在 RCU 读端临界区中循环
  2. CPU 在 关中断 情况下循环,造成 RCU-sche RCU-bh停顿
  3. CPU 在 关调度 情况下循环,造成 RCU-sche RCU-bh停顿
  4. CPU 在 关闭下半部 情况下循环,造成 RCU-sche RCU-bh停顿
  5. 任何阻塞RCU的宽限周期线程运行
  6. 实时任务恰好抢占 RCU 读取端临界区中间的低优先级任务。 如果不允许低优先级任务在任何其他 CPU 上运行,下一个 RCU 宽限期将永远无法完成,导致系统内存不足并挂起

vfork与fork区别

区别:

  1. vfork会挂起父进程,直到子进程执行完毕,fork父子进程会同时执行
  2. vfork与父进程共享地址空间

FD_CLOEXEC标志

使用场景:fork出的子进程在执行exec时,关闭父进程的文件句柄

缘由:

  1. fork出的子进程共享父进程的文件描述符
  2. 通常子进程会执行exec,将新的程序替换原来的进程正文,堆栈等
  3. 子进程在调用exec后,保存的文件描述符就会丢失,从而无法关闭无用的文件描述符,甚至在复杂的程序中,子进程不知道父进程打开了几个文件
  4. FD_CLOEXEC(close on exec)表示会使得文件在子进程调用exec时关闭
#include 
#include 

int fd=open("foo.txt",O_RDONLY);  
int flags = fcntl(fd, F_GETFD);  
flags |= FD_CLOEXEC;
fcntl(fd, F_SETFD, flags);

你可能感兴趣的:(Linux内核,linux)