调用sleep后,我做了一个噩梦
sleep系统调用
我是一个线程,生活在Linux帝国。一直以来辛勤工作,日子过得平平淡淡,可今天早上发生了一件事让我回想起来都后怕。
早上,我还是如往常一样执行着人类编写的代码指令,不多时走到了一个冷门的分支,一个sleep()函数调用摆在了我的面前。
终于可以去休息了!听老一辈的线程们说,执行了这个函数就可以休息休息了。我瞄了一眼参数,足足有5秒钟的休息时间,我简直乐坏了,没有犹豫,赶紧执行了这个调用。

进入sleep()函数后,又来到了nano_sleep()函数,接着看到了一个syscall系统调用指令,我继续执行,来到了内核空间。
进入内核空间后,我接连穿过了
- --> nano_sleep()
- --> hrtimer_nanosleep()
- --> do_nanosleep()
- --> freezable_schedule()
把我累得够呛,说好让我休息,没想到休息之前还有这么多事要做。
终于,我来到了一个叫schedule()的函数面前。
线程调度
进入schedule()后,迎面走来一位发须皆已花白的长者。

“小伙子,这是要来休息了,我是负责线程调度的使者,让我看下你占用的CPU号码”,一边说一边查找着什么。
“哦,是2号CPU,来,跟我到这边来”,在他的指引下,我又来到了一个函数面前。
“你先去pick_next_task()找到一个接盘侠,哦不,找到下一个需要执行的线程,这是2号CPU的就绪队列,你可拿好了,等你办完回来我再带你去办理交接手续”,说完给我手里塞了一个参数rq,随即便离开了,留下我不知所措。
我只好按他说的照办,迈进了pick_next_task()函数的大门,一位美女接待映入眼帘。
“先生您好,您来此想必是要寻找接班线程吧”,见我到来,美女起身招呼。
“你猜的不错,要麻烦你帮我处理一下,多谢了”
“您别客气,把就绪队列给我看看吧”
我先是愣了一下,反应过来后将手里的rq参数给了她。
struct rq {
raw_spinlock_t lock;
...
unsigned int nr_running;
...
struct cfs_rq cfs;
struct rt_rq rt;
struct dl_rq dl;
...
struct task_struct *curr, *idle, *stop;
...
struct mm_struct *prev_mm;
...
struct list_head cfs_tasks;
};
美女拿着rq一阵端详,说到:“您运气不错哦,rq->nr_running和rq->cfs.h_nr_running相等,看来没有实时线程,全是普通线程,您直接去那边的公平调度CFS窗口fair_sched_class那里去办理吧。”
我顺着美女指向的方向看去,那边一共有5个窗口:
stop_sched_classdl_sched_classrt_sched_classfair_sched_classidle_sched_class
“唉,美女,那要是不相等该去哪个窗口办理呢?你告诉我一下,下次来我就知道了”
“不相等的话那就说明就绪队列里除了普通线程还有其他优先级更高的线程,就得按照优先级从stop_sched_class窗口挨个向后询问,直到找到一个线程。不过我在这干了这么久,就实时线程所在的rt_sched_class窗口和普通线程所在的fair_sched_class最常用”,美女耐心的给我解释到。
听了她的解释,我想到了一个问题:“那要是都找不到线程需要执行怎么办,比如他们都在等待IO事件之类的?那我怎么交差啊”
“放心吧,最后那个idle_sched_class窗口绝对不会让你空手而归的”,美女笑着说。
原来如此,我点了点头。

来到fair_sched_class窗口的旁边,递交了我的rq参数,只见工作人员取出了其中的cfs_rq:
struct cfs_rq {
struct load_weight load;
unsigned int nr_running, h_nr_running;
...
struct rb_root tasks_timeline;
struct rb_node *rb_leftmost;
...
struct sched_entity *curr, *next, *last, *skip;
...
struct rq *rq;
};
我这才注意到,原来这个cfs_rq中指向了一棵红黑树,再仔细一看,这树上的每个节点都是一个线程task_struct。

