有个同事看3.10代码中,看着两个结构,会混淆,所以我简单答复了一下。

thread_info是和内核栈放一块的,网上到处都是thread_info的资料,但thread_struct的资料比较少,在此记录下,以备忘

struct thread_info {
struct task_struct *task; /* main task structure */
struct exec_domain *exec_domain; /* execution domain */
__u32 flags; /* low level flags */
__u32 status; /* thread synchronous flags */
__u32 cpu; /* current CPU */
int preempt_count; /* 0 => preemptable,
<0 => BUG */
mm_segment_t addr_limit;
struct restart_block restart_block;
void __user *sysenter_return;
#ifdef CONFIG_X86_32
unsigned long previous_esp; /* ESP of the previous stack in
case of nested (IRQ) stacks
*/
__u8 supervisor_stack[];
#endif
unsigned int sig_on_uaccess_error:;
unsigned int uaccess_err:; /* uaccess failed */
};

thread_info 在task_struct中的stack成员,它为啥能和内核栈成为union呢,按道理union里面的内容都是不同时有效的,也就是既然用作了A成员,则B不可能使用,但是明显我们的

thread_info结构和内核栈是同时使用的,其实可以理解为thread_info 放在了内核栈的下面,因为栈的增长方向是地址大到地址小,所以两者不冲突。这也间接说明了,

其实内核栈没有union那么大,要被thread_info占据一部分。放在一起还有个好处就是根据esp能够

快速地查找到task_struct的指针,因为thread_info的第一个成员就是task_struct指针。通过将esp的末尾几位设置为0就ok。到底设置多少位,是与栈的大小相关的。

比如64为的x86,默认内核栈大小为:

#define THREAD_SIZE_ORDER    2
#define THREAD_SIZE (PAGE_SIZE << THREAD_SIZE_ORDER)
#define get_current() (current_thread_info()->task)
#define current get_current() static inline struct thread_info *current_thread_info(void)
{
register unsigned long sp asm ("sp");
return (struct thread_info *)(sp & ~(THREAD_SIZE - ));
}

所以经常可以看到代码中使用 current 宏,就是通过sp指针来找到taks_struct.看下面的一个例子更能理解:

crash> bt
PID: TASK: ffff88290f7ddee0 CPU: COMMAND: "kthread_send/9"----------------当前task指针为ffff88290f7ddee0
# [ffff882fbe843a70] machine_kexec at ffffffff8105d77b
# [ffff882fbe843ad0] __crash_kexec at ffffffff8110aca2
# [ffff882fbe843ba0] panic at ffffffff816ad52f
# [ffff882fbe843c20] watchdog_timer_fn at ffffffff81135a51
# [ffff882fbe843c58] __hrtimer_run_queues at ffffffff810b93a6
# [ffff882fbe843cb0] hrtimer_interrupt at ffffffff810b993f
# [ffff882fbe843cf8] local_apic_timer_interrupt at ffffffff8105467b
# [ffff882fbe843d10] smp_apic_timer_interrupt at ffffffff816c9e83
# [ffff882fbe843d28] apic_timer_interrupt at ffffffff816c6732
# [ffff882fbe843dc8] queued_spin_lock_slowpath at ffffffff816adeee
# [ffff882fbe843dd8] _raw_spin_lock at ffffffff816bb080
# [ffff882fbe843de8] dev_watchdog at ffffffff815bca52
# [ffff882fbe843e28] call_timer_fn at ffffffff8109a9c8
# [ffff882fbe843e60] run_timer_softirq at ffffffff8109ceed
# [ffff882fbe843ed8] __do_softirq at ffffffff8109404d
# [ffff882fbe843f48] call_softirq at ffffffff816c8afc
# [ffff882fbe843f60] do_softirq at ffffffff8102d435
# [ffff882fbe843f80] irq_exit at ffffffff81094495
# [ffff882fbe843f98] smp_apic_timer_interrupt at ffffffff816c9e88
# [ffff882fbe843fb0] apic_timer_interrupt at ffffffff816c6732
--- <IRQ stack> ---
# [ffff882b680d3c28] apic_timer_interrupt at ffffffff816c6732
[exception RIP: ixgbe_xmit_frame_ring+]
RIP: ffffffffc01299e3 RSP: ffff882b680d3cd0 RFLAGS: 00000212---------------------在中断之前的rsp
RAX: RBX: RCX: 000000000000403d
RDX: ffff882fb9331c00 RSI: ffff8828d7b8fac0 RDI:
RBP: ffff882b680d3d48 R8: R9: 0000a0a5447b9d78
R10: ffff8828c6e84f00 R11: 000000002b3000b8 R12: ffff8828c0291b00
R13: R14: R15: ffff882b680d3cc0
ORIG_RAX: ffffffffffffff10 CS: SS:
# [ffff882b680d3d50] ixgbe_xmit_frame at ffffffffc012a918 [ixgbe]
# [ffff882b680d3d80] wit_send_tasklet at ffffffffc043b63c [witdriver]
# [ffff882b680d3e78] wit_kthread_xmit_fn at ffffffffc043ba95 [witdriver]
# [ffff882b680d3ec8] kthread at ffffffff810b5241
# [ffff882b680d3f50] ret_from_fork at ffffffff816c5577

