挪用sleep后,我做了一个噩梦

sleep系统挪用

我是一个线程,生活在Linux帝国。一直以来辛勤事情,日子过得平平淡淡,可今天早上发生了一件事让我回想起来都后怕。

早上,我照样如往常一样执行着人类编写的代码指令,不多时走到了一个冷门的分支,一个sleep()函数挪用摆在了我的眼前。

终于可以去休息了!听老一辈的线程们说,执行了这个函数就可以休息休息了。我瞄了一眼参数,足足有5秒钟的休息时间,我简直乐坏了,没有犹豫,赶快执行了这个挪用。

挪用sleep后,我做了一个噩梦

进入sleep()函数后,又来到了nano_sleep()函数,接着看到了一个syscall系统挪用指令,我继续执行,来到了内核空间。

进入内核空间后,我接连穿过了

  • –> nano_sleep()
  • –> hrtimer_nanosleep()
  • –> do_nanosleep()
  • –> freezable_schedule()

把我累得够呛,说好让我休息,没想到休息之前另有这么多事要做。

终于,我来到了一个叫schedule()的函数眼前。

线程调剂

进入schedule()后,迎面走来一位发须皆已花白的父老。

挪用sleep后,我做了一个噩梦

“小伙子,这是要来休息了,我是卖力线程调剂的使者,让我看下你占用的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_class
  • dl_sched_class
  • rt_sched_class
  • fair_sched_class
  • idle_sched_class

“唉,玉人,那要是不相等该去哪个窗口解决呢?你告诉我一下,下次来我就知道了”

“不相等的话那就说明停当行列里除了通俗线程另有其他优先级更高的线程,就得根据优先级从stop_sched_class窗口挨个向后询问,直到找到一个线程。不外我在这干了这么久,就实时线程所在的rt_sched_class窗口和通俗线程所在的fair_sched_class最常用”,玉人耐心的给我注释到。

听了她的注释,我想到了一个问题:“那要是都找不到线程需要执行怎么办,好比他们都在守候IO事宜之类的?那我怎么交差啊”

“放心吧,最后谁人idle_sched_class窗口绝对不会让你空手而归的”,玉人笑着说。

原来如此,我点了颔首。

挪用sleep后,我做了一个噩梦

来到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

挪用sleep后,我做了一个噩梦

事情人员很快就取出了一个task_struct交给我,一个年纪轻轻的线程小T,我带着小T告辞了玉人接待,回到了schedule()

context_switch

看到我回来,父老起身言道:“小伙子,回来啦,走,带你们去context_switch()

进入这个context_switch()之后,父老又带着我又做了一些准备事情,好比把当前的历程地址空间换成了小T的,最终我们来到了一个叫switch_to的地方。

“小伙子,再往前走几步就是换班的地方了,就可以休息了,我就不送你了,谢谢你这段时间的辛苦事情”,父老一边说一边拍拍我的肩膀。

告辞了父老,我和小T踏上了这神秘的switch_to,跟随着一步一步的指令,我把自己线程上下文的寄存器都保留到了我的内核栈上面,然后将栈指针指向了小T的内核栈,最后把小T保留在他内核栈的指令地址加载进指令寄存器,终于完成了交接事情。

“小T,接下来就该你事情了,我要去休息了”,我和小T握手告辞,来到旁边准备眯一会儿。

挪用sleep后,我做了一个噩梦

神秘的叫醒

“醒醒,醒醒”,睡梦中听到有人唤我。

Laravel – 上手实现 – 增删改查

我揉揉睡眼,看了下时间,这才睡了2s,时间还没到,岂非现在是在做梦?

“总算把你叫醒了,快起来,换班时间到了,该你上了”,我抬起头才发现另外一个线程小H站在眼前。

“我休息时间还没够啊,怎么选中了我啊,让我再睡会儿”,说罢我就要躺下。

小H一把拉住了我,“别磨叽了,就是你,快走”。

挪用sleep后,我做了一个噩梦

在小H的率领下,我们又来到了谁人叫switch_to地方,只不外这一次我的角色变了。

小H一顿和我之前一样的操作,把执行流程交给了我。

我再次获得了执行代码的能力,随后回到了context_switch(),又回到了schedule(),看到了熟悉的父老。

我和父老再次告了别,继续返回,最后通过sysret虫洞,回到了用户态空间。

不外我马上意识到事情不对劲,这里并不是我最最先挪用sleep()的地方,而是一片完全生疏的区域,岂非那里出了问题,我这是到了那里?

挪用sleep后,我做了一个噩梦

这一定是在做梦,我还在sleep()呢,时间还没够,我只好这样抚慰自己。

我战战兢兢的执行了这里的代码,只是简朴输出了一行日志,然后来到了一个叫__restore_rt()的函数,又一条syscall指令摆在了我的眼前,我没有犹豫再一次一头扎进了内核空间。

经由一番折腾,又来到了sysret虫洞眼前,不知道这一次又将带我去到那里。我闭上了眼睛跳了进去···

挪用sleep后,我做了一个噩梦

等我睁开眼睛,竟然事业般的回到了最最先挪用sleep()的地方,这场梦终于醒了,庆幸我回到了这里。

我看了一眼sleep()的返回值,竟然是3。我又看了一眼日志文件,竟看到了梦里输出的那一行日志。

岂非那不是梦?这究竟是怎么一回事?

未完待续······

彩蛋

“新鲜,这个TCP数据包的ACK和SEQ怎么和适才谁人一样,估量是重传了吧”,帝国网络部的小Q丢掉了这个重复的数据包。

不外,同样的事情连续不断的泛起,履历了上次那件事的小Q不敢大意,赶快向平安部长汇报了情形。

预知后事若何,请关系后续精彩······

本文关联前作

内核地址空间大冒险1:系统挪用

内核地址空间大冒险2:中止与异常

内核地址空间大冒险3:权限治理

往期热门回首

震撼!全网第一张源码剖析全景图揭秘Nginx

一个整数+1引发的灾难

一网打尽!每个程序猿都该领会的黑客技术大汇总

看过无数Java GC文章,这5个问题你也未必知道!

一个Java工具的回忆录:垃圾接纳

谁动了你的HTTPS流量?

路由器里的广告隐秘

DDoS攻击:无限战争

一条SQL注入引出的惊天大案

一个HTTP数据包的奇幻之旅

一个DNS数据包的惊险之旅

我是一个流氓软件线程

扫码关注,更多精彩

挪用sleep后,我做了一个噩梦

原创文章,作者:28x29新闻网,如若转载,请注明出处:https://www.28x29.com/archives/5664.html