[其他ST产品] ST源码分析-st_thread_create - 弦外之音

[复制链接]
4264|96
 楼主| 9dome猫 发表于 2023-4-21 12:23 | 显示全部楼层
上面的代码,ST_KEYS_MAX 等于 16,(void *) 在 32位占 4字节,在 64 位占 8字节。所以上面的代码逻辑是 从之前申请的内存里面拿出 16 * 8 个字节的内存 存储协程的私有数据,这块内存在 ST 代码里没有使用,是留给使用者自由发挥的。想存什么就存什么变量 ptds 的全称是 private_data,ptds 指向数据的开始地址。

sp = sp - sizeof(_st_thread_t);
thread = (_st_thread_t *) sp;
 楼主| 9dome猫 发表于 2023-4-21 12:23 | 显示全部楼层
再 从 之前申请的内存里面拿出 sizeof(_st_thread_t) 大小的内存来存储 _st_thread_t 协程。

/* Make stack 64-byte aligned */
if ((unsigned long)sp & 0x3f)
  sp = sp - ((unsigned long)sp & 0x3f);
stack->sp = sp - _ST_STACK_PAD_SIZE;
 楼主| 9dome猫 发表于 2023-4-21 12:25 | 显示全部楼层
上面的代码就是把 sp 做 64位对齐,最后 再拿走 _ST_STACK_PAD_SIZE 大小的内存,_ST_STACK_PAD_SIZE 等于 128 。
 楼主| 9dome猫 发表于 2023-4-21 12:25 | 显示全部楼层
上面全部代码执行完之后,栈的内存布局如下:
370236442103fd2625.png
 楼主| 9dome猫 发表于 2023-4-21 12:25 | 显示全部楼层
从上图可以看出,stack->stk_bottom ~ stack->stk_sp 中间的空白区域,这块区域 是 协程函数局部变量用的。
 楼主| 9dome猫 发表于 2023-4-21 12:25 | 显示全部楼层
继续 分析 st_thread_create() 的后续代码逻辑。

memset(thread, 0, sizeof(_st_thread_t));
memset(ptds, 0, ST_KEYS_MAX * sizeof(void *));

/* Initialize thread */
thread->private_data = ptds;
thread->stack = stack;
thread->start = start;
thread->arg = arg;
 楼主| 9dome猫 发表于 2023-4-21 12:26 | 显示全部楼层
上面这些都是 变量操作,初始化逻辑,跳过,继续看下面代码。

_ST_INIT_CONTEXT(thread, stack->sp, _st_thread_main);
 楼主| 9dome猫 发表于 2023-4-21 12:26 | 显示全部楼层
_ST_INIT_CONTEXT 实际上就是 MD_INIT_CONTEXT,我的环境是 Linux + x86_64,所以MD_INIT_CONTEXT 宏定义如下:

#define MD_INIT_CONTEXT(_thread, _sp, _main) \
  ST_BEGIN_MACRO                             \
  if (MD_SETJMP((_thread)->context))         \
    _main();                                 \
  MD_GET_SP(_thread) = (long) (_sp);         \
  ST_END_MACRO
 楼主| 9dome猫 发表于 2023-4-21 12:26 | 显示全部楼层
_ST_INIT_CONTEXT 实际上就是 MD_INIT_CONTEXT,我的环境是 Linux + x86_64,所以MD_INIT_CONTEXT 宏定义如下:

#define MD_INIT_CONTEXT(_thread, _sp, _main) \
  ST_BEGIN_MACRO                             \
  if (MD_SETJMP((_thread)->context))         \
    _main();                                 \
  MD_GET_SP(_thread) = (long) (_sp);         \
  ST_END_MACRO
 楼主| 9dome猫 发表于 2023-4-21 12:31 | 显示全部楼层
_st_md_cxt_save 函数的汇编实现如下:

.globl _st_md_cxt_save
        .type _st_md_cxt_save, @function
        .align 16
_st_md_cxt_save:
        /*
         * Save registers.
         */
        movq %rbx, (JB_RBX*8)(%rdi)
        movq %rbp, (JB_RBP*8)(%rdi)
        movq %r12, (JB_R12*8)(%rdi)
        movq %r13, (JB_R13*8)(%rdi)
        movq %r14, (JB_R14*8)(%rdi)
        movq %r15, (JB_R15*8)(%rdi)
        /* Save SP */
        leaq 8(%rsp), %rdx
        movq %rdx, (JB_RSP*8)(%rdi)
        /* Save PC we are returning to */
        movq (%rsp), %rax
        movq %rax, (JB_PC*8)(%rdi)
        xorq %rax, %rax
        ret
        .size _st_md_cxt_save, .-_st_md_cxt_save
 楼主| 9dome猫 发表于 2023-4-21 12:31 | 显示全部楼层
回到 MD_SETJMP((_thread)->context) 这里的 context 是一个 jmp_buf 的结构, jmp_buf 是操作系统提供的
 楼主| 9dome猫 发表于 2023-4-21 12:31 | 显示全部楼层
为了理清楚 MD_SETJMP((_thread)->context) 这句代码干了什么事情,需要一步一步分析。

MD_SETJMP((_thread)->context) 等于 _st_md_cxt_save(env),_st_md_cxt_save 是一个汇编函数,之前没有讲过 汇编函数,如果传参,这个参数是怎么压进去堆栈,寄存器又是如何变换,现在就用 gdb 演示一次。
 楼主| 9dome猫 发表于 2023-4-21 12:48 | 显示全部楼层
命令如下:

gdb ./obj/lookupdns
# 设置参数
set args xianwaizhiyin.net www.baidu.com
# 断点 1
b *st_thread_create
# 断点 2
b sched.c:588
# 查看 寄存器
layout regs
# 查看 C 源码
layout src
# 查看 汇编 源码
layout asm
# 打印地址
print &(thread->context)
 楼主| 9dome猫 发表于 2023-4-21 12:49 | 显示全部楼层
 楼主| 9dome猫 发表于 2023-4-21 12:49 | 显示全部楼层
如上图所示,在调 callq 之前 ,rax 跟 rdi 的值 就是 thread->context 的内存地址。所以参数 env 是通过 rdi 寄存器传进去给 _st_md_cxt_save 函数用的。
chenho 发表于 2023-4-21 21:25 | 显示全部楼层
ptds 的全称应该是 pthread_data_register
langgq 发表于 2023-4-21 21:26 | 显示全部楼层
sp 的全称 是啥
chenho 发表于 2023-4-21 21:28 | 显示全部楼层
stack point
langgq 发表于 2023-4-21 21:30 | 显示全部楼层
用什么获取操作系统的内存页大小
dingy 发表于 2023-4-21 21:31 | 显示全部楼层
langgq 发表于 2023-4-21 21:30
用什么获取操作系统的内存页大小

getpagesize()
您需要登录后才可以回帖 登录 | 注册

本版积分规则

快速回复 在线客服 返回列表 返回顶部