1 POSIX pthread_create原理
1)fork()、pthread_create()、vfork()對應的系統調用分別是sys_fork()、sys_clone()、sys_vfork(),它們在內核中都是通過do_fork()實現的。
2)系統中所有的進程都組織在init_task.tasks鏈表下面,每個進程的線程組織在每個進程task_sturct->signal的鏈表下。參考函數for_each_process()和for_each_process_thread()。
3)根據上述分析,Linux用戶態和內核線程都由task_struct結構體描述,是一個輕量級的進程,因此內核中current變量可以表示進程、用戶態線程和內核線程。
2 kernel的log機制
2.1 kernel代碼中調整loglevel
int old_lvl = console_loglevel;
console_loglevel = CONSOLE_LOGLEVEL_MOTORMOUTH;
// TODO
console_loglevel = old_lvl;
2.2 Linux Kernel動態log
- 需在內核中配置CONFIG_DYNAMIC_DEBUG,并且將debugfs掛載到某個文件夾mount -t debugfs none /sys/kernel/debug/
- 通過echo -n "file xxxxxx.c +p" > /sys/kernel/debug/dynamic_debug/control(或者/d/dynamic_debug/control)來打開調試信息
2.3 Kernel hexdump
print_hex_dump(KERN_INFO, "",
DUMP_PREFIX_OFFSET, 16, 1,
buf,
len,
true);
3 Interrupt
3.1 中斷上半部
arch/x86/kernel/irq.c
do_IRQ()
分析do_IRQ()函數可知,Linux內核在x86平臺上不支持中斷嵌套。
3.2 中斷下半部
tasklet基于softirq。
3.3 irqbalance
https://github.com/ix5/android-irqbalance
4 x86 NMI watchdog
4.1 工作原理
soft lockup:禁止搶占,使用了spin_lock()
hard lockup:禁止搶占和禁止CPU local irq,使用了spin_lock_irqsave()
用戶態watchdog:/dev/watchdog
kernel/watchdog.c
hrtimer的處理函數:watchdog_timer_fn
調用wake_up_process函數喚醒此cpu上的watchdog線程,如果watchdog線程被喚醒就會去刷新時間戳。如果系統關了搶占,此watchdog線程不會被喚醒,這樣時間戳就不會更新。
4.2 soft lockup
Soft Lockup refers to the occurrence of the CPU that has occurred in 20 seconds (default) does not have scheduling switching.
Refer to Analysis of soft lockup mechanism of Linux series AND Using RCU’s CPU Stall Detector.
echo 30 > /proc/sys/kernel/watchdog_thresh
4.3 watchdog線程調度方式
- kernel/watchdog.c
- SCHED_FIFO,最高優先級MAX_RT_PRIO - 1 = 99
- 實時進程的優先級(rt_priority)數字越大則優先級越高,99最高,0最低;而普通進程正好相反,優先級(prio)數字越大則優先級越低,100最高,139最低(對應nice值-20 ~ 19)
- 內核調度是按照task_struct中的prio來調度的,所以內核會將實時線程的rt_priority轉換成prio:prio = MAX_RT_PRIO - 1 - p->rt_priority;prio的值越小,優先級就越高
4.4 進程的調度方式和優先級
ps -Al
chrt -p $PID
4.5 per-CPU變量
current,每個CPU一個變量:
arch/x86/include/asm/current.h
DECLARE_PER_CPU(struct task_struct *, current_task);
搶占計數,每個CPU一個變量:
arch/x86/include/asm/preempt.h
DECLARE_PER_CPU(int, __preempt_count);
CPUx的搶占禁止了后,實際上就是一個單片機,只能執行關閉搶占的線程以及響應硬件的中斷。
4.6 kmod demo
#include<linux/kernel.h>
#include<linux/module.h>
#include<linux/kthread.h>
static struct task_struct *task0;
static spinlock_t spinlock;
static int val;
int task(void *arg)
{
printk(KERN_INFO "%s:%d\n",
__func__, __LINE__);
/* To generate panic uncomment following */
/* panic("softlockup: hung tasks"); */
while (!kthread_should_stop()) {
printk(KERN_INFO "%s:%d\n",
__func__, __LINE__);
spin_lock(&spinlock);
/* busy loop in critical section */
while(1) {
// printk(KERN_INFO "%s:%d\n",
__func__, __LINE__);
}
spin_unlock(&spinlock);
}
return val;
}
static int softlockup_init(void)
{
printk(KERN_INFO "%s:%d\n",
__func__, __LINE__);
val = 1;
spin_lock_init(&spinlock);
task0 = kthread_run(&task, (void *)val,
"softlockup_thread");
set_cpus_allowed_ptr(task0, cpumask_of(0));
return 0;
}
static void softlockup_exit(void)
{
printk(KERN_INFO "%s:%d\n",
__func__, __LINE__);
kthread_stop(task0);
}
module_init(softlockup_init);
module_exit(softlockup_exit);
MODULE_LICENSE("GPL");
4.7 lockdep
lockdep:lock dependency
CONFIG_PROVE_LOCKING=y
打開lockdep時內核啟動后會自動配置console loglevel到CONSOLE_LOGLEVEL_MOTORMOUTH(=15),可以修改代碼將這個配置屏蔽,不然打印的內核log太多。
5 addr2line和objdump使用
5.1 帶有完整調試信息的符號路徑
./out/target/product/[PROJECT]/symbols/vendor/lib64/
5.2 addr2line
addr2line -C -f -e libxxx.so <地址>
其中地址為#00 Frame前面顯示的pc指向的地址
aarch64-linux-android-addr2line -C -f -e vmlinux <目標地址>
5.3 objdump
objdump -l -C -S libxxx.so > deasm.log
vi deasm.log
輸入#00 Frame前面顯示的pc指向的地址(需要去掉前面的多個0)
5.4 gdb vmlinux
Use 7-Zip to extract vmlinux from IPK file. The following l means list.
gdb vmlinux
l *func_name+offset_addr
6 coredump
Aarch64 Linux SDK中可以運行xxx-gdb來調試core dump。
Default core dump, must use sudo bash -c 'shell command' to run coredump configuration.
sudo bash -c 'echo "/data/core.%e.%p" > \
/proc/sys/kernel/core_pattern'
ulimit -c <size>: unit is block (= 512bytes)
ulimit -c unlimited
gdb main core.main.19188
7 Abbreviations
HECI:Host Embedded Controller Interface,Host OS和x86 ME的通信接口,只有4pin,分別是REQ#、GNT#、TX和RX,目的是為了代替SMBus