C_and_CPP 板


LINE

看了一系列的旧文章「Re: 什麽是 multi-thread」 https://webptt.com/cn.aspx?n=man/Programming/D156/DB3A/D13D/M.1147208004.A.A92.html 才知道原来 thread 是不需要 os kernel 支援就可以办到的, 之前一直以为需要 os kernel 支援 kernel thread, thread library 才有办法实作出来, 因为我实在想不到如果 kernel 不支援 kernel thread, library 到底要怎麽支援 thread, 在 user mode 要靠「什麽」才有办法在 2 个 function 之间切换。 噢! 当然战文本身也是很精彩的, 这系列文章有些你来我往的回文, 只要能说出个道理, 都是能从中学到东西的。 coroutine 之前研究过, 它是主动让出 cpu 执行权, 如果不主动让出, 该怎麽在执行的 function 中让出 cpu 呢? os 靠的是 timer 中断, user mode 程式要怎麽作到类似的效 果呢? 在查询资料的过程, 找到 pthreads-1_60_beta6.tar.gz 这个 user mode pthread, 是由 Chris Provenzano 开发的 pthread 实作品。 这是搭配 linux kernel 1.X 用的 pthread library, 只支援 x86 32bit, 那时候的 linux kernel 还没有 kernel thread 支援, 我像是挖到宝似的, 想看 Chris Provenzano 是怎麽办到的, 本想编译起来用 gdb 追踪, 不过在目前的环境似乎编不起来, 我放弃了, 而我追 code 能力太差, 没有从 souce code 看出什麽端倪。 另外找到一个课程的作业 - CS170: Project 2 - User Mode Thread Library (20% of project score), 我的妈呀! 还真的有这样的课程, 不禁为他们学生默哀, 这应该会是让 他们困扰很久的作业的吧! 这是 ucsb 的 os 课程。 ucsb 中文是 - 加州大学圣塔芭芭拉分校, 不太熟悉这间学校。 看到这些学校的作业是这麽的扎实, 记得自己的 os 课程就是教课书 - Operating System Concepts 的内容, 考试则是课本上的知识, 程度真的差太多, 实作太少了。 但是该课程也没那麽狠心, 在 Implementation 一节中, 说明的一些实作细节, 用着我的 破英文很勉强的看了看, 得知了几个关键。 需要使用 setjmp/longjmp, signal。 知道这个概念之後, 相当高兴, 以为可以顺利写出来, 但在我开始下手时, 却发现困难重 重, 我不知道应该在哪里执行 setjmp, 在哪里执行 longjmp。 ex.c 1 void func1() 2 { 3 printf("1\n"); 4 printf("2\n"); 5 printf("3\n"); 6 printf("4\n"); 7 printf("5\n"); 8 } 9 10 void func2() 11 { 12 printf("21\n"); 13 printf("22\n"); 14 printf("23\n"); 15 printf("24\n"); 16 printf("25\n"); 17 } ex.c 我该在哪里插入 setjmp 呢? ex.c L3 ~ L7 之间吗? 都不对阿! longjmp 应该安插 在哪里呢? Implementation 原来还有背面, 我漏看了, 重新看过之後得到以下心得: 1. setjmp/longjmp - 这个用来保存 2 个 function 切换的状态, 还需要特别保存 stack。 2. signal/SIGALRM - 这个就是我百思不得其解的关键, 使用 signal 来中断正在执行 的 function, 在 signal handler 中, 保存正在执行的 function 状态 (使用 setjmp), 再选出一个 function, 跳去执行它 (透过 longjmp)。 而在看过一些 source code 之後, 我又得到一些心得, 需要修改 jmp_buf 的 esp, eip 栏位。 CS170: Project 2 - User Mode Thread Library (20% of project score) Project Goals ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ The goals of this project are: ‧ to understand the idea of threads ‧ to implement independent, parallel execution within a process Administrative Information Implementation ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 略 ... 其实在看提示之前, 我有想到应该在 signal handler 使用 setjmp/longjmp, 只是我被 自 己迷惑了, 因为在 signal handler 的 stack, 已经不是原本程式的 stack, 为了跳到 signal handle, kernel 对原本的 stack 做了修改, 我自以为在这里保存这个 stack 是 没有用的, 是我自己想太多了。 基本概念是这样, 假设我们有 func1, func2 这 2 个 function, func1 先执行, 使用 alarm signal, 让 5ms 发动一次 alarm signal, 5ms 就会呼叫一次 signa handler, 这 时候就可以在这里将目前执行的 function - func1 setjmp 起来, 然後使用 longjmp 跳 到 func2 去执行, 这样就完成了 5ms 切换 func1, func2, 就达到了 user mode thread 的效果。 这个概念和 os 的 process 切换是类似的。 而对 func1, func2 来说, 需要有各自的 stack, 这样才不会有相互盖到 stack 的问题, 使用 setjmp 来保存 register 资料外, 还需要提供一个 stack 空间, 所以要把 jmp_buf 的 esp 栏位改到预先准备好的 stack 空间, simple_thread.c L112, L117。 另外要修改 jmp_buf 的另外一个栏位是 eip, 需要把它指到 func1, func2 的开头, 这 样一来, longjmp 就会从 func1, func2 的开头执行。 而不幸的是, jmp_buf 和执行的 cpu 有关系, 所以得要搞懂这个平台的 jmp_buf 是怎麽 安排这些暂存器的资料结构。 Implementation 还提供了另外一个重要的讯息, 由於为了安全, jmp_buf 都会被用一个 演算法保护起来, 避免被乱改, 所以 Implementation 提供了一段程式码 帮助同学处理这部份。 我没有用这些方式, 我懒得搞懂这些, 我只想搞懂 user mode thread 怎麽做而已, 所以 准备了自己的 setjmp/longjmp, 叫做 my_setjmp, my_long_jmp, 当然对应的 jmp_buf 就是 my_jmp_buf。 再来还剩下一个难题: 在 signal handler 发动自己准备的 my_longjmp 之後, 会发现之 後的 signal handler 不会再次被呼叫了, 这里存在一个很难发现的魔法, 需要对 singal 是怎麽实作有点了解才会知晓或是阅读相关介绍 signal 书籍, tlpi (The Linux Programming Interface) 那本就不错, 经典的 apue (Advanced Programming in the UNIX Environment) 当然也是。 如果想知道 signal 是怎麽实作的话, 可以参考「Linux 内核源代码情景分析」6.4 一节 。 总之在 signal handler 被呼叫之後, 预设情形这个 signal 会被 block 起来, 直到 signal handler 返回之後, 才会被 unblock, 这时候, 同个 signal 来了之後, 这个 signal handler 才会再次发动。 但是我们的 signal handler 并不会正常返回, 因为我们用 longjmp 跳到 func1 或是 func2, 所谓的 signal handler 正常返回是指在 signal handler return 之後, 还会呼 叫 sigreturn (man sigreturn), 这时候会从 user mode 再次切回 kernel mode, 然後 才有机会把原来被中断的地方再次安插回原本的 stack, 如此一来, 下次这个 process 执行的时候, 才会从被中断的地方继续执行。 所以被 block 的 SIGUSR1 会被一直 block 住, 导致之後的收到 SIGUSR1 後, 都不会再执行 signal handler。 所以要在 simple_thread.c 加入 L62 ~ L64, unblock SIGUSR1。 但是如果你是使用 libc 的 setjmp/longjmp, sigsetjmp/siglongjmp 可能不需要自己 unblock SIGUSR1, 系统的 setjmp/longjmp 可能会处理被 block 的 signal, 如果用 _setjmp/_longjmp 就不会处理 signal, 类似我用自己的 my_setjmp, 这时候就要自己 unblock SIGUSR1。 这边会遇到进阶的 signal 议题, 例如: signal handler 可以被中断吗? 在执行 signal hadnler 时, 如果有 2 个 signal 送过来, signale handler 会再次执行 2 次吗? 如果 对这些议题不熟也没关系, 以这个范例来说, SIGUSR1 signal handler 在执行的时候, 如果再次收到 SIGUSR1, 会等到原本的 SIGUSR1 signal handler 做完, 然後才会再次执行。 这是可以设定的, 那一种作法好呢? 我还没有答案。 而如果在执行 SIGUSR1 signal handler 期间收到 2 次以上的 SIGUSR1, 之後只会再执 行 SIGUSR1 signal handler 一次, 这样的行为让你有点担心吧, 这表示很有可能 func1, func2 的切换行为有可能会漏掉几次, 是的, 没办法, 传统 signal 就是这麽「不可靠」。 signal 相关问题可参考 - linux/unix signal 议题 疑! 刚刚不是说要用 SIGALRM, 怎麽变成 SIGUSR1, 因为後来发现用 SIGUSR1 比较好测 试, 就改用这个了。 程式在 setjmp func1, func2 之後, 会使用 longjmp 执行 func2, 再来就是透过 signal handler 来切换到 func1, 再来又透过 signal handler 再次切换到 func2, 依序下去。 simple_thread.c 1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <unistd.h> 4 #include <sys/time.h> 5 #include <signal.h> 7 #include "my_setjmp.h" 8 11 12 #define BUF_SIZE 32768 13 char func1_stack[BUF_SIZE+64]; 14 char func2_stack[BUF_SIZE+64]; 16 17 my_jmp_buf th1; 18 my_jmp_buf th2; 21 22 my_jmp_buf *cur_th; 23 my_jmp_buf *next_th; 24 25 26 void func1() 27 { 28 while(1) 29 { 30 printf("1"); 31 printf("2"); 32 printf("3"); 33 printf("4"); 34 printf("5"); 35 printf("6"); 36 printf("7"); 37 printf("8"); 38 printf("9"); 39 printf("a"); 40 printf("\n"); 41 } 42 } 43 void func2() 44 { 45 while(1) 46 { 47 printf("21 "); 48 printf("22 "); 49 printf("23 "); 50 printf("24 "); 51 printf("25 "); 52 printf("\n"); 53 } 54 } 55 57 void sigalrm_fn(int sig) 58 { 59 sigset_t sigs; 60 /* Unblock the SIGUSR1 signal that got us here */ 61 #if 1 62 sigemptyset (&sigs); 63 sigaddset (&sigs, SIGUSR1); 64 sigprocmask (SIG_UNBLOCK, &sigs, NULL); 65 #endif 66 printf("got USR1!\n"); 71 #if 1 72 if (cur_th == &th1) 73 { 74 printf("2\n"); 75 next_th = &th2; 76 } 77 else 78 { 79 printf("1\n"); 80 next_th = &th1; 81 } 82 #endif 83 86 if (my_setjmp(*cur_th) == 0) 87 { 88 cur_th = next_th; 91 my_longjmp(*next_th, 1); 92 } 93 else 94 { 95 return; 96 } 104 return; 105 } 106 107 int main(int argc, char *argv[]) 108 { 109 signal(SIGUSR1, sigalrm_fn); 110 my_setjmp(th1); 111 th1[0].eip = (unsigned long)func1; 112 th1[0].esp = (unsigned long)(func1_stack + BUF_SIZE); 113 114 if (my_setjmp(th2) == 0) 115 { 116 th2[0].eip = (unsigned long)func2; 117 th2[0].esp = (unsigned long)(func2_stack + BUF_SIZE); 118 cur_th = &th2; 119 my_longjmp(th2, 1); 120 } 131 132 while (1) 133 pause(); 181 return 0; 182 } func1 印出 123456789a, function 印出 21 22 23 24 25, 可以从以下影片看出, 当送 出 SIGUSR1, func1 和 func2 会相互切换, 基本上算是成功了。 当然离完成 pthread 这样的 library 还很远, 但至少迈出一小步了。 而我的「目的」当然也只是想知道 user mode thread library 是怎麽做的, 也不是想写出一个 pthread library, 有兴趣的朋友可以继续下去, 完成 ucsb 的作业。 可以用以下指令送出 SIGUSR1 killall -s SIGUSR1 simple_thread https://www.youtube.com/watch?time_continue=1&v=uFv_39Ys2vg&feature=emb_logo
整个程式从开始到完成期间: 20200220 ~ 20200226, 20200312 补上 x86_64 setjmp/ longjmp 的版本。 CS170 作业可不是只要求这样, 再来还需要有 atomic 的操作, 要写支援 mutex 这个作 业, 是不是又感觉害怕了。 这个作业都是基本中的基本, 但是基本问题可不等於简单问题, 这些观念过了在久, 都不 会改变的, 把心思花在上头并不会随着时间而白费。 soure code: https://github.com/descent/simple_thread user mode thread implementation: ‧ ftp://ftp.gnu.org/gnu/pth/pth-1.0.0.tar.gz ‧ ftp.ai.mit.edu/pub/rst/rsthreads.tgz ‧ https://stuff.mit.edu/afs/sipb/project/pthreads/stable/src/release/ pthreads-1_60_beta6.tar.gz blog 版本 descent-incoming.blogspot.com/2020/03/user-mode-pthread-simplethread.html -- 纸上得来终觉浅,绝知此事要躬行。 --



