进程内核栈

进程内核栈


为什么有进程内核栈

  进程在创建的时候也可以理解为一个程序,或者在简单的理解也可以把进程理解为一个函数,只不过这个函数很大而已,这个进程也需要有一些函数调用,也需要有一些函数去标记一些信息,于是 便有了进程内核栈这个东西。

  简单理解,进程内核栈实际上就是为进程开辟一个栈帧空间。
  但是这个栈帧空间不是用户的栈帧空间,因为用户的栈帧空间时不安全的,所以内核会专门为它开辟一个空间,这个就是内核栈。

进程内核栈和进程描述符的关系

  • 进程描述符

  进程描述符就是进程结构体(struct task_struct),每一个进程在创建之后都会有一个进程结构体,用来记录进程的所有信息。这其中的有一个信息就是就是进程内核栈,用一个指针指示。void *stack;就是指向下面的内核栈结构体的“栈底”。

  • 内核栈结构体

    union thread_union {
        struct thread_info thread_info;
        unsigned long stack[THREAD_SIZE/sizeof(long)];
    };

其中的stack成员就是内核栈。

而其中的struct thread_info是记录部分进程信息的结构体,其中包括了进程上下文信息。

从这里可以看出内核栈空间和 thread_info是共用一块空间的。如果内核栈溢出, thread_info就会被摧毁,系统崩溃了~~~

让我们详细的看一下内核栈结构体。

    /*
     * low level task data that entry.S needs immediate access to.
     * __switch_to() assumes cpu_context follows immediately after cpu_domain.
     */
    struct thread_info {
        unsigned long        flags;        /* low level flags */
        int            preempt_count;    /* 0 => preemptable, <0 => bug */
        mm_segment_t        addr_limit;    /* address limit */
        struct task_struct    *task;        /* main task structure */
        struct exec_domain    *exec_domain;    /* execution domain */
        __u32            cpu;        /* cpu */
        __u32            cpu_domain;    /* cpu domain */
        struct cpu_context_save    cpu_context;    /* cpu context */
        __u32            syscall;    /* syscall number */
        __u8            used_cp[16];    /* thread used copro */
        unsigned long        tp_value;
        struct crunch_state    crunchstate;
        union fp_state        fpstate __attribute__((aligned(8)));
        union vfp_state        vfpstate;
    #ifdef CONFIG_ARM_THUMBEE
        unsigned long        thumbee_state;    /* ThumbEE Handler Base register */
    #endif
        struct restart_block    restart_block;
    };

关键是其中的task成员,指向的是所创建的进程的struct task_struct结构体。就是进程描述符。

图文表示三者关系

内核栈—内核栈结构体(struct thread_info)—-进程描述符(struct task_struct)三者的关系入下图

这里写图片描述

内核栈的产生

在进程被创建的时候,fork族的系统调用中会分别为内核栈和struct task_struct分配空间,调用过程是:
fork族的系统调用—>do_fork—>copy_process—>dup_task_struct