工作人员很快就取出了一个task_struct交给我,一个年纪轻轻的线程小T,我带着小T告别了美女接待,回到了schedule()。
context_switch
看到我回来,长者起身言道:“小伙子,回来啦,走,带你们去context_switch()”
进入这个context_switch()之后,长者又带着我又做了一些准备工作,比如把当前的进程地址空间换成了小T的,最终我们来到了一个叫switch_to的地方。
“小伙子,再往前走几步就是换班的地方了,就可以休息了,我就不送你了,感谢你这段时间的辛苦工作”,长者一边说一边拍拍我的肩膀。
告别了长者,我和小T踏上了这神秘的switch_to,跟随着一步一步的指令,我把自己线程上下文的寄存器都保存到了我的内核栈上面,然后将栈指针指向了小T的内核栈,最后把小T保存在他内核栈的指令地址加载进指令寄存器,终于完成了交接工作。
“小T,接下来就该你工作了,我要去休息了”,我和小T握手告别,来到旁边准备眯一会儿。

神秘的唤醒
“醒醒,醒醒”,睡梦中听到有人唤我。
我揉揉睡眼,看了下时间,这才睡了2s,时间还没到,难道现在是在做梦?
“总算把你叫醒了,快起来,换班时间到了,该你上了”,我抬起头才发现另外一个线程小H站在面前。
“我休息时间还没够啊,怎么选中了我啊,让我再睡会儿”,说罢我就要躺下。
小H一把拉住了我,“别磨叽了,就是你,快走”。

在小H的带领下,我们又来到了那个叫switch_to地方,只不过这一次我的角色变了。
小H一顿和我之前一样的操作,把执行流程交给了我。
我再次获得了执行代码的能力,随后回到了context_switch(),又回到了schedule(),看到了熟悉的长者。
我和长者再次告了别,继续返回,最后通过sysret虫洞,回到了用户态空间。
不过我马上意识到事情不对劲,这里并不是我最开始调用sleep()的地方,而是一片完全陌生的区域,难道哪里出了问题,我这是到了哪里?

这一定是在做梦,我还在sleep()呢,时间还没够,我只好这样安慰自己。
我小心翼翼的执行了这里的代码,只是简单输出了一行日志,然后来到了一个叫__restore_rt()的函数,又一条syscall指令摆在了我的面前,我没有犹豫再一次一头扎进了内核空间。
经过一番折腾,又来到了sysret虫洞面前,不知道这一次又将带我去到哪里。我闭上了眼睛跳了进去···

等我睁开眼睛,竟然奇迹般的回到了最开始调用sleep()的地方,这场梦终于醒了,庆幸我回到了这里。
我看了一眼sleep()的返回值,竟然是3。我又看了一眼日志文件,竟看到了梦里输出的那一行日志。
难道那不是梦?这究竟是怎么一回事?
未完待续······
彩蛋
“奇怪,这个TCP数据包的ACK和SEQ怎么和刚才那个一样,估计是重传了吧”,帝国网络部的小Q丢掉了这个重复的数据包。
不过,同样的事情接二连三的出现,经历了上次那件事的小Q不敢大意,赶紧向安全部长汇报了情况。
预知后事如何,请关系后续精彩······
本文关联前作
往期热门回顾
扫码关注,更多精彩