根据task_struct 找stack:

crash> task_struct.stack ffff88290f7ddee0
stack = 0xffff882b680d0000
crash> rd 0xffff882b680d0000
ffff882b680d0000: ffff88290f7ddee0----------------------stack中的第一个成员就是指向task_struct的

再看看esp 的值  ffff882b680d3cd0 与 stack的值 0xffff882b680d0000 ,两者其实就是14位的相差,也就是 16k的低位不同。

有时候我们会遇到内核堆栈越界的情况,越界就是栈变量向下扩展的时候,踩到了thread_info结构的成员。

这时会遇到:Thread overran stack, or stack corrupted 这样的打印,判断的标准就是thread_info的上面留了一个magic特征字:

#define STACK_END_MAGIC        0x57AC6E9D

以下面例子来说明:

crash> struct thread_info
struct thread_info {
struct task_struct *task;
struct exec_domain *exec_domain;
__u32 flags;
__u32 status;
__u32 cpu;
int preempt_count;
mm_segment_t addr_limit;
struct restart_block restart_block;
void *sysenter_return;
unsigned int sig_on_uaccess_error : ;
unsigned int uaccess_err : ;
}
SIZE: 104

crash> px 0xffff882b680d0000 + 104
$8 = 0xffff882b680d0068

crash> rd 0xffff882b680d0068
ffff882b680d0068: 0000000057ac6e9d .n.W.... -----------------对应的magic特征字

 

在一些服务器中,经常会使用 echo 1 > /proc/sys/kernel/stack_tracer_enabled 的方式来监控线程栈,这个会使得能够打印最深的栈

cat /sys/kernel/debug/tracing/stack_trace
Depth Size Location ( entries)
----- ---- --------
) mempool_alloc_slab+0x15/0x20
) mempool_alloc+0x6e/0x170
) sg_pool_alloc+0x45/0x50
) __sg_alloc_table+0xd6/0x140
) sg_alloc_table_chained+0x3c/0x90
) scsi_init_sgtable+0x26/0x70
) scsi_init_io+0x4e/0x200
) sd_setup_read_write_cmnd+0x3d/0x950 [sd_mod]
) sd_init_command+0x2f/0xc0 [sd_mod]
) scsi_setup_cmnd+0x111/0x1c0
) scsi_prep_fn+0xdb/0x180
) blk_peek_request+0x16a/0x290
) scsi_request_fn+0x48/0x680
) __blk_run_queue+0x39/0x50
) cfq_insert_request+0x384/0x550
) __elv_add_request+0x1a2/0x2e0
) blk_queue_bio+0x35b/0x3a0
) generic_make_request+0x10b/0x320
) submit_bio+0x70/0x150
) _submit_bh+0x127/0x160
) submit_bh+0x10/0x20
) ext4_read_block_bitmap_nowait+0x48c/0x5f0 [ext4]
) ext4_mb_init_cache+0x181/0x6e0 [ext4]
) ext4_mb_load_buddy+0x2b6/0x340 [ext4]
) ext4_mb_regular_allocator+0x1d7/0x470 [ext4]
) ext4_mb_new_blocks+0x658/0xa20 [ext4]
) ext4_alloc_branch+0x3b9/0x430 [ext4]
) ext4_ind_map_blocks+0x34f/0x7b0 [ext4]
) ext4_map_blocks+0x2a5/0x6f0 [ext4]
) _ext4_get_block+0x1df/0x220 [ext4]
) ext4_get_block+0x16/0x20 [ext4]
) __block_write_begin+0x17d/0x4b0
) ext4_write_begin+0x18f/0x440 [ext4]
) generic_file_buffered_write+0x124/0x2c0
) __generic_file_aio_write+0x1e2/0x400
) generic_file_aio_write+0x59/0xa0
) ext4_file_write+0xdb/0x470 [ext4]
) do_sync_write+0x93/0xe0
) vfs_write+0xc0/0x1f0
) SyS_write+0x7f/0xe0
) system_call_fastpath+0x1c/0x21

