尝试写一个jvm吧(二)

今天清明节,刚好放假更新第二章,就是读取字节码文件。 一个更舒适的UI:https://www.mdnice.com/writing/d1c0dc8c7bbc4ef3bf5f5a2ae3e10f97 前面两节都比较简单,分别是命令行操作和读取class文件,从第三节开始估计进度会慢一些和复杂一点点,要开始写运行时和解析class文件了~

首先,我们还是通过命令行来进行操作,所以首先在command.rs文件中加入以下指令

command结构体

#[derive(Debug)]
pub struct Command {
    pub help_flag: bool,
    pub version_flag: bool,
    pub info_flag: bool,
    pub cp_option: String,
    pub x_jre_option: String,
    pub class: String,
    pub args: Vec<String>,
}

命令行操作命令

opts.optopt("", "classpath", "Specify the classpath", "classpath");
opts.optopt("", "cp", "Specify the classpath", "classpath");
opts.optopt("", "Xjre", "Path to jre", "jre");

对命令进行解析

match matches.opt_str("classpath") {
    Some(classpath) => {
        command.cp_option = classpath;
    },
    None => {
        match matches.opt_str("cp") {
            Some(cp) => {
                command.cp_option = cp;
            },
            None => {}
        }
    }
}

match matches.opt_str("Xjre") {
    Some(x_jre_option) => {
        command.x_jre_option = x_jre_option;
    },
    None => {}
}

// 未定义的参数放在 free Vec 中
if !matches.free.is_empty() {
    command.class = matches.free[0].clone();
    command.args = matches.free[1..].to_vec();
}

上面就是命令行解析的内容了,命令行解析完后,我们要通过读取的命令解析执行对应的步骤,比如我输入下面的命令

  • java -classpath xxx/xx/xxx/xx java.lang.Object

就应该可以读取Objecgt的文件数据,不过肯定是二进制格式的,所以需要定义一个读取classpath的包。当前就暂且叫做classpath

这儿我们还是先引入一些必要依赖

Cargo.toml文件

mockall = "0.11.3"
zip = {version = "0.6.4", default-features = false, features = ["deflate"]}
tokio = {version = "1.26.0",  features = ["full"]}
  • mockall 是用来写单测的,好的开发者应该对于每一个module的开发都有完善的单测验证自己的逻辑
  • zip是压缩包,用于读取类似.jar 或者 .zip 或者 .war 或者 .rar 等文件,可能更多的是jar包,不过我们把其他压缩包也考虑进去
  • tokio 是用来实现一个自定义线程池,可以实现IPC高性能通信那种,当然这个是一个优化点,后续会考虑慢慢实现,不做也不影响整个项目的开发