<think>我们面对的问题是如何在Linux系统中查看一个已经hang住(无响应)的进程内核调用。通过分析内核调用,我们可以了解进程在hang住时正在执行什么操作,比如是否在等待锁、是否在系统调用中阻塞等,从而进一步诊断问题原因。 根据引用[2]和引用[4]中的案例,当进程hang住时,我们通常可以通过查看进程内核来获取信息。但是,需要注意的是,在进程hang住的情况下,我们可能无法通过常规的调试工具(如gdb)直接附加到进程,因为进程可能已经无法响应。因此,我们主要依赖内核提供的机制来获取信息。 ### 查看hang住进程内核调用的方法 #### 方法1:通过`/proc`文件系统获取 这是最直接的方法。每个进程在`/proc`文件系统下都有一个以其进程ID(PID)命名的目录,其中`stack`文件记录了该进程当前的内核调用。 步骤: 1. 首先确定hang住进程的PID。可以使用`ps`、`top`等命令查找,例如: ```bash ps aux | grep <进程名> ``` 2. 查看该进程内核: ```bash cat /proc/<PID>/stack ``` 例如,如果PID是509,则命令为: ```bash cat /proc/509/stack ``` **注意**:这个方法要求进程仍然存在(即没有退出),并且内核支持`/proc/<PID>/stack`(通常都支持)。如果进程已经处于不可中断睡眠(D状态)或者由于其他原因无法被调度,这个方法仍然可以获取到内核,因为内核信息是由内核直接提供的。 #### 方法2:使用`sysrq`触发内核转储 如果整个系统都hang住了(包括无法执行命令),我们可以使用`sysrq`组合键来触发内核转储,其中会包含所有进程内核信息。 步骤: 1. 启用`sysrq`(如果尚未启用): ```bash echo 1 > /proc/sys/kernel/sysrq ``` 2. 触发显示所有活动CPU上的进程(通过魔术组合键): - 在物理机上:按下`Alt+SysRq+l`(注意:某些系统可能需要同时按下`Ctrl`)。 - 在终端模拟器中(如ssh连接):通常无法直接发送`SysRq`,但可以通过`/proc/sysrq-trigger`文件触发: ```bash echo l > /proc/sysrq-trigger ``` 这会导致内核将当前所有进程内核打印到内核日志中。 3. 查看内核日志(通常通过`dmesg`): ```bash dmesg -T | tail -n 50 # 查看最近的日志,可能需要多翻一些 ``` #### 方法3:使用`crash`工具分析vmcore 如果系统完全hang住以至于无法响应任何命令,我们可能需要配置kdump,在系统崩溃时生成vmcore。然后,我们可以使用`crash`工具离线分析这个vmcore,从而查看进程内核。 步骤: 1. 配置kdump(需提前配置,不能在hang住后临时配置)[^1]。 2. 当系统hang住时,通过硬件看门狗或手动触发崩溃(如`echo c > /proc/sysrq-trigger`)让系统生成vmcore。 3. 使用crash工具加载vmcore和相应的内核调试符号文件(vmlinux): ```bash crash /path/to/vmlinux /path/to/vmcore ``` 4. 在crash交互环境中,使用`ps`命令查看进程,然后使用`bt`命令查看指定进程内核: ```bash crash> ps | grep <进程名> # 找到进程的PID crash> bt <PID> # 显示该进程内核 ``` #### 方法4:使用`ftrace`或`perf` 如果进程hang住是间歇性的,我们可以使用`ftrace`或`perf`进行动态跟踪,记录进程内核函数调用情况。 例如,使用`ftrace`跟踪特定进程: ```bash # 设置跟踪 echo function > /sys/kernel/debug/tracing/current_tracer echo <PID> > /sys/kernel/debug/tracing/set_ftrace_pid echo 1 > /sys/kernel/debug/tracing/tracing_on # 等待一段时间(直到问题复现),然后停止 echo 0 > /sys/kernel/debug/tracing/tracing_on # 查看跟踪结果 cat /sys/kernel/debug/tracing/trace > /tmp/trace.log ``` ### 注意事项 1. **权限要求**:查看`/proc/<PID>/stack`需要root权限(或者与目标进程相同的用户权限)。 2. **进程状态**:如果进程处于不可中断睡眠(D状态),则可能很难唤醒,但内核信息仍然可以查看。 3. **内核配置**:某些方法(如`/proc/<PID>/stack`)需要内核配置选项`CONFIG_STACKTRACE`的支持。 ### 示例输出解析 假设我们有一个hang住的进程,其`/proc/509/stack`输出如下: ``` [<0>] down_interruptible+0x50/0x70 [<0>] charger_irq_handler+0x18c/0x1d0 [bq24190_charger] [<0>] __handle_irq_event_percpu+0x70/0x1a0 [<0>] handle_irq_event_percpu+0x38/0x90 [<0>] handle_irq_event+0x48/0x78 [<0>] handle_fasteoi_irq+0xd0/0x1a0 [<0>] generic_handle_irq+0x30/0x48 [<0>] __handle_domain_irq+0x68/0xc0 [<0>] gic_handle_irq+0x60/0xb0 [<0>] el1_irq+0xb8/0x140 [<0>] cpuidle_enter_state+0x130/0x2b0 [<0>] cpuidle_enter+0x34/0x48 [<0>] do_idle+0x1d8/0x280 [<0>] cpu_startup_entry+0x24/0x28 [<0>] secondary_start_kernel+0x170/0x180 ``` 这个调用显示进程在`charger_irq_handler`函数中调用`down_interruptible`时被阻塞(等待信号量),说明该进程可能在等待一个锁,而这个锁没有被释放。 ### 相关问题 1. 如何区分进程是处于用户态hang还是内核态hang? 2. 当进程处于不可中断睡眠(D状态)时,如何进一步诊断? 3. 有哪些常见的内核函数会导致进程hang住(如锁、信号量等)? 4. 如何配置kdump以便在系统hang住时生成vmcore?</think>### 如何在Linux系统中查看hang住进程内核调用进程hang住(无响应)时,查看其内核调用是诊断问题的关键步骤。以下是详细的操作方法: #### 1. **定位目标进程的PID** ```bash ps -ef | grep <进程名> # 例如:ps -ef | grep kpoc_charger ``` 输出示例: ``` root 509 1 0 Jan01 ? 00:00:15 kpoc_charger ``` #### 2. **查看进程内核调用** ```bash cat /proc/<PID>/stack # 将<PID>替换为实际进程ID ``` 示例(查看PID 509的调用): ```bash cat /proc/509/stack ``` 典型输出分析: ``` [<0>] down_interruptible+0x50/0x70 [<0>] charger_irq_handler+0x18c/0x1d0 [bq24190_charger] [<0>] __handle_irq_event_percpu+0x70/0x1a0 [<0>] handle_irq_event+0x48/0x78 [<0>] retint_careful+0x12/0x18 <-- 阻塞点标识 ``` **关键解读**: - `down_interruptible`:进程在等待信号量 - `retint_careful`:典型的中断返回阻塞点[^4] - 函数名+偏移量:精确定位代码位置 #### 3. **增强诊断方法** ##### (1) 实时监控调用变化 ```bash watch -n 0.5 "cat /proc/509/stack" ``` ##### (2) 获取等待通道信息 ```bash cat /proc/<PID>/wchan # 显示进程等待的资源类型 ``` 示例输出: ``` down_interruptible ``` ##### (3) 使用sysrq触发全系统堆 ```bash echo l > /proc/sysrq-trigger # 需先启用sysrq dmesg | grep "Backtrace" -A 30 # 查看输出 ``` #### 4. **高级诊断工具** ##### (1) ftrace动态跟踪 ```bash # 配置跟踪 echo function_graph > /sys/kernel/debug/tracing/current_tracer echo 509 > /sys/kernel/debug/tracing/set_ftrace_pid echo 1 > /sys/kernel/debug/tracing/tracing_on # 捕获数据(等待数秒) cat /sys/kernel/debug/tracing/trace > /data/ftrace.log ``` ##### (2) crash工具分析(需vmcore) ```bash crash /usr/lib/debug/boot/vmlinux /var/crash/vmcore crash> bt 509 # 查看进程509的完整内核 ``` #### 5. **阻塞点分析参考表** | **阻塞函数** | **含义** | **常见原因** | |--------------------|---------------------------|---------------------------| | `down_interruptible` | 等待信号量 | 锁竞争或死锁[^2] | | `io_schedule` | 等待I/O完成 | 慢速存储设备或IO错误 | | `poll_schedule_timeout` | 等待poll事件 | 驱动未正确处理事件 | | `retint_careful` | 中断返回时的安全路径 | 调度延迟或中断风暴[^4] | | `__cond_resched` | 主动让出CPU | 优先级反转或CPU过载 | #### 6. **注意事项** 1. **权限要求**:查看`/proc/<PID>/stack`需要root权限 2. **进程状态**: - 仅对**运行中**(R)或**可中断睡眠**(S)进程有效 - **不可中断睡眠**(D状态)进程需结合驱动分析[^3] 3. **内核配置**:确保启用`CONFIG_STACKTRACE`内核选项 4. **符号解析**:安装`kernel-debuginfo`包获取完整符号信息 > **诊断提示**:当看到`retint_careful`阻塞点时,表明进程在中断返回路径被阻塞,通常与中断处理异常或调度延迟有关[^4]。此时应检查`/proc/interrupts`和CPU负载情况。 --- ### 相关问题 1. 如何区分用户态hang和内核态hang? 2. 当进程处于不可中断睡眠(D状态)时如何诊断? 3. Linux内核的锁机制有哪些?如何分析死锁问题? 4. ftrace工具在内核调试中的高级用法有哪些? 5. 如何配置kdump捕获系统hang住时的完整状态?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值