※ 发信站: 批踢踢实业坊(ptt.cc), 来自: 180.217.67.245 (台湾)
※ 文章网址: https://webptt.com/cn.aspx?n=bbs/C_and_CPP/M.1584751926.A.0D6.html
1F:推 LiloHuang: 感谢分享,顺便提一下signal safety 03/21 10:05
2F:→ LiloHuang: https://bit.ly/2wtVGDx 用 signal 时要留意一下就是 03/21 10:07
3F:→ descent: 感谢提醒, 要把 signal handler 写好, 真的难 03/21 10:35
4F:→ descent: stdio.h 的 function 几乎都不能用 03/21 10:35
5F:推 ggBird: 推 03/21 10:47
6F:推 ko27tye: 赞 03/21 11:52
7F:推 a58524andy: 推 03/21 12:20
8F:→ nevak: 感谢分享 03/21 13:58
9F:推 CoNsTaR: M$ 的系统也可以吗? 03/22 01:26
ms 若有类似 unix signal 的机制, 应该也办的到, 但 message 机制应该和 signal 不同。
10F:→ chuegou: 直觉先想到coroutine in c 03/22 03:42
11F:→ KILLE: 为何要自己实作setjmp/longjmp呢? https://bit.ly/3dmbkBm 03/22 10:42
12F:→ KILLE: 以上那代码目的与原po相同 但并无实作setjmp/longjmp 03/22 10:43
感谢分享, 你看他在指定 jmp_buf 的 ip, sp, 需要用 i64_ptr_mangle() 做个运算, 而不是直接指定, 这就是我不想用标准程式库 setjmp/longjmp 的原因。 我也和这位开发者类似, 用 c++ 写了 pthread 介面的版本。 https://github.com/descent/simple_thread/blob/master/cpp/simple_thread.cpp
13F:推 Caesar08: 推 03/22 14:50
14F:推 sarafciel: 好文章 推 03/23 10:53
15F:→ sunneo: 正想说之前才看过你的文章 :D 03/23 12:13
16F:推 KILLE: https://bit.ly/33CsN46 这讲setjmp/longjmp 也是讲自干协 03/23 12:32
17F:→ KILLE: 程 03/23 12:32
感谢分享, 你真会找资料。 ※ 编辑: descent (175.98.141.254 台湾), 03/23/2020 13:57:18
18F:→ KILLE: 做锁也未如难 只要有让路函数就可https://bit.ly/2WJkB0n 03/25 15:37
19F:→ KILLE: 还有 你讲圣塔巴巴拉很变态 个人非常不以为然 03/25 15:40
20F:→ KILLE: github上不少用户态之线程实作 查查都还算完整 03/25 15:41
21F:→ KILLE: CS170是2019春季作业 github上随手抓都四年前作品 03/25 15:42
22F:→ KILLE: https://bit.ly/39go3m6 https://bit.ly/2vShgRW两个就是 03/25 15:49
23F:→ KILLE: 交CS170-2作业之学生 并无实作锁功能 03/25 15:50
24F:→ KILLE: 这作业难度在於 在网上寻得并整理自己需要部份 03/25 15:51
25F:→ KILLE: 这年代要人白白写 无意义 也毋可能 03/25 15:52
26F:→ KILLE: github上查uthread/uthreads看是要协程还是跳转实作 03/25 15:58
27F:→ KILLE: 任君挑选 03/25 15:58
28F:推 ibmibmibm: 不要在signal handler呼叫非reentrant的function 04/12 23:38
29F:→ ibmibmibm: https://stackoverflow.com/questions/16891019/ 04/12 23:39







