我知道每个线程通常都有一个调用堆栈,它只是一个内存块,并使用esp和ebp进行控制。
这些调用堆栈是如何创建的,是谁负责的?我猜是运行时,例如iOS应用程序的Swift运行时。线程是通过esp和ebp或通过运行时直接与自己的调用堆栈对话吗?
2,对于每个调用堆栈,它们必须与esp和ebb cpu寄存器一起工作,如果我有一个具有2个内核的CPU (4个线程),那么假设它有4个核心(指令集)。这是否意味着每个调用堆栈将只处理特定核心中的这些寄存器?
发布于 2019-10-16 11:22:51
XNU内核就是这么做的。Swift线程是POSIX线程,也就是马赫线程。在程序启动期间,XNU内核解析Mach-O可执行格式,并处理现代LC_MAIN或遗留LC_UNIXTHREAD load命令(除其他外)。这是在内核函数中处理的:
static
load_return_t
load_main(
struct entry_point_command *epc,
thread_t thread,
int64_t slide,
load_result_t *result
)&
static
load_return_t
load_unixthread(
struct thread_command *tcp,
thread_t thread,
int64_t slide,
load_result_t *result
)哪个恰好是开源
LC_MAIN通过thread_userstackdefault初始化堆栈
LC_UNIXTHREAD通过load_threadstack。
正如@PeterCordes在注释中提到的那样,只有当内核创建主线程时,已启动的进程本身才能通过一些api (如GCD )或直接通过syscall (bsdthread_create,不确定是否还有其他线程)从它自己的主线程中生成子线程。syscall碰巧有user_addr_t stack作为它的第三个参数(即MacOS使用的x86-64 System内核ABI中的rdx )。MacOS系统参考
我还没有彻底研究过这个特定堆栈参数的细节,但我可以想象它类似于thread_userstackdefault / load_threadstack方法。
我相信您对Swift运行时责任的怀疑可能是因为经常提到数据结构(比如Swift struct --没有双关语)存储在堆栈中(顺便说一下,这是实现的细节,运行时的特性也没有保证)。
更新
他是一个例子,main.swift命令行程序稀释了这个想法。
import Foundation
struct testStruct {
var a: Int
}
class testClass {
}
func testLocalVariables() {
print("main thread function with local varablies")
var struct1 = testStruct(a: 5)
withUnsafeBytes(of: &struct1) { print($0) }
var classInstance = testClass()
print(NSString(format: "%p", unsafeBitCast(classInstance, to: Int.self)))
}
testLocalVariables()
print("Main thread", Thread.isMainThread)
var struct1 = testStruct(a: 5)
var struct1Copy = struct1
withUnsafeBytes(of: &struct1) { print($0) }
withUnsafeBytes(of: &struct1Copy) { print($0) }
var string = "testString"
var stringCopy = string
withUnsafeBytes(of: &string) { print($0) }
withUnsafeBytes(of: &stringCopy) { print($0) }
var classInstance = testClass()
var classInstanceAssignment = classInstance
var classInstance2 = testClass()
print(NSString(format: "%p", unsafeBitCast(classInstance, to: Int.self)))
print(NSString(format: "%p", unsafeBitCast(classInstanceAssignment, to: Int.self)))
print(NSString(format: "%p", unsafeBitCast(classInstance2, to: Int.self)))
DispatchQueue.global(qos: .background).async {
print("Child thread", Thread.isMainThread)
withUnsafeBytes(of: &struct1) { print($0) }
withUnsafeBytes(of: &struct1Copy) { print($0) }
withUnsafeBytes(of: &string) { print($0) }
withUnsafeBytes(of: &stringCopy) { print($0) }
print(NSString(format: "%p", unsafeBitCast(classInstance, to: Int.self)))
print(NSString(format: "%p", unsafeBitCast(classInstanceAssignment, to: Int.self)))
print(NSString(format: "%p", unsafeBitCast(classInstance2, to: Int.self)))
}
//Keep main thread alive indefinitely so that process doesn't exit
CFRunLoopRun()我的输出如下:
main thread function with local varablies
UnsafeRawBufferPointer(start: 0x00007ffeefbfeff8, count: 8)
0x7fcd0940cd30
Main thread true
UnsafeRawBufferPointer(start: 0x000000010058a6f0, count: 8)
UnsafeRawBufferPointer(start: 0x000000010058a6f8, count: 8)
UnsafeRawBufferPointer(start: 0x000000010058a700, count: 16)
UnsafeRawBufferPointer(start: 0x000000010058a710, count: 16)
0x7fcd0940cd40
0x7fcd0940cd40
0x7fcd0940c900
Child thread false
UnsafeRawBufferPointer(start: 0x000000010058a6f0, count: 8)
UnsafeRawBufferPointer(start: 0x000000010058a6f8, count: 8)
UnsafeRawBufferPointer(start: 0x000000010058a700, count: 16)
UnsafeRawBufferPointer(start: 0x000000010058a710, count: 16)
0x7fcd0940cd40
0x7fcd0940cd40
0x7fcd0940c900现在我们可以观察到一些有趣的事情:
Class实例显然占用了与Structs不同的内存部分。Structs时,主线程和子线程都指向完全相同的内存。Update2 - 4^的证明我们实际上可以检查下面的内存:
x 0x10058a6f0 -c 8
0x10058a6f0: 05 00 00 00 00 00 00 00 ........
x 0x10058a6f8 -c 8
0x10058a6f8: 05 00 00 00 00 00 00 00 ........因此,这肯定是实际的结构原始数据,即,结构本身,。
更新3
我添加了一个testLocalVariables()函数,以区分Swift Struct定义为全局变量和局部变量。在这种情况下
x 0x00007ffeefbfeff8 -c 8
0x7ffeefbfeff8: 05 00 00 00 00 00 00 00 ........它显然存在于线程堆栈上。
最后但并非最不重要的是,在lldb中,我需要:
re read rsp
rsp = 0x00007ffeefbfefc0 from main thread
re read rsp
rsp = 0x000070000291ea40 from child thread它为每个线程产生不同的值,因此线程堆栈显然是不同的。
进一步挖掘
这里有一个方便的记忆区 lldb命令,它可以显示出正在发生的事情。
memory region 0x000000010058a6f0
[0x000000010053d000-0x000000010058b000) rw- __DATA因此,全局Structs位于预先分配的可写可写__DATA内存页面(与您的全局变量所在的页面相同)。对于类0x7fcd0940cd40地址的相同命令没有那么引人注目(我估计是因为这是一个动态分配的堆)。类似于线程堆栈地址0x7ffeefbfefc0,它显然不是进程内存区域。
幸运的是,还有最后一个工具可以进一步深入兔子洞。
vmmap -v -purge pid确实确认类位于MALLOC_ed堆中,同样地,线程堆栈(至少对于主线程)可以交叉引用到Stack。
有些相关的问题也是这里。
HTH
发布于 2019-10-16 04:16:15
(我假设Swift线程就像其他语言中的线程一样。实际上并没有很多好的选择,无论是普通的OS级线程还是用户空间的“绿色线程”,或者两者的混合。区别只是在发生上下文切换的情况下;主要概念仍然相同)
每个线程都有自己的堆栈,由mmap或父线程在进程的地址空间中分配,或者通过创建线程的同一个系统调用来分配。iOS系统调用。在Linux中,您必须将一个void *child_stack传递给实际创建一个新线程的clone(2)系统调用。直接使用特定于操作系统的低级别系统调用是非常罕见的;语言运行库可能会在像pthread_create这样的线程函数的基础上执行线程处理,而线程库将处理特定于操作系统的详细信息。
是的,每个软件线程都有自己的体系结构状态,包括x86-64或论AArch64上的RSP。(如果您制作了过时的32位x86代码,也可以使用ESP )。我认为框架指针是可选的,对斯威夫特。
是的,每个逻辑核心都有自己的体系结构状态(寄存器包括堆栈指针);软件线程在逻辑核心上运行,并且在软件线程之间切换保存/恢复寄存器。相关的,可能是线程之间共享哪些资源?的副本。
软件线程共享相同的页表(虚拟地址空间),但不共享寄存器。
https://stackoverflow.com/questions/58405568
复制相似问题