当前位置:网站首页>setcontext getcontext makecontext swapcontext
setcontext getcontext makecontext swapcontext
2022-04-23 15:05:00 【Mrpre】
Linux Context switching and collaboration
Context switch , Sounds ethereal , What is context , What does switching mean ? In fact, the context can be understood as the relevant register value of a process , That includes sp/bp/pc equivalence , let me put it another way , A context , It includes all the necessary things needed to restore the operation of the process . So called switching , That's a necessary function of a multi process operating system , One CPU Ability to run multiple processes ( look ), Then it is necessary to constantly switch between multiple processes ,A Switch to B when , It is necessary to preserve A Related information , Only in this way can we learn from B Switch back and run A, And operate correctly A.
But the above description is OS Things about , A process or a thread in the kernel is composed of struct task_struct Description of the ,OS The scheduling object of is task_struct. In the user mode, you want to realize similar functions , Then you must need the corresponding library function . According to the description above , We already know , To achieve the so-called scheduling , Then the relevant register information must be saved .
Let's take an example test1.c
#include <ucontext.h>
#include <stdio.h>
int done = 0;
int main()
{
ucontext_t context;
getcontext(&context);
if (done)
{
printf("return from getcontext,exit\n");
return 0;
}
done = 1;
setcontext(&context);
return 0;//never goto here!
}
The program calls getcontext Save the current register information to context in , And then execute setcontext, So-called setcontext Is to put context The register information in is restored to the current register information , in other words , Coercion context Of pc bp sp equivalence , Assigned to the current cpu In the deposit of , The obvious thing is , This jumps back to getcontext It's about .
Above this goto equally , The use goto That's it ? Let's look at the following example (test2.c):
#include <ucontext.h>
#include <stdio.h>
int done = 0;
int func1(ucontext_t *context)
{
done = 1;
setcontext(context);
}
int main()
{
ucontext_t context;
getcontext(&context);
if (done)
{
printf("return from getcontext,exit\n");
return 0;
}
func1(&context);
return 0;
}
goto You can't jump between functions , Can only do local jump . Of course getcontet and setcontext It's definitely not just these functions , Here's an example .
//test3.c
#include <ucontext.h>
#include <stdio.h>
#include <malloc.h>
void func()
{
printf("in func\n");
}
int main()
{
ucontext_t context;
getcontext(&context);
// Specified stack
context.uc_stack.ss_sp = malloc(10000);
context.uc_stack.ss_size = 10000;
context.uc_link = NULL;
makecontext(&context, func, 0);
setcontext(&context);
return 0;//never goto here!
}
First setcontext initialization context, then makecontext, Specify the function to jump , And then again setcontext Switch context To func function .
besides , Also for the new context Specified a new stack , Why ? Because if you don't specify a stack , So the stack is still getcontext When you get sp bp The pointer ,sp bp Describe the main Stack size of function , If main The function stack size is 100 byte , But what you have to do func The stack size needs to be 1000 byte , Obviously not enough .
See this , A lot of people must be confused , And if that's done func, Will return to main Do you ? The answer is No . Why not ? Don't func No man It's called ? Why not return to main Well ? Now let's talk about this problem , But before we talk about this , I hope you can find out by yourself x86、x64 Call rules for .
We know one main As a function , It must have been called elsewhere .
Let's write a simple one main function , Then stop , Push stack , The caller is __libc_start_main:
Breakpoint 3, 0x00000000004004d8 in main () at main.c:7
7 }
(gdb) disassemble
Dump of assembler code for function main:
0x00000000004004c4 <+0>: push %rbp
0x00000000004004c5 <+1>: mov %rsp,%rbp
0x00000000004004c8 <+4>: mov $0xa,%edi
0x00000000004004cd <+9>: callq 0x4003b8 <putchar@plt>
0x00000000004004d2 <+14>: mov $0x0,%eax
0x00000000004004d7 <+19>: leaveq
=> 0x00000000004004d8 <+20>: retq
End of assembler dump.
(gdb) i r
rax 0x0 0
rbx 0x0 0
rcx 0xffffffff 4294967295
rdx 0x7ffff77d1e10 140737345560080
rsi 0x7ffff7ff7000 140737354100736
rdi 0x0 0
rbp 0xcc 0xcc
rsp 0x7fffffffe528 0x7fffffffe528
r8 0xffffffff 4294967295
r9 0xa 10
r10 0xffffffff 4294967295
r11 0x246 582
r12 0x4003e0 4195296
r13 0x7fffffffe600 140737488348672
r14 0x0 0
r15 0x0 0
rip 0x4004d8 0x4004d8 <main+20>
eflags 0x246 [ PF ZF IF ]
cs 0x33 51
ss 0x2b 43
ds 0x0 0
es 0x0 0
fs 0x0 0
gs 0x0 0
(gdb) x/40xg 0x7fffffffe528
0x7fffffffe528: 0x00007ffff7460d5d 0x0000000000000000
0x7fffffffe538: 0x00007fffffffe608 0x0000000100000000
0x7fffffffe548: 0x00000000004004c4 0x0000000000000000
0x7fffffffe558: 0x6fcc4be0773d5f7c 0x00000000004003e0
0x7fffffffe568: 0x00007fffffffe600 0x0000000000000000
0x7fffffffe578: 0x0000000000000000 0x9033b41fbd5d5f7c
0x7fffffffe588: 0x9033a56c6d0d5f7c 0x00007fff00000000
0x7fffffffe598: 0x0000000000000000 0x0000000000000000
0x7fffffffe5a8: 0x00000000004004f0 0x00007fffffffe608
0x7fffffffe5b8: 0x0000000000000001 0x0000000000000000
0x7fffffffe5c8: 0x0000000000000000 0x00000000004003e0
0x7fffffffe5d8: 0x00007fffffffe600 0x0000000000000000
0x7fffffffe5e8: 0x0000000000400409 0x00007fffffffe5f8
0x7fffffffe5f8: 0x000000000000001c 0x0000000000000001
0x7fffffffe608: 0x00007fffffffe805 0x0000000000000000
0x7fffffffe618: 0x00007fffffffe822 0x00007fffffffe83d
0x7fffffffe628: 0x00007fffffffe84d 0x00007fffffffe861
0x7fffffffe638: 0x00007fffffffe872 0x00007fffffffee7f
0x7fffffffe648: 0x00007fffffffee95 0x00007fffffffeea6
0x7fffffffe658: 0x00007fffffffeebb 0x00007fffffffeec7
(gdb) info symbol 0x00007ffff7460d5d
__libc_start_main + 253 in section .text of /lib64/libc.so.6
main When the function returns , Yes 0x00007ffff7460d5d, Also called directly exit, The end of the process .
0x00007ffff7460d5d:
0x00007ffff7460d56 <+246>: mov (%rax),%rdx
0x00007ffff7460d59 <+249>: callq *0x18(%rsp)
0x00007ffff7460d5d <+253>: mov %eax,%edi
0x00007ffff7460d5f <+255>: callq 0x7ffff7477a40 <exit>
Said so much , Then we test3.c Of func Who called ? If you simply want to
Its call stack must be
main->setcontext->func , But I said above , It's not like that .
We are func Excuse me, have a look :
Breakpoint 1, func () at test3.c:6
6 printf("in func\n");
Missing separate debuginfos, use: debuginfo-install glibc-2.12-1.166.alios6.7.x86_64
(gdb) bt
#0 func () at test3.c:6
#1 0x00007ffff74858f0 in ?? () from /lib64/libc.so.6
#2 0x0000000000000000 in ?? ()
(gdb) disassemble
Dump of assembler code for function func:
0x00000000004005f4 <+0>: push %rbp
0x00000000004005f5 <+1>: mov %rsp,%rbp
=> 0x00000000004005f8 <+4>: mov $0x400778,%edi
0x00000000004005fd <+9>: callq 0x4004a8 <puts@plt>
0x0000000000400602 <+14>: leaveq
0x0000000000400603 <+15>: retq
End of assembler dump.
(gdb) i r
rax 0x0 0
rbx 0x603710 6305552
rcx 0x0 0
rdx 0x7fffffffe618 140737488348696
rsi 0x7fffffffe608 140737488348680
rdi 0x7fffffffe170 140737488347504
rbp 0x603700 0x603700
rsp 0x603700 0x603700
r8 0x7ffff77d1300 140737345557248
r9 0x7ffff7debac0 140737351957184
r10 0x8 8
r11 0x246 582
r12 0x400510 4195600
r13 0x7fffffffe600 140737488348672
r14 0x0 0
r15 0x0 0
rip 0x4005f8 0x4005f8 <func+4>
eflags 0x246 [ PF ZF IF ]
cs 0x33 51
ss 0x2b 43
ds 0x0 0
es 0x0 0
fs 0x0 0
gs 0x0 0
(gdb) x/40xg 0x603700
0x603700: 0x00007fffffffe520 0x00007ffff74858f0
0x603710: 0x0000000000000000 0x0000000000000000
0x603720: 0x0000000000000000 0x00000000000208e1
0x603730: 0x0000000000000000 0x0000000000000000
0x603740: 0x0000000000000000 0x0000000000000000
0x603750: 0x0000000000000000 0x0000000000000000
0x603760: 0x0000000000000000 0x0000000000000000
0x603770: 0x0000000000000000 0x0000000000000000
0x603780: 0x0000000000000000 0x0000000000000000
0x603790: 0x0000000000000000 0x0000000000000000
0x6037a0: 0x0000000000000000 0x0000000000000000
0x6037b0: 0x0000000000000000 0x0000000000000000
0x6037c0: 0x0000000000000000 0x0000000000000000
0x6037d0: 0x0000000000000000 0x0000000000000000
0x6037e0: 0x0000000000000000 0x0000000000000000
0x6037f0: 0x0000000000000000 0x0000000000000000
0x603800: 0x0000000000000000 0x0000000000000000
0x603810: 0x0000000000000000 0x0000000000000000
0x603820: 0x0000000000000000 0x0000000000000000
0x603830: 0x0000000000000000 0x0000000000000000
(gdb) info symbol 0x00007ffff74858f0
__start_context in section .text of /lib64/libc.so.6
func The stack address of is 0x603700 He's ours main function malloc Got Address + Stack size To get the top of the stack ( There is a certain offset ).
Now comes the question ,func By __start_context Called , And from x/40xg 0x603700 The instructions show that , except __start_context, The rest of the stack is 0, Which means no one calls __start_context. It also means not from main->setcontext … Call all the way to func. It's strange to put , But think about it , So called stack , That's memory , What's in the memory , Someone must have put it .
In fact, I can guess __start_context Is deliberately arranged in the stack . That is, deliberately arranged in 0x603700 Medium , So that func perform retq return , Read the pc Pointer is __start_context.
ss_sp Is that we are main Function malloc The address of ,sp = ss_sp + ss_size It points to the top of the stack .
We are sp[0] Arranged an address &__start_context, and func This is the function stack of sp,func At the end of the execution ret when , Meeting pop This sp[0], And put it in your own pc On the pointer , Then jump to pc It's about , in other words ,func The return value of __start_context.
good , Now look at __start_context What did you do , I thought that from the result of pushing the stack, no one called __start_context, So I guess it must directly call exit 了 .
(gdb) disassemble __start_context
Dump of assembler code for function __start_context:
0x00007ffff74858f0 <+0>: mov %rbx,%rsp
0x00007ffff74858f3 <+3>: pop %rdi
0x00007ffff74858f4 <+4>: test %rdi,%rdi
0x00007ffff74858f7 <+7>: je 0x7ffff74858fe <__start_context+14>
0x00007ffff74858f9 <+9>: callq 0x7ffff7483090 <setcontext>
0x00007ffff74858fe <+14>: mov %rax,%rdi
0x00007ffff7485901 <+17>: callq 0x7ffff7477a40 <exit>
0x00007ffff7485906 <+22>: hlt
End of assembler dump.
func Back to __start_context after , Just exit.
To sum up , In execution func when , At the top of the stack is a __start_context, such func perform ret when , That's it __start_context. let me put it another way , Not at all __start_context Called func, It is func Back to the __start_context.
If you look carefully, __start_context Function assembly , One possibility for him is not to carry out exit, But to perform setcontext, The judgment condition is %rdi Whether it is 0, No 0 Point to setcontext 了 , What logic is this . Let's see test4.c
#include <ucontext.h>
#include <stdio.h>
#include <malloc.h>
int did = 0;
void func()
{
did = 1;
printf("in func\n");
}
int main()
{
ucontext_t context,rt;
getcontext(&context);
getcontext(&rt);
if(did == 1)
{
printf("continue from func\n");
return 0;
}
// Specified stack
context.uc_stack.ss_sp = malloc(10000);
context.uc_stack.ss_size = 10000;
context.uc_link = &rt;
makecontext(&context, func, 0);
setcontext(&context);
return 0;//never goto here!
}
test4.c Than test3.c Just one more pair uc_link Assignment ( There are also some process control variables ).uc_link It's designated func After execution , Then the context of execution , If it is empty , Then the execution is finished func after ,exit, It's like test3.c equally ; If uc_link Not empty , Then the execution is finished func after , Then perform uc_link In the specified context .
as __start_context Logic is consistent .
Next , see test4.c, We from getcontext(&rt); I went back to , But can we start from setcontext Where to return , Certainly. , Method 1 Namely getcontext(&rt); After the return ,goto To setcontext Back . however glibc It also provides an interface, which is swapcontext. Let's look at the following example .
#include <ucontext.h>
#include <stdio.h>
#include <malloc.h>
int did = 0;
void func()
{
did = 1;
printf("in func\n");
}
int main()
{
ucontext_t context,rt;
getcontext(&context);
// Specified stack
context.uc_stack.ss_sp = malloc(10000);
context.uc_stack.ss_size = 10000;
context.uc_link = &rt;
makecontext(&context, func, 0);
swapcontext(&rt,&context);
printf("finally return\n");
return 0;//should goto here!
}
swapcontext Two things , One is to save the current context , To rt in , And then switch to context.context After the execution of the specified context is completed , And then you jump to swapcontext Behind the .
Be careful : these xxxcontext The function gives the user state program “ Scheduling function ”, This sum os The scheduling is different ,os For each thread , namely task_struct Switch scheduling , And inside each thread , That is, user state programs , The above library functions can be used for scheduling with smaller granularity ( Say switching is more reasonable ). This is the so-called concept of CO process .
版权声明
本文为[Mrpre]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/04/202204231409587460.html
边栏推荐
- How to use OCR in 5 minutes
- Leetcode exercise - 396 Rotation function
- Nuxt project: Global get process Env information
- Fill in the next right node pointer II of each node [classical hierarchy traversal | regarded as linked list]
- 8.4 realization of recurrent neural network from zero
- OPPO数据湖统一存储技术实践
- JUC learning record (2022.4.22)
- Epolloneshot event of epoll -- instance program
- Detailed explanation of C language knowledge points -- data types and variables [1] - carry counting system
- 如何打开Win10启动文件夹?
猜你喜欢
8.5 concise implementation of cyclic neural network
[NLP] HMM hidden Markov + Viterbi word segmentation
Swift protocol Association object resource name management multithreading GCD delay once
Advanced version of array simulation queue - ring queue (real queuing)
Nuxt project: Global get process Env information
Programming philosophy - automatic loading, dependency injection and control inversion
Swift: entry of program, swift calls OC@_ silgen_ Name, OC calls swift, dynamic, string, substring
我的树莓派 Raspberry Pi Zero 2W 折腾笔记,记录一些遇到的问题和解决办法
Provided by Chengdu control panel design_ It's detailed_ Introduction to the definition, compilation and quotation of single chip microcomputer program header file
Lotus DB design and Implementation - 1 Basic Concepts
随机推荐
Leetcode167 - sum of two numbers II - double pointer - bisection - array - Search
How to use OCR in 5 minutes
Share 20 tips for ES6 that should not be missed
帧同步 实现
JS -- realize click Copy function
8.2 text preprocessing
LeetCode 练习——396. 旋转函数
封面和标题中的关键词怎么写?做自媒体为什么视频没有播放量
Role of asemi rectifier module mdq100-16 in intelligent switching power supply
Leetcode165 compare version number double pointer string
Vous ne connaissez pas encore les scénarios d'utilisation du modèle de chaîne de responsabilité?
Detailed analysis of SQL combat of Niuke database (26-30)
Async void caused the program to crash
select 同时接收普通数据 和 带外数据
Leetcode149 - maximum number of points on a line - Math - hash table
like和regexp差别
1 - first knowledge of go language
Select receives both normal data and out of band data
Leetcode153 - find the minimum value in the rotation sort array - array - binary search
填充每个节点的下一个右侧节点指针 II [经典层次遍历 | 视为链表 ]