如果新增加了内核模块,测试时最好能够监控起来,保证不会栈越界。

如果说 thread_info 在进程运行时访问很多,比如取当前task_struct指针,设置是否能够抢占的 preempt_count ,是跟arch体系无关的一些参数,那么thread_struct 就是与体系强相关的

一个结构了,比如x86的架构如下,321位和64位用一些宏来控制。

struct thread_struct {
/* Cached TLS descriptors: */
struct desc_struct tls_array[GDT_ENTRY_TLS_ENTRIES];
unsigned long sp0;
unsigned long sp;
#ifdef CONFIG_X86_32
unsigned long sysenter_cs;
#else
unsigned long usersp; /* Copy from PDA */
unsigned short es;
unsigned short ds;
unsigned short fsindex;
unsigned short gsindex;
#endif
#ifdef CONFIG_X86_32
unsigned long ip;
#endif
#ifdef CONFIG_X86_64
unsigned long fs;
#endif
unsigned long gs;
/* Save middle states of ptrace breakpoints */
struct perf_event *ptrace_bps[HBP_NUM];
/* Debug status used for traps, single steps, etc... */
unsigned long debugreg6;
/* Keep track of the exact dr7 value set by the user */
unsigned long ptrace_dr7;
/* Fault info: */
unsigned long cr2;
unsigned long trap_nr;
unsigned long error_code;
/* floating point and extended processor state */
struct fpu fpu;
#ifdef CONFIG_X86_32
/* Virtual 86 mode info */
struct vm86_struct __user *vm86_info;
unsigned long screen_bitmap;
unsigned long v86flags;
unsigned long v86mask;
unsigned long saved_sp0;
unsigned int saved_fs;
unsigned int saved_gs;
#endif
/* IO permissions: */
unsigned long *io_bitmap_ptr;
unsigned long iopl;
/* Max allowed port in the bitmap, in bytes: */
unsigned io_bitmap_max;
};

arm32的长成这样:

struct thread_struct {
/* fault info */
unsigned long address;
unsigned long trap_no;
unsigned long error_code;
/* debugging */
struct debug_info debug;
};

arm64的长成这样:

struct cpu_context {
unsigned long x19;
unsigned long x20;
unsigned long x21;
unsigned long x22;
unsigned long x23;
unsigned long x24;
unsigned long x25;
unsigned long x26;
unsigned long x27;
unsigned long x28;
unsigned long fp;
unsigned long sp;
unsigned long pc;
}; struct thread_struct {
struct cpu_context cpu_context; /* cpu context */
unsigned long tp_value;
struct fpsimd_state fpsimd_state;
unsigned long fault_address; /* fault info */
struct debug_info debug; /* debugging */
};

因为不同的结构,寄存器明显不一样,所以cpu的上下文显然不一样,这个结构就是用来保存在进程切换的时候,用于特定于arch的进程上下文切换的。

