多线程:ARM linux平台上线程栈信息的建立流程

本文深入探讨了在ARM Linux系统中,如何创建并管理线程栈的详细流程,包括线程的初始化、栈空间分配以及线程上下文的建立。通过理解这一过程,读者将能更好地掌握多线程编程在嵌入式系统中的实践。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  •   联合体  thread_union:表示一个进程的线程描述符和内核栈大小定义

    union thread_union {

    struct thread_info thread_info;

    unsigned long stack[THREAD_SIZE/sizeof(long)];根据联合体的定义和作用,一个 thread_info的大小也就是栈的大小

    };

    线程描述符:----也可以理解成线程的”逻辑栈信息“,注意不是与cpu栈信息,这里仅仅是软件层面上线程的“栈信息”

    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 */-----cpu的寄存器 层面栈的信息

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

    int                        cpu_excp;

    void                         *regs_on_excp;

    };

     

    小结: thread_union与 thread_info  是一样的作用,只是 thread_union 做了一个stack大小的定义。

     

      线程栈信息的建立流程

    概述:首先 系统启动的时候,init线程首先分配了一个thread_union;以后其他的线程创建的时候,都是copyinit线程的thread_union结构,复制出一个线程副本出来。

     

    下面分析:

    1)init_task 相关栈和task信息的建立

    这里关注栈信息(线程描述信息)

    1.1)初始化设置栈信息:参考”内核栈的初始化设置过程“

    什么意思呢?就是建立第一个init线程的对应cpu 体系的栈信息结构的起始位置SP指针,与后面系统建立其他线程的栈无关,其他线程自己会分配自己的空间,初始化两个数据结构:一个是thread_info,另一个就是task_struct

     

    thread_info的定义,

    0号线程是通过联合体 thread_union 定义对象:”init_thread_union“:

    init_thread_union定义和初始化在哪里呢?如下:

     union thread_union init_thread_union __init_task_data =

            { INIT_THREAD_INFO(init_task) };

     

    __init_task_data只是一个宏,来自:

    Init_task.h (zaw809aa_lava\kernel\include\linux):#define __init_task_data __attribute__((__section__(".data..init_task")))

    #define __init_task_data __attribute__((__section__(".data.init_task")))

     

    那么,上面的内容就展开成了:

    union thread_union init_thread_union __attribute__((__section__(".data.init_task"))) ={ INIT_THREAD_INFO(init_task) };并且把数据放进指定的数据段.data.init_task

     

    那么如何完成init_thread_union 赋值呢?

    Step1 )先执行宏:

    #define INIT_THREAD_INFO(tsk)                                                \

    {                                                                        \

    .task                = &tsk,                                                \

    .exec_domain        = &default_exec_domain,                                \

    .flags                = 0,                                                \

    .preempt_count        = INIT_PREEMPT_COUNT,                                \

    .addr_limit        = KERNEL_DS,                                        \

    .cpu_domain        = domain_val(DOMAIN_USER, DOMAIN_MANAGER) |        \

      domain_val(DOMAIN_KERNEL, DOMAIN_MANAGER) |        \

      domain_val(DOMAIN_IO, DOMAIN_CLIENT),                \

    .restart_block        = {                                                \

    .fn        = do_no_restart_syscall,                        \

    },                                                                \

    }

    所以union thread_union init_thread_union __attribute__((__section__(".data.init_task"))) ={ INIT_THREAD_INFO(init_task) };展开就是:

    union thread_union init_thread_union __attribute__((__section__(".data.init_task"))) ={

    .task                = &tsk,                                                \

    .exec_domain        = &default_exec_domain,                                \

    .flags                = 0,                                                \

    .preempt_count        = INIT_PREEMPT_COUNT,                                \

    .addr_limit        = KERNEL_DS,                                        \

    .cpu_domain        = domain_val(DOMAIN_USER, DOMAIN_MANAGER) |        \

      domain_val(DOMAIN_KERNEL, DOMAIN_MANAGER) |        \

      domain_val(DOMAIN_IO, DOMAIN_CLIENT),                \

    .restart_block        = {                                                \

    .fn        = do_no_restart_syscall,                        \

    },                                                                \

    };

    可以看到在初始化thread_info 的成员(task,exec_domain,flags,preempt_count        ,addr_limit等);

     

    Step2)然后执行struct task_struct init_task = INIT_TASK(init_task);

    Step3)最后执行完 INIT_TASK(tsk)这个宏以后,init_thread_union就被初始化成以上内容了,至此,0号进程的task_structthread_info就初始化完毕了:

    /*

     * Initial task structure.

     *

     * All other task structs will be allocated on slabs in fork.c

     */

    struct task_struct init_task = INIT_TASK(init_task); 每一个线程 对应一个task_struct任务调度的基本单位的初始化。

     

     

    #define INIT_TASK(tsk)        \

    {                                                                        \

    .state                = 0,                                                \

    .stack                = &init_thread_info,                                \init线程的描述符(也就是栈信息)指向栈空间

    .usage                = ATOMIC_INIT(2),                                \

    .flags                = PF_KTHREAD,                                        \

    ….

    }

     

    ===============================================================

    2)其他线程的创建的时候,copy 当前线程(init线程是 一切线程的鼻祖)task_struct信息和栈内存空间:

    copy_process()---->p = dup_task_struct(current);

    static struct task_struct *dup_task_struct(struct task_struct *orig)

    {

    struct task_struct *tsk;

    struct thread_info *ti;//创建thread_info 也就是栈

    unsigned long *stackend;

    int node = tsk_fork_get_node(orig);

    int err;

     

    prepare_to_copy(orig);

     

    tsk = alloc_task_struct_node(node);//在当前线程的node 上分配和复制该task信息

    if (!tsk){

    printk("[%d:%s] fork fail at alloc_tsk_node, please check kmem_cache_alloc_node()\n", current->pid, current->comm);

    return NULL;

    }

    ti = alloc_thread_info_node(tsk, node);////在当前线程的node 上分配和复制该taskstack信息(栈)

    if (!ti) {

    printk("[%d:%s] fork fail at alloc_t_info_node, please check alloc_pages_node()\n", current->pid, current->comm);

    free_task_struct(tsk);

    return NULL;

    }

     

    err = arch_dup_task_struct(tsk, orig);

    if (err){

    printk("[%d:%s] fork fail at arch_dup_task_struct, err:%d \n", current->pid, current->comm, err);

    goto out;

    }

    tsk->stack = ti;

     

    setup_thread_stack(tsk, orig);//把当前taskstack成员,数据类型转化成栈信息结构:thread_info

    clear_user_return_notifier(tsk);

    clear_tsk_need_resched(tsk);

    stackend = end_of_stack(tsk);

    *stackend = STACK_END_MAGIC;        /* for overflow detection */

    …...

    }

    #define task_thread_info(task)        ((struct thread_info *)(task)->stack)//把(task)->stack数据类型格式化成栈信息结构thread_info,

    #define task_stack_page(task)        ((task)->stack)

     

    static inline void setup_thread_stack(struct task_struct *p, struct task_struct *org)

    {

    *task_thread_info(p) = *task_thread_info(org);

    task_thread_info(p)->task = p;

    }

     

     

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值