先导知识
xv6为每一个进程都创建一张页表,除此之外还有一张表用来描述内核的虚拟地址映射(这有很多不合理之处,之后的实验会手动修改)。
下图是原始的xv6的内核的内存映射图:

可以看到,大部分的映射的线都是水平的,也就是虚拟地址直接映射到物理内存,只有最上面的那些是斜着映射到RAM中的,而且看样子似乎能够覆盖掉内核的代码段。
系统调用中,我们是如何传递参数的?
在x86-32中:通过中断实现(int 0x80)。把对应的系统调用的号码放入eax寄存器中,具体的参数放到指定的寄存器中。更为具体的代码:
1 | pushl ebx # 把寄存器的内容丢到用户栈里面 |
INT是用于x86处理器的汇编语言指令,该指令会生成软件中断。后面跟的0x80可以理解为指代是一个具体的中断处理程序。在Linux中,这个中断处理程序恰好就是内核。更加具体的中断号码对应的操作可以看这里。
在x86-64中:通过syscall指令实现(其实也可以通过int 80,但是推荐别那么做)。同样把对应的系统调用放入eax寄存器中,具体的参数放到指定的寄存器中(和32位不同)。具体为什么从32位系统过度到64位系统的时候,会有这种变化,主要还是因为有了新的指令的支持,所以可以重新用新的机制。具体的变化的更为详细的信息可以阅读这份文档。
这篇博客主要是细读spring-core的文档,其中主要列出了文档中比较重要的点,以及一些自己的思考,并不是事无巨细都列出来的。
文档对应的链接:https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#beans
对应的spring版本:5.3.5
这篇博客是学习spring的时候,对于官网中的guides做出的一些自己的理解和拓展。
官方几乎对于所有的方面都做了相关的guides介绍,每一个guides只需要15-30分钟即可。
首先,你需要了解,什么是RESTful,这个概念是Roy Thomas Fielding这位老哥在2000年的时候,在自己的博士毕业论文中提出的概念,有兴趣可以去阅读一下原文。
在这个小guide中,将会介绍,如果使用spring来返回一个json数据格式。
只需要构造好一个对应的对象(bean),然后创建对应的controller就可以了。
问题来了,我们的controller只是返回了一个对象,为什么最后能够以json的形式返回呢?
这是因为在类上声明了@RestController(@ResponseBody),有了这个注解,它会利用jackson自动把对象给解析成json进行返回。
更加具体的,只需要你的pojo有对应的getter方法,那么这些有getter方法的属性,就会自动解析成json返回。
@Component是一个比较通用的说法,即任何被spring管理的类,都可以加上这个注解。剩下的三个则是它的更加具体的表现,显然如果你的类有更加明确的作用,那么标注更加具体能够在语义上更好地表现。
这个小guide没有使用web,只是展示了如何使用spring实现一个定时任务,比如每隔多久运行一次,spring也可以轻易实现。
其实就是把需要实现定时功能的代码写到一个方法中,然后使用@Scheduled注解即可。
这里注意需要把对应的类加上componet注解加入到spring中,否则是不生效的。
同时还需要在主应用中使用@EnableScheduling来开启定时任务。
Scheduled这个注解本身如果需要测试的话,需要额外使用awaitility这个工具。
前面学习了如何使用spring来构造一个RESTful的web服务,这一部分则是讲述如果收到一个json数据,如何去处理它。
官方为我们提供了一个随机的RESTful的网站,访问即可获取对应的json数据。
对应的json数据看起来长这样:
1 | { |
第一步自然而然就是根据上面的json数据来构建一个java的POJO了。
这里有一个新的知识点,就是之前是我们把一个对象给序列化成json,而这里是别人的json数据我们反序列化成对应的对象,要是里面有我们不认识的数据怎么办?这里jackson是提供了一个注解(@JsonIgnoreProperties(ignoreUnknown = true))来忽略的。
当然你也可以每个对象的名字和上面的json的key不对应,只需要@JsonProperty指定就可以了。
然后就是重点:RestTemplate这个类了。这个类以json数据作为输入,然后用第三方库进行处理,并且这个类可以从远端获取对应的json数据串,并且解析成对应的对象。
如果我的json的key和POJO的属性名不对应,会发生什么情况?那就解析不了,对应的值是NULL。当然是用@JsonProperty指定对应的json的key就可以了。
RestTemplate使用Jackson的解析库来解析。CommandLineRunner会自动在springboot启动的时候进行运行。
这两者区别就大了,而且是完全不能搞混的。
@Component通过使用类路径扫描来进行自动检测和自动配置,让spring管理一个类。
@Bean则是告诉spring管理这个方法返回的对象。
所以这两者都是能够让框架来管理对应的类。
这是一个接口,实现了这个接口的bean可以在容器加载完成之后执行。一般我们可以让一个方法返回一个CommandLineRunner对象,这样就能启动了;或者也可以在主类中声明实现这个接口,并且重写对应的run方法。
暂时没有这个需求。
第一步,创建文件夹:mkdir -p src/main/java/hello。
第二步,写入对应的java类。
第三步,在和src同级的目录下,创建一个pom.xml文件,写入对应的信息。
第四步,开始使用maven进行编译、打包、安装到本地库等一系列操作。
之后又介绍了一下使用JUnit配合maven进行单元测试。
首先,我们假定数据需要访问的有一个顾客对象,然后顾客有ID、姓和名这三个属性。
Spring为开发者提供了JdbcTemplate这个类来让开发人员focus on业务。
首先第一步,当然是为了对应的实体创建对应的POJO。
第二步,获取JdbcTemplate对应的实例,由于在pom文件中已经有了对应的starter(spring-jdbc),所以springboot会帮我们自动注入,我们只需要autowire即可。
第三步,教程中选用的是H2数据库,这是一个用java写的嵌入式数据库,且只在内存中生效,所以用来作为测试是非常方便的。然后就是使用JdbcTemplate用来插入和查询,并将查询结果封装到对应的POJO中。
利用jdbcTemplate简单操作数据库。
一些删除表格、创建表格等的DDL语句,可以直接用jdbcTemplate.execute来执行对应的语句。
插入可以用?作为占位符,然后传入一个Object[]作为参数,这个数组中的每一个参数会一一取出然后填入问号里。
在这一部分,使用Spring Data JPA这项技术来访问数据。所以依赖关系如图所示:

到对应的数据库中,创建对应的数据库和用户,并且授予对应的权限(非常标准的做法):
1 | create database db_example; -- Creates the new database |
之前我们使用H2数据库的时候,我们并没有做任何配置,似乎是直接拿来就用了,这是因为在springboot中的默认配置文件中,默认指定了数据库是H2,这次使用了MySQL,所以需要修改:
1 | spring.jpa.hibernate.ddl-auto=update |
这里使用的是hibernate框架,第一个是hibernate的设置。
然后创建对应的实体来对应数据库的记录,这里只需要在类上标注上@Entity,然后再在主键上使用@Id就可以了。
创建一个仓库类,来管理所有的这些增删改查,spring中有一个不错的父类可以继承:CrudRepository,它实现了通过id增删改查,所以就省去了很多代码。
好了,最后就是controller层,就是简单封装一下而已。
这里遇到一个比较严重的问题:required a bean named 'entityManagerFactory' that could not be found.,显然是在spring中没有这个对应的bean,然后我网上搜了不少答案,最后大概率应该是有bad的jar file,然后只能把.m2下面的repository删掉了重新下….
相同的问题发现网友也有不少,参考这个链接。
测试的话,可以用postman,也可以用命令行工具curl,利用-d(data)这个选项指定对应的数据。当然也可以查看对应的信息。
具体使用hibernate的crud的这个repo类的话,可以发现在数据库中它还额外生成了一张表,是下一个值的表。
这次除了标准的web应用之外,还需要用到thymeleaf这个渲染的第三方库。
然后是上传文件的话,需要用到MultipartConfigElement,但是springboot已经帮我们配置好了。也就是在springboot启动的时候,已经有对应的bean在这个容器中了。
上来首先写一个存储服务的接口,因为我们的存储服务肯定需要一些操作。
1 | public interface StorageService { |
定义好了对应的接口之后,就需要对应的实现。这里教程里非常奇怪,直接就给出了对应的实现,并不要求你去实现它。应该也是固定的一些套路,看了一眼代码并不算很难。
systemctl在最开始是