linux thread_info 与thread_struct的更多相关文章

  1. linux内核学习之二 一个精简内核的分析(基于时间片轮转)

    一   实验过程及效果 1.准备好相关的代码,分别是mymain.c,mypcb.h,myinterrupt.c ,如下图,make make成功: 在qemu创建的虚拟环境下的运行效果:(使用的命令 ...

  2. linux内核堆栈

    一:进程的堆栈 内核在创建进程的时候,在创建task_struct的同时会为进程创建相应的堆栈.每个进程会有两个栈,一个用户栈,存在于用户空间,一个内核栈,存 在于内核空间.当进程在用户空间运行时,c ...

  3. Linux用户抢占和内核抢占详解(概念, 实现和触发时机)--Linux进程的管理与调度(二十)

    1 非抢占式和可抢占式内核 为了简化问题,我使用嵌入式实时系统uC/OS作为例子 首先要指出的是,uC/OS只有内核态,没有用户态,这和Linux不一样 多任务系统中, 内核负责管理各个任务, 或者说 ...

  4. Linux进程核心调度器之主调度器schedule--Linux进程的管理与调度(十九)

    主调度器 在内核中的许多地方, 如果要将CPU分配给与当前活动进程不同的另一个进程, 都会直接调用主调度器函数schedule, 从系统调用返回后, 内核也会检查当前进程是否设置了重调度标志TLF_N ...

  5. Linux内核学习笔记(2)-- 父进程和子进程及它们的访问方法

    Linux系统中,进程之间有一个明显的继承关系,所有进程都是 PID 为1的 init 进程的后代.内核在系统启动的最后阶段启动 init 进程.该进程读取系统的初始化脚本(initscript)并执 ...

  6. Linux 驱动开发

    linux驱动开发总结(一) 基础性总结 1, linux驱动一般分为3大类: * 字符设备 * 块设备 * 网络设备 2, 开发环境构建: * 交叉工具链构建 * NFS和tftp服务器安装 3, ...

  7. /proc/version 的生成过程

    /proc/version 的生成过程 通常我们cat /proc/version时,会显示kernel相关的版本.编译等信息 那么问题来了,这些信息是怎么生成的呢? /proc/version文件是 ...

  8. 由于ptrace.h文件导致的内核编译出错的解决方法

    arch/x86/kernel/ptrace.c:1472:17: error: conflicting types for 'syscall_trace_enter'  In file includ ...

  9. Linux源码解析-内核栈与thread_info结构详解

    1.什么是进程的内核栈? 在内核态(比如应用进程执行系统调用)时,进程运行需要自己的堆栈信息(不是原用户空间中的栈),而是使用内核空间中的栈,这个栈就是进程的内核栈 2.进程的内核栈在计算机中是如何描 ...

随机推荐

  1. [UE4]使用另一个相机Scene Capture Component 2D当小地图

    挂一个相机(Scene Capture Component 2D)在人物角色的正上方,相机朝下,让UI上的某一块区域看到相机所显示的内容. 一.在人物角色正上方添加相机组件Scene Capture ...

  2. [UE4]多线程开关,开启的解决方案

    像这样直接获取值就会被警告. 解决方法:定义一个变量speed,然后在“Blueprint Update Animation”事件中赋值给这个变量. 这样就不会被警告了. 另外一种解决方法:就是关掉多 ...

  3. CentOS7 yum安装Java+Apache(httpd)+Tomcat并开启自启动

    首先,感觉yum里的东西质量不好的可以先换源. http://blog.csdn.net/qq_36731677/article/details/58288979 一.查询 两种方式可查询安装包 yu ...

  4. 通过注解实现一个简易的Spring mvc框架

    1.首先我们来搭建架构,就建一个普通的javaweb项目就OK了,具体目录如下: 对于小白来说可以细看后面web.xml的配置,对javaweb有点研究可以忽略而过后面的web.xml配置. 2.先上 ...

  5. postgresql数据库常用操作命令及SQL语言

    (1)登录 peng@peng-virtual-machine:~$ sudo -u postgres psql 以用户postgres身份登录,postgres为用户名,可有多个用户,登录时会要求输 ...

  6. ie-table不显示边框解决办法

    .thisTd {          background-clip: padding-box;           position:relative; } 原来背景也有边界的:决定背景会盖住哪些部 ...

  7. echart力导向图

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="GBK" ...

  8. 腾讯微信被怼,iOS版微信不能打赏了

    2017年4月19日,估计很多有着大量粉丝的微信自媒体作者会感到很不爽,因为他们的苹果粉丝再也无法很爽快地.肆意.任性地打赏他们了,按目前iphone手机的占有率,估计打赏率会掉一半以上. 据微信派微 ...

  9. courator - maven

    ZK3.4.x: <!-- https://mvnrepository.com/artifact/org.apache.curator/curator-recipes --> <de ...

  10. 在javascript中toString 和valueOf的区别

    1.toString()方法:主要用于Array.Boolean.Date.Error.Function.Number等对象转化为字符串形式.日期类的toString()方法返回一个可读的日期和字符串 ...