首先是项目结构,目前我们考虑有通过 绝对路径去获取class文件的,也有通过xx/*等方式获取的,也有.jar的压缩文件,也有读取多个文件的,所以我们定义四个rs文件来区分它们

alt

classpath 中需要定义两个重要的方法 分别是 parse() 和 read_class(), 但是考虑到读取class是一个通用的方法,需要读取不同种类的文件,所以就准备定义成一个trait来使用

::: block-1

这儿说的trait,用java和go的看法来说,就是接口,不过在rust中叫特征。 :::

所以我们定义一个公共的Entry,作为trait的入口

entry.rs

/// 目前有这么几种文件对象
/// -classpath file => 目录形式的类路径 entry_dir.rs
/// -classpath file1.jar:file2.jar:file3.jar  => 多个文件对象组成的类路径 entry_multiple.rs
/// -classpath file/*  => 以*结尾的路径 entry_wildcard.rs
/// -classpath file.jar => 压缩文件路径 entry_compression.rs
pub trait Entry: fmt::Display {
    fn read_class(&mut self, class_name: &str) -> Result<Vec<u8>, String>;
}

同时在classpath中定义我们之前说好的两个方法,分别作解析和读取文件数据。

classpath.rs

impl Classpath {
    pub fn parse(jre_option: &str, cp_option: &str) -> Self {
    // 加载 boot_classpath
    // 加载 ext_classpath
    // 加载 user_classpath   
    todo!()
    }
}

impl Entry for Classpath {
    /// 读取class数据,从boot_classpath 
    /// 到 ext_classpath 到 user_classpath
    fn read_class(&mut self, class_name: &str) -> Result<Vec<u8>, String> {
   
        todo!()
    }
}
  • boot_classpath: jvm自带类库,包含JavaSE核心类库,jvm启动时必用路径,包含jre/lib/lib/rt.jar 和 jre/lib/ext 中的jar包
  • ext_classpath: jvm扩展路径,用于加载开发者自己编写的扩展类库
  • user_classpath: 用户自定义路径,用于加载用户自己编写的java类

最后就是main方法的启动,在一开始我们最后是start_jvm()方法来进行启动的,所以我们也把解析class和读取class的操作放在这个方法中

main.rs

fn start_jvm(cmd: Command) {
    info!("[WELCOME USE THIS JVM, It's named azh after my girlfriend, thanks]");
    let mut classpath = Classpath::parse(&cmd.x_jre_option, &cmd.cp_option);

    info!("classpath: {} class: {} args: {:?}", classpath, cmd.class, cmd.args);

    let class_name = cmd.class.replace(".", "/");
    let class_data = match classpath.read_class(&class_name) {
        Ok(class_data) => class_data,
        Err(err) => { panic!("Could not find or load main class {}: {}", cmd.class, err); },
    };
    info!("class data: {:?}", class_data)
}

上面就是整个大体步骤,整体脉络就是两步骤,解析classpath,读取class文件,下一章会具体介绍jar、j*、(jar1,jar2)等文件的具体的parse方法和三种类路径的parse方法。

可以自己先写一下试试,对于单个class文件应该不难,就是从中读取二进制数据而已。

::: block-2

之前说的线程池,主要是想实现一个IPC高性能的缓存buffer,其中尽量通过Arc等指令和避免Box来做到无锁和不操作堆内存来达到高性能,不过目前还在构思当中,当前缓存的方案是想实现HotRing论文,可惜这个数据结构并不是那么容易实现,而且对于Rust写链表,相信学过的人都知道,这算是一个劝退题了~ :::

#大厂##23届找工作求助阵地##我的实习求职记录##Java##实习#
全部评论
???
1 回复
分享
发布于 2023-04-05 17:57 湖北
可以,很硬
1 回复
分享
发布于 2023-04-05 21:39 浙江
滴滴
校招火热招聘中
官网直投
哥们你来真的?
点赞 回复
分享
发布于 2023-05-07 11:09 四川
NB
点赞 回复
分享
发布于 2023-06-01 02:27 广东

相关推荐

人是群居性动物,害怕孤独,但是我发觉自己内心成长最快的就是前几个月孤独的自我探索,自我批判,从害怕孤独,慢慢地开始品味孤独,到现在的享受孤独的做自己和生活。这倒不是那种生活里没人陪的孤独,而是内心层面上的,默认自己一直都是一个人,需要自己学习和思考怎么扛住或者躲开生活里生存所需面对的超大压力,从繁琐的社交中把自己完全解放了出来,我想去哪去哪,想干啥干啥,不用花时间等谁,不用担心被🕊️,也不用担心说错话被算计,我有自己的想法并且去付诸实践。不过拥有这份权利的同时也有着诸多不便,比如有什么事发生了可能会是最后一个甚至这事情结束了都不知道的人,信息资源少,同时职场上有派系分别,不参与的话分配的资源也会少不少。对于心思敏感的人来说可能会很痛苦,但是对于楼主这种独狼性格的人来说简直是天赐福音(你们爱干啥干啥,爱怎么争怎么争,反正我就看乐子) 或许,这种性格不叫孤独而叫做独立?还有哈,好好沟通很重要的,要是觉得工作有些不舒服的地方,咱们可以沟通的啊,别骂人,比如前几天领导派我给一个春招新来的实习生教他熟悉熟悉jmeter和postman,这人本来是学后端的,我寻思他也不用干测试,本来怀着好心过来的,教不会也没事嘛,那我自认技不如人,反正你实习结束会不会跑都不知道,咱也就当交个朋友了,你也多个技术写简历,两全其美嘛不是,然后,我晚上去食堂吃饭的时候坐这实习生后面一点地方,他估计没看见,他和他一批的人反手骂我**给他增加工作骂到吃完走人,不是,您以为我很闲吗,一堆事没干抽俩小时教你跑来伺候您,只是熟悉又没让你干测试的活,我寻思你领导也没给你派啥活啊,大伙都是来打工的,要不是我领导要求,你***谁啊……咳咳,不好意思,怒上心头了……咱持有不满,你大可直接跟我说你不学不就完了,非得我教完了在背后骂我 #应届生初入职场,求建议#
点赞 评论 收藏
转发
不一会,HR来找三小只谈话了,大致的内容就是项目进行不下去了,这边也留不了你们了,建议转&nbsp;Java&nbsp;,行情会好一些,校招生还是从事&nbsp;Java&nbsp;开发会更合适一些。​收拾完东西后,五小只成立失业五人组,中午在熟悉的公司旁的商圈点了顿干锅。​大四哥们:“艹!**公司,RM养不起就不要招,浪费Lz准备考公的时间。”​大四哥们二号:“算了算了,那还能啷个办呢,继续投嘛。不然又只有进厂了。”​小W:“哎呀,我觉得那个人对你还可以啊,还给你分了点活干,说明还是想培养你的,要不你现在回去再问一下。”​小Z:“我们属于是整个业务线没了,没有什么挽回的余地了。”​小W:“你不能楞个想啊,他可以帮忙维护现有的项目啊,我觉得你还是回去谈一下比较好,我们勒些大三的无所谓,但是你们已经大四了。”​小Q一直没怎么说话。只是抱怨了一下不知道如何应对学校。​大四哥们:“小Z,你好像学了两年移动开发吧。”​小Z:“嗯,我就是做这个的,之前大厂面试没发挥好,只能到这边混一混了。”​大四哥们:“据我们所知,这个移动端也不太好找工作,我们&nbsp;Android&nbsp;还好一点,可以做车机什么的,你&nbsp;iOS,呵呵,只能说很有勇气。”​小Z:“...”​小Z:“你打算之后怎么办?”​大四哥们:“考公啊。”​小Z:“那你考公那些信息从哪里获取的。”​大四哥们:“xxx,xxxx,xx&nbsp;这些APP。”​小Z:&quot;行。&quot;​吃完饭后,大四两哥们要道别了,不忘提了句有缘再见,其实我们都知道,不出意外这就是最后一面了。​失业三小组开始了重庆一日游,一路步行到千厮门大桥,路上讨论着专业,学校,之后的打算,晚上吃了个疯狂星期四。现在是退潮的季节,来江边游玩的人越来越多,有人拍婚纱照,有人唱歌,有小孩嬉戏,对于小Z来说,人与人的悲欢并不相同,他只觉得他们吵闹。 #我的实习求职记录# #iOS开发##iOS#
点赞 评论 收藏
转发
6 7 评论
分享
牛客网
牛客企业服务