JIT是什么,它将怎样运行?

JIT是什么,它将怎样运⾏?
什么是 JIT?
名如其特点,JIT —— just in time,即时编译。
把它详细化点讲,就是
⼀个程序在它运⾏的时候创建并且运⾏了全新的代码,⽽并⾮那些最初作为这个程序的⼀部分保存在硬盘上的固有的代码。就叫 JIT。
这⾥有⼏点要看的:
程序需要运⾏
eps复合保温板⽣成的代码是新的代码,并⾮作为原始程序的⼀部分被存在磁盘上的那些代码
不光⽣成代码,还要运⾏。
需要提醒的是第三点,也就是 JIT不光是⽣成新的代码,它还会运⾏新⽣成的代码,⽽这些代码在存储于磁盘上时不属于该程序的⼀部分,它就是⼀个JIT。
JIT的两个阶段
我把JIT分为了两个阶段
阶段1:在程序运⾏时创建机器代码。
阶段2:在程序运⾏时也执⾏该机器代码。
第1阶段是JITing 99%的挑战所在,但它也是这个过程中不那么神秘的部分,因为这正是编译器所做的。众所周知的编译器,如gcc和clang,将C/C++源代码转换为机器代码。机器代码被发送到输出流中,但它很可能只保存在内存中(实际上,gcc和clang / llvm都有构建块⽤于将代码保存在内存中以便执⾏JIT)。第2阶段,看下去 ::twemoji :
模拟⼀下JIT运⾏的过程
现代操作系统对于允许程序在运⾏时执⾏的操作可以说是⾮常挑剔。过去“海阔凭鱼跃,天⾼任鸟飞”的⽇⼦随着保护模式的出现⽽不复存在,保护模式允许操作系统以各种权限对虚拟内存块的使⽤做出限制。因此,在“普通”代码中,你可以在堆上动态创建新数据,但是你不能在没有操作系统明确允许的情况下从堆中运⾏其内容。
在这⼀点上,我希望机器代码只是数据 - ⼀个字节流,⽐如:
unsigned char[] code = {0x48, 0x89, 0xf8};
不同的⼈会有不同的视⾓,对某些⼈⽽⾔,0x48, 0x89, 0xf8只是⼀些可以代表任何事物的数据。对于其他⼈来说,它是真实有效的机器代码的⼆进制编码,其对应的x86-64汇编代码如下:
mov %rdi, %rax
其实可以看出机器码就是⽐特流,所以将它加载进内存并不困难。⽽问题是应该如何执⾏。
好啦。下⾯我们就模拟⼀下执⾏新⽣成的机器码的过程。假设JIT已经为我们编译出了新的机器码,是⼀个求和函数的机器码:石墨烯地暖瓷砖
//求和函数
long add4(long num) {
return num + 4;
}
//对应的机器码
0x48, 0x83, 0xc0, 0x01, 0xc3
⾸先,动态的在内存上创建函数之前,我们需要在内存上分配空间。具体到模拟动态创建函数,其实就是将对应的机器码映射到内存空间中。这⾥我们使⽤c语⾔做实验,利⽤ mmap 函数来实现这⼀点。 ::twemoji :
所以,我们就需要这些:
//头⽂件
#include <unistd.h>
#include <sys/mman.h>
//定义函数羟乙基纤维素钠
void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offsize)
/*函数说明:
mmap()⽤来将某个⽂件内容映射到内存中,对该内存区域的存取即是直接对该⽂件内容的读写。*/
因为我们想要把已经是⽐特流的“求和函数”在内存中创建出来,同时还要运⾏它。所以mmap有⼏个参数需要注意⼀下。
⽽代表映射区域的保护⽅式,有下列组合:
PROT_EXEC //映射区域可被执⾏;
PROT_READ //映射区域可被读取;
PROT_WRITE //映射区域可被写⼊;
所以,我们的程序可以像是这个样⼦:
#include<stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/mman.h>
//分配内存
void* create_space(size_t size) {
反猫眼窥镜
void* ptr = mmap(0, size,
PROT_READ | PROT_WRITE | PROT_EXEC,
MAP_PRIVATE | MAP_ANON,
-1, 0);
return ptr;
}
通过这⼀段代码我们可以获得⼀块分配给我们存放代码的空间。下⼀步就是实现⼀个⽅法将机器码拷贝到分配给我们的那块空间上去。使⽤函数 memcpy 即可。
//在内存中创建函数
void copy_code_2_space(unsigned char* m) {
unsigned char macCode[] = {
0x48, 0x83, 0xc0, 0x01,
c3
};
memcpy(m, macCode, sizeof(macCode));
}
我们再整理⼀下,最后程序就变成这个样⼦
#include<stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/mman.h>
//分配内存
void* create_space(size_t size) {
void* ptr = mmap(0, size,
PROT_READ | PROT_WRITE | PROT_EXEC,
MAP_PRIVATE | MAP_ANON,
-1, 0);
return ptr;
}
//在内存中创建函数
void copy_code_2_space(unsigned char* addr) {
unsigned char macCode[] = {
0x48, 0x83, 0xc0, 0x01,
0xc3
};
opnetmemcpy(addr, macCode, sizeof(macCode));
}
//main 声明⼀个函数指针TestFun⽤来指向我们的求和函数在内存中的地址
int main(int argc, char** argv) {
const size_t SIZE = 1024;
typedef long (*TestFun)(long);
void* addr = create_space(SIZE);
copy_code_2_space(addr);
TestFun test = addr;
int result = test(1);
printf("result = %d\n", result);
return 0;
}
编译
我们通过
gcc a.c
./a.out 1蛇形线
我们可以得到
result=2
所以这就是JIT在编译的作⽤以及最后的结果了

本文发布于:2024-09-23 04:26:23,感谢您对本站的认可!

本文链接:https://www.17tex.com/tex/3/105371.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:代码   内存   函数   程序   机器   映射   机器码   创建
留言与评论(共有 0 条评论)
   
验证码:
Copyright ©2019-2024 Comsenz Inc.Powered by © 易纺专利技术学习网 豫ICP备2022007602号 豫公网安备41160202000603 站长QQ:729038198 关于我们 投诉建议