调用sleep后,我做了一个噩梦的更多相关文章
- 自己做了一个json格式化工具,亲测可以使用
随笔背景:在向后台请求数据之后,我们常常会拿到一串json格式.此时,为了方便查看key-value,程序猿们常常使用一些在线json格式化工具或者是类似于notepadd++这样的工具进行转换.今天 ...
- HeadFirstPython学习笔记——OSError: [WinError 10013] 以一种访问权限不允许的方式做了一个访问套接字的尝试。
1.文件构成如下 2.运行服务器时报错 OSError: [WinError 10013] 以一种访问权限不允许的方式做了一个访问套接字的尝试. 解决方法:更换端口 3.Python的CGI跟踪术 在 ...
- ”危险“的RESTRICT与GCC的编译优化(编程者对编译器所做的一个“承诺”:使用restrict修饰过的指针,它所指向的内容只能经由该指针修改)
restrict是C99标准中新添加的关键字,对于从C89标准开始起步学习C语言的同学来说(包括我),第一次看到restrict还是相当陌生的.Wikipedia给出的解释如下: In the C p ...
- 由STL map调用clear后,内存不返还给操作系统的问题出发,探讨glibc malloc/free行为(转)
1. 问题 我们的程序有几十个线程,每个线程拥有一个std::map,每个线程都要向自己的std::map中插入大量的数据,但每个数据只有几十字节:当使用完std::map,调用map.clear() ...
- 分享自己做的一个指定进程以及线程长时间cpu监控的工具
前言: 前面给大家分享过一个工作中用到的编译拷贝脚本,其实工作中还有一些其他工具的使用,今天再来分享一个自己纯手工的CPU监控的脚本.大家可以结合上篇文章与本篇文章一起学习shell. 主要实现功能: ...
- 用js给闺女做了一个加减乘除的html
下班回家用二十分钟给闺女做了一个加减乘除的页面,顺便记录下代码,时间仓促,后期再来修改吧 目录结构 -yq --menu.html --yq.html --yq50.html --yq70.html ...
- 做为一个前端工程师,是往node方面转,还是往HTML5方面转
文章背景:问题本身来自于知乎,但是我感觉这个问题很典型,有必要把问题在整理一下,重新分享出来. 当看到这个问题之前,我也碰到过很多有同样疑惑的同学,他们都有一个共同的疑问该学php还是nodejs,包 ...
- 做了一个简易的git 代码自动部署脚本
做了一个简易的git 代码自动部署脚本 http://my.oschina.net/caomenglong/blog/472665 发表于2个月前(2015-06-30 21:08) 阅读(200 ...
- 安装Ubuntu 14.04后要做的5件事情
转自安装Ubuntu 14.04后要做的5件事情 Ubuntu目前是世界上最流行的Linux操作系统,它提供了桌面版本和服务器版本,其他流行的Linux发行版本如Linux Mint也是基于Ubunt ...
随机推荐
- 使用Python创建自己的Instagram滤镜
不知道你有没有使用过Instagram滤镜,它们非常方便,只需单击几个按钮,就可以变换我要发布的照片. 你是否想过自己可以创建一个?答案是可以的! 在本文中,我将向你展示如何使用代码和示例图像来创 ...
- nim博弈 LightOJ - 1253
主要是写一下nim博弈的理解,这个题有点奇怪,不知道为什么判断奇偶性,如果有大佬知道还请讲解一下. //nim博弈 //a[0]~a[i] 异或结果为k 若k=0 则为平衡态 否则为非平衡态 //平衡 ...
- nginx负载均衡例子
upstream demo { ip_hash;//客户连接后, 一直用这个IP,直到会话结束,否则,动态程序可能会在换IP后出错 server 192.168.1.1:80 weight=5 | d ...
- Polya 定理相关题目
参考知识链接 关于枚举旋转置换: 前两题都是枚举了 n 种旋转, 但这个可以优化到\(O(\sqrt{n})\) (这个其实是基本操作). 考虑到每个循环节的长度都是 n 的因数, 所以可以枚 ...
- 【cs224w】Lecture 3 - Motif, Graphlet 及 结构性角色
目录 Network Motifs Configuration Model Graphlets How to Find Motifs and Graphlets Structural Roles 转自 ...
- 实验十--- MySQL过程式数据库对象
实验十 MySQL过程式数据库对象 一. 实验内容: 1. 存储过程的创建和调用 2. 存储函数的创建和调用 3. 触发器的创建和触发 4. 事件的创建和修改 一. 实验项目:员工管理数据库 用于 ...
- 感染(low)bfs 、感染(mid) 二分、感染(high) 二分 + 维护单调 队列去除无用的点
感染(low) Description n户人家住在一条直线上,从左往右依次编号为1,2,-,n.起初,有m户人家感染了COVID-19,而接下来的每天感染的人家都会感染他家左右两家的人,问t天后总共 ...
- 1014 Waiting in Line (30 分)
Suppose a bank has N windows open for service. There is a yellow line in front of the windows which ...
- PTA数据结构与算法题目集(中文) 7-3
PTA数据结构与算法题目集(中文) 7-3 树的同构 给定两棵树T1和T2.如果T1可以通过若干次左右孩子互换就变成T2,则我们称两棵树是“同构”的.例如图1给出的两棵树就是同构的,因为我们把其中一 ...
- PTA | 1010 一元多项式求导 (25分)
设计函数求一元多项式的导数.(注:xn(n为整数)的一阶导数为n*xn-1.) 输入格式: 以指数递降方式输入多项式非零项系数和指数(绝对值均为不超过1000的整数).数字间以空格分隔. 输出格式: ...