Sea*_*den 12
你的确可以.有几种方法可以实现它.困难的部分是最初获得指向其他堆栈的jmpbufs.Longjmp仅针对由setjmp创建的jmpbuf参数定义,因此如果不使用汇编或利用未定义的行为,就无法执行此操作.用户级线程本质上不是可移植的,因此可移植性并不是没有真正做到的强有力的论据.
步骤1 您需要一个存储不同线程的上下文的位置,因此为您想要的多个线程创建一个jmpbuf结构的队列.
步骤2 您需要为每个线程malloc一个堆栈.
步骤3 您需要获取一些jmpbuf上下文,这些上下文在您刚分配的内存位置中具有堆栈指针.您可以检查计算机上的jmpbuf结构,找出它存储堆栈指针的位置.调用setjmp然后修改其内容,以便堆栈指针位于您分配的堆栈中.堆栈通常会长大,因此您可能希望堆栈指针位于最高内存位置附近.如果你编写一个基本的C程序并使用调试器来反汇编它,然后找到它从函数返回时执行的指令,你就可以找出偏移应该是什么.例如,使用x86上的系统V调用约定,您将看到它弹出%ebp(帧指针)然后调用ret,它将返回地址弹出堆栈.因此,在进入函数时,它会推送返回地址和帧指针.每次推送都会将堆栈指针向下移动4个字节,因此您希望堆栈指针从分配区域的高地址开始,-8字节(就像您刚刚调用函数来实现那样).接下来我们将填充8个字节.
您可以做的另一件事是编写一些非常小的(一行)内联汇编来操作堆栈指针,然后调用setjmp.这实际上更具可移植性,因为在许多系统中,jmpbuf中的指针会因安全性而受损,因此您无法轻松修改它们.
我没有尝试过,但你可以通过声明一个非常大的数组并因此移动堆栈指针故意溢出堆栈来避免asm.
步骤4 您需要退出线程以使系统返回某个安全状态.如果你不这样做,并且其中一个线程返回,它将把你分配的堆栈上方的地址作为返回地址并跳转到一些垃圾位置并且可能是段错误.所以首先你需要一个安全的地方返回.通过在主线程中调用setjmp并将jmpbuf存储在全局可访问的位置来获取此信息.定义一个不带参数的函数,只用已保存的全局jmpbuf调用longjmp.获取该函数的地址并将其复制到您为返回地址留出空间的已分配堆栈.您可以将帧指针留空.现在,当一个线程返回时,它将转到调用longjmp的函数,并且每次都跳回到你调用setjmp的主线程中.
步骤5 在主线程的setjmp之后,你想要一些代码来确定哪个线程跳转到下一个,从队列中拉出适当的jmpbuf并调用longjmp去那里.当该队列中没有剩余线程时,程序就完成了.
步骤6 编写一个上下文切换函数,它调用setjmp并将当前状态存储回队列,然后longjmp从队列中另一个jmpbuf.
结论 这是基础知识.只要线程继续调用上下文切换,队列就会不断重新填充,并且不同的线程会运行.当线程返回时,如果还有剩余要运行,主线程选择一个,如果没有剩下,则进程终止.使用相对较少的代码,您可以拥有一个非常基本的协作式多任务设置.你可能想做更多的事情,比如实现一个清理函数来释放死线程的堆栈等等.你也可以使用信号实现抢占,但这要困难得多,因为setjmp不保存浮点寄存器状态或标志寄存器,当程序异步中断时是必需的.
它可能会稍微弯曲规则,但GNU pth会这样做.这是可能的,但你可能不应该自己尝试,除非作为一个学术概念验证练习,如果你想认真地并以一种远程便携的方式使用pth实现 - 你会明白为什么当你阅读第p个线程创建代码.
(基本上它使用一个信号处理程序来欺骗操作系统创建一个新的堆栈,然后longjmp离开那里并保持堆栈.它显然有效,但它是粗略的,因为地狱.)
在生产代码中,如果您的操作系统支持makecontext/swapcontext,请改用它们.如果它支持CreateFiber/SwitchToFiber,请改用它们.并意识到了令人失望的道理,最引人注目的使用协程的一个 - 那就是,通过产生出由外国代码调用事件处理程序的反相控制 - 因为调用模块必须折返是不安全的,你通常可以不能证明这一点.这就是.NET中不支持光纤的原因......
这是所谓的用户空间上下文切换的一种形式。
这是可能的,但容易出错,特别是如果您使用 setjmp 和 longjmp 的默认实现。这些函数的一个问题是,在许多操作系统中,它们只会保存 64 位寄存器的子集,而不是整个上下文。这通常是不够的,例如在处理系统库时(我的经验是使用 amd64/windows 的自定义实现,考虑到所有因素,它的工作相当稳定)。
也就是说,如果您不尝试使用复杂的外部代码库或事件处理程序,并且您知道自己在做什么,并且(特别是)如果您在汇编器中编写自己的版本来保存更多当前上下文(如果您使用 32 位 Windows 或 Linux,这可能没有必要,如果您使用某些版本的 BSD,我想它几乎肯定是必要的),并且您调试它并仔细注意反汇编输出,那么您可能能够实现什么你要。