like.gif 您可能会有兴趣的文章
icon.png[问题/行为] 猫晚上进房间会不会有憋尿问题
icon.pngRe: [闲聊] 选了错误的女孩成为魔法少女 XDDDDDDDDDD
icon.png[正妹] 瑞典 一张
icon.png[心得] EMS高领长版毛衣.墨小楼MC1002
icon.png[分享] 丹龙隔热纸GE55+33+22
icon.png[问题] 清洗洗衣机
icon.png[寻物] 窗台下的空间
icon.png[闲聊] 双极の女神1 木魔爵
icon.png[售车] 新竹 1997 march 1297cc 白色 四门
icon.png[讨论] 能从照片感受到摄影者心情吗
icon.png[狂贺] 贺贺贺贺 贺!岛村卯月!总选举NO.1
icon.png[难过] 羡慕白皮肤的女生
icon.png阅读文章
icon.png[黑特]
icon.png[问题] SBK S1安装於安全帽位置
icon.png[分享] 旧woo100绝版开箱!!
icon.pngRe: [无言] 关於小包卫生纸
icon.png[开箱] E5-2683V3 RX480Strix 快睿C1 简单测试
icon.png[心得] 苍の海贼龙 地狱 执行者16PT
icon.png[售车] 1999年Virage iO 1.8EXi
icon.png[心得] 挑战33 LV10 狮子座pt solo
icon.png[闲聊] 手把手教你不被桶之新手主购教学
icon.png[分享] Civic Type R 量产版官方照无预警流出
icon.png[售车] Golf 4 2.0 银色 自排
icon.png[出售] Graco提篮汽座(有底座)2000元诚可议
icon.png[问题] 请问补牙材质掉了还能再补吗?(台中半年内
icon.png[问题] 44th 单曲 生写竟然都给重复的啊啊!
icon.png[心得] 华南红卡/icash 核卡
icon.png[问题] 拔牙矫正这样正常吗
icon.png[赠送] 老莫高业 初业 102年版
icon.png[情报] 三大行动支付 本季掀战火
icon.png[宝宝] 博客来Amos水蜡笔5/1特价五折
icon.pngRe: [心得] 新鲜人一些面试分享
icon.png[心得] 苍の海贼龙 地狱 麒麟25PT
icon.pngRe: [闲聊] (君の名は。雷慎入) 君名二创漫画翻译
icon.pngRe: [闲聊] OGN中场影片:失踪人口局 (英文字幕)
icon.png[问题] 台湾大哥大4G讯号差
icon.png[出售] [全国]全新千寻侘草LED灯, 水草

请输入看板名称,例如:BabyMother站内搜寻

TOP