一文讲解Linux内核中根文件系统挂载流程

article/2025/9/19 19:40:40

根文件系统的概念

根文件系统是控制权从linux内核转移到用户空间的一个桥梁。linux内核就类似于一个黑匣子,只向用户提供各种功能的接口,但是功能的具体实现不可见,用户程序通过对这些功能接口的不同整合实现不同的功能需求。以用户的角度来说,应用程序调用内核的接口实现不同的功能,此时系统的控制权在用户手中,但是实际上却是先有内核的初始化提供这些接口,用户才可以使用这些接口的,也就是系统的控制权最初应该属于内核。那么控制权是如何从内核转交到用户的呢?通过调用init程序实现,而一般把存在init程序的文件系统称之为根文件系统。

文件系统是基于物理存储设备至上的一种机制,用于存储空间的管理,并维护文件内容与磁盘单元之间的对应关系,便于对文件内容的访问。由前面所述,init程序存储在文件系统之中,如果需要访问init程序必须能够识别对应文件系统的格式(通过挂载实现),而文件系统又建立在物理存储设备之上,所以需要物理存储设备的驱动程序已准备就绪。

文件系统的挂载需要提供挂载点(挂载目录),linux内核在初始化时会初始化一个虚拟的“/”目录用于根文件系统的挂载,其初始化过程如下:

start_kernelvfs_caches_init()mnt_init()init_rootfs()register_filesystem(&rootfs_fs_type)						//注册虚拟的rootfs文件系统init_mount_tree()												//创建“/”目录bdev_cache_init()chrdev_init()rest_init()kernel_thread(kernel_init, NULL, CLONE_FS)kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES);

内核资料分享:Linux内核源码技术学习路线+视频教程代码资料https://link.zhihu.com/?target=https%3A//docs.qq.com/doc/DUGZVQk1qWVBHTEl3

内核学习:Linux内核源码/内存调优/文件系统/进程管理/设备驱动/网络协议栈-学习视频教程-腾讯课堂是不是学完操作系统原理后觉得纸上谈兵不过瘾?是不是面对浩若烟海的Linux内核源代码迷失在代码的海洋里不知所措?这门课可以带您用理论结合实践的方法一步一步抓住Linux内核最核心的部分代码,理解Linux操作系统运行的基本过程及涉及的核心机制。https://ke.qq.com/course/4032547?flowToken=1044374

initramfs和initrd

若init程序存在的物理磁盘设备在内核访问时已准备就绪,内核便可以直接挂载根文件系统并运行init程序,实现kernel空间到用户空间的跳转。但是用户程序可能根据不同的需要存储在诸如IDE、SCSI、USB等多种介质当中,如果将所有的驱动程序都编译进内核,将导致内核异常臃肿,因此,为了使内核适应不同的存储介质,同时不将非必要的驱动程序编译进内核,可使用一种过渡根文件系统。过渡根文件系统与linux内核存放在同一个存储设备中,因此可以直接挂载,而此时该文件系统中的init程序只是加载实际根文件系统的驱动和其他的一些初始化工作,待初始化完成,该init程序会挂载实际的根文件系统,并再次跳转到实际根文件系统中执行实际的init程序。

过渡的根文件系统根据是否直接编译进内核分为initramfs和initrd,而initrd根据文件系统的打包格式又分为cpio-initrd和image-initrd,通过cpio打包的文件系统可以直接释放到“/”,而无需挂载过程,initramfs也是cpio的打包格式。

根文件系统的整体挂载流程如下图所示:

首先,内核根据配置利用过渡根文件系统或默认设置初始化“/”目录;然后检测初始化的“/”目录中是否存在指定应用程序(默认为init程序),如果存在,则直接执行该init程序;否则,根据配置挂载实际根文件系统。与之对应的函数流程为:

kernel_initkernel_init_freeable()do_basic_setup()do_initcalls()do_initcall_level(level) 										
[1]					do_one_initcallif (!ramdisk_execute_command)										//如果没有定义rdinit参数,则默认执行rootfs中的/init程序ramdisk_execute_command = "/init";[2]		sys_access((const char __user *) ramdisk_execute_command, 0)		//查看rootfs是否有/init程序,若包含则直接运行,否则运行prepare_namespaceramdisk_execute_command = NULL;									//并将ramdisk_execute_command置为NULL
[3]			prepare_namespace()//此时已经处于实际根文件系统的目录下if (ramdisk_execute_command) {											//首先尝试运行rdinit=指定的程序run_init_process(ramdisk_execute_command);[4]	if (execute_command)  													//再尝试运行init=指定的程序run_init_process(execute_command)if (!try_to_run_init_process("/sbin/init") ||							//如果没有rdinit= 和 init= 指定的程序,则查找如下的程序运行!try_to_run_init_process("/etc/init")  ||!try_to_run_init_process("/bin/init")  ||!try_to_run_init_process("/bin/sh"))

【1】初始化“/”目录

内核初始化时会依次调用处于.init段的初始化函数,其中与根文件系统相关的初始化函数为default_rootfs和populate_rootfs,两个函数根据内核配置项的选择决定是否会被运行:

Makefile:obj-y							+= noinitramfs.oobj-$(CONFIG_BLK_DEV_INITRD)	+= initramfs.omounts-$(CONFIG_BLK_DEV_INITRD)	+= do_mounts_initrd.oinitramfs.crootfs_initcall(populate_rootfs);noinitramfs.c:#if !IS_BUILTIN(CONFIG_BLK_DEV_INITRD)rootfs_initcall(default_rootfs);#endif

由上述代码段可知,在初始化阶段,default_rootfs和papulate_rootfs是互斥的,并由CONFIF_BLK_DEV_INITRD决定哪一个函数会在初始化阶段被执行。CONFIF_BLK_DEV_INITRD配置内核对过渡根文件系统的支持,当配置CONFIF_BLK_DEV_INITRD=y时,包含papulate_rootfs的initramfs.c被编译进内核并在初始化时被执行,并对过渡根文件系统进行解析,而相反的包含default_rootfs的noinitramfs虽然被包含进内核,但是却由于#if语句不会被执行。相反,当CONFIF_BLK_DEV_INITRD不被配置时,default_rootfs会在初始化时被执行,而papulate_rootfs则不会被运行。default_rootfs只是在“/”目录下初始相关节点即完成初始化,如下:

default_rootfssys_mkdir((const char __user __force *) "/dev", 0755)sys_mknod((const char __user __force *) "/dev/console",S_IFCHR | S_IRUSR | S_IWUSR,new_encode_dev(MKDEV(5, 1)));sys_mkdir((const char __user __force *) "/root", 0700);

papulate_rootfs则对过渡根文件系统进行解析,如果过渡根文件系统是cpio格式的initramfs和initrd,则直接解压并释放到“/”目录中,以initramfs或initrd中的内容对“/”进行初始化,而如果是image格式的initrd则需要通过创建虚拟的ramdisk对过渡根文件系统进行挂载才能访问,具体过程如下:

static int __init populate_rootfs(void)
{
[1.1]	if (do_skip_initramfs) {												//跳过initrd过程if (initrd_start)free_initrd();return default_rootfs();											//只创建包含基本目录结构的default_rootfs}
[1.2]	err = unpack_to_rootfs(__initramfs_start, __initramfs_size);			//首先尝试解压initramfs到rootfs[1.3]	if (initrd_start && !IS_ENABLED(CONFIG_INITRAMFS_FORCE)) {			//如果存在initrd
#ifdef CONFIG_BLK_DEV_RAM													//如果定义了CONFIG_BLK_DEV_RAM,则需要以cpio.initrd和image.initrd两种格式尝试对initrd进行解析
[1.4]		err = unpack_to_rootfs((char *)initrd_start, initrd_end - initrd_start);if (!err) {															//若以cipo.initrd的格式将initrd解压到rootfs成功,则释放initrd占用的空间		free_initrd();goto done;} else {clean_rootfs();unpack_to_rootfs(__initramfs_start, __initramfs_size);			//若以cpio.initrd解压到rootfs失败,则以initramfs unpack将rootfs还原到初始状态}[1.5]		fd = sys_open("/initrd.image", O_WRONLY|O_CREAT, 0700);			//以image.initrd的格式对initrd进行解析if (fd >= 0) {														//将initrd内容复制到rootfs的/initrd.image目录下,即ramdisk中ssize_t written = xwrite(fd, (char *)initrd_start, initrd_end - initrd_start);sys_close(fd);														free_initrd();													//释放initrd占用的内存空间}done:
#else																		//如果没有定义CONFIG_BLK_DEV_RAM,则只以cpio.initrd的格式解压initrd到rootfs
[1.4]		err = unpack_to_rootfs((char *)initrd_start, initrd_end - initrd_start);free_initrd();														//释放initrd原来占用的空间
#endif}
}

当内核支持过渡根文件系统时,由于内核并不知道过渡根文件系统以何种形式存在,所以总是试图以initramfs、cpio-initrd到image-initrd的顺序对过渡根文件系统进行解析/挂载。若采用initramfs的格式对过渡根文件系统进行封装,则需要配置CONFIG_INITRAMFS_SOURCE项,指定过渡根文件系统的路径并将该路径下的内容编译进内核。该路径下的内容可以是一个包含所有过渡根文件系统所需文件的目录、或是一个已将内容打包完成的cpio包,亦或是一个指导内核打包相关文件的配置文件。当路径下的内容为一个目录或一个配置文件时,编译时会自动生成cpio格式的initramfs并包含进内核,而cpio包则需要与cpio-initrd一样预先打包完成。编译出来时,initramfs的位置由内核指定,因此内核可直接通过地址__initramfs_start对其进行访问。即使当该项为空时,内核同样会在该位置编译一个空的包。

【1.1】do_skip_initramfs

do_skip_initramfs为一个布尔变量,默认为0,可以通过kernel cmdline中的skip_initramfs参数将其指定为1,如下:

static int __initdata do_skip_initramfs;
static int __init skip_initramfs_param(char *str)
{if (*str)return 0;do_skip_initramfs = 1;return 1;
}
__setup("skip_initramfs", skip_initramfs_param);

当指定该参数为1时,表示内核忽略过渡根文件系统,直接以default_rootfs初始化“/”目录,并直接挂载实际根文件系统。如果此时存在过渡根文件系统,并且过渡根文件系统同样被配置为一个具有实际功能的文件系统(不仅仅起过渡作用),那么可以通过cmdline的skip_initramfs指引内核转移到具有不同功能的用户空间中,如android的recovery模式和boot模式。

【1.2】unpack_to_rootfs(initramfs)

无论内核中是否存在实际的initramfs,内核总是会首先以initramfs的格式试图对其进行解析。

【1.3】initrd解析

initrd与内核一样由uboot加载到内存的指定位置以待处理,此时内核并不知道initrd在内存中的位置,需要通过uboot传递的参数进行指定,在内核中该参数表现为initrd_start变量。当该变量不为0时,表示存在initrd需要解析,而initrd又分为cpio-initrd和image-initrd,cpio-initrd可以直接被解压释放到“/”目录中,而image-initrd则需要借用虚拟设备ramdisk,首先将image-initrd的内容装载到ramdisk中,然后对ramdisk进行挂载,才能对image-initrd中的内容进行访问。

内核是否支持建立虚拟设备ramdisk由CONFIG_BLK_DEV_RAM配置项决定。当配置CONFIG_BLK_DEV_RAM=y时,内核需要分别以cpio-initrd和image-initrd的格式对initrd_start处的内容进行解析(【1.4】和【1.5】),否则只以cpio-initrd的格式进行解析(【1.4】)。

【1.4】unpack_to_rootfs(initrd)

cpio格式的initrd也直接解压并释放到“/”目录中,与initramfs中不同的是,initramfs存在于内核的所占内存,释放到“/”目录之后,原initramfs所占内存不会被释放。而initrd由uboot加载进内存,当initrd被释放到“/”目录之后,initrd所占的内存可以被释放以节约内存空间。

【1.5】image-initrd

若initrd并不是cpio格式,则以image-initrd的格式对其进行解析,此时只是将image-initrd的内容转移到“/initrd.image”,并释放initrd的占用空间,具体的处理过程在prepare_namespace中的load_initrd中。

【2】检查初始化程序是否存在

在无论哪种形式完成“/”目录的初始化之后,检查“/”目录中是否包含初始化init程序,如果有则直接运行该程序并跳转到用户空间。该init程序的具体执行流程由用户决定。如果过渡根文件系统不是最终的根文件系统,用户可通过init程序安装实际根文件系统的驱动并挂载真正的根文件系统。

【3】挂载实际根文件系统

如果“/”目录下不存在初始化程序,则尝试在内核中直接挂载根文件系统,分为两种情况:根文件系统存在于MTD/UBI设备,驱动程序在内核初始化阶段已安装,可直接挂载;根文件系统存在其他存储设备中,以过渡根文件系统安装存储设备的驱动,最后由内核挂载根文件系统。具体过程如下:

void __init prepare_namespace(void)
{if (root_delay) {											//root_delay由rootdelay= 参数指定,__setup("rootdelay=", root_delay_setup);ssleep(root_delay);										//再挂载根文件系统之前等待一段时间,待驱动程序准备就绪}wait_for_device_probe();									//等待device设备初始化完成md_run_setup();dm_run_setup();if (saved_root_name[0]) {									//saved_root_name由root=参数指定,__setup("root=", root_dev_setup)root_device_name = saved_root_name;if (!strncmp(root_device_name, "mtd", 3) ||!strncmp(root_device_name, "ubi", 3)) {				//若root=指定的根设备为mtd或ubi分区,则直接挂载根文件系统,不经过initrd
[3.1]		mount_block_root(root_device_name, root_mountflags);goto out;}ROOT_DEV = name_to_dev_t(root_device_name);				//获取根设备的设备号if (strncmp(root_device_name, "/dev/", 5) == 0)			//若root=指定的根设备为/dev下的节点,则需要通过initrd挂载真正的根文件系统root_device_name += 5;}[2.2]	if (initrd_load())										//若CONFIG_BLK_DEV_INITRD没有被定义,该函数返回为0goto out;if ((ROOT_DEV == 0) && root_wait) {printk(KERN_INFO "Waiting for root device %s...\n",saved_root_name);while (driver_probe_done() != 0 ||(ROOT_DEV = name_to_dev_t(saved_root_name)) == 0)msleep(5);async_synchronize_full();}is_floppy = MAJOR(ROOT_DEV) == FLOPPY_MAJOR;if (is_floppy && rd_doload && rd_load_disk(0))ROOT_DEV = Root_RAM0;[3.3]	mount_root();											//如果不支持initrd,或initrd挂载根文件系统失败,则会直接尝试mount_root挂载实际的根文件系统
out:devtmpfs_mount("dev");									sys_mount(".", "/", NULL, MS_MOVE, NULL);					//将当前目录移动到“/”目录下sys_chroot(".");											//将当前目录设置为系统根目录
}

【3.1】如果根文件系统在MTD/UBI设备上,则直接进行挂载。因为其驱动程序为内核必须,所以在内核初始化时便会加载。此时将根文件系统将被挂载到“/root”下:

mount_block_root:mount_block_root(root_device_name, root_mountflags);do_mount_root(name, p, flags, root_mount_data);sys_mount(name, "/root", fs, flags, data);

【3.2】load_initrd

当根文件系统存储在其他存储设备上时,此时需要利用过渡根文件系统对存储设备进行识别并挂载实际的根文件系统,当然前提是使能了initrd和ramdisk。image-initrd格式的数据并不能直接被释放到“/”目录下,而需要通过挂载来访问,而挂载的前提是存储设备驱动已安装,内核将内存中特定的一段区域抽象为一个虚拟的存储设备以供挂载使用,即ramdisk。将image-initrd中的内容加载到ramdisk中,并通过挂载ramdisk,便能够对image-initrd中的内容进行访问了。如果ramdisk被指定为最终的文件系统存储设备,则此时只是将image-initrd的内容加载到ramdisk中,但并不在此对其进行挂载,而在最终的mount_root对其进行挂载。过程如下:

bool __init initrd_load(void)
{if (mount_initrd) {													//mount_initrd默认为1,可通过noinitrd将其指定为0,__setup("noinitrd", no_initrd);create_dev("/dev/ram", Root_RAM0);								//创建/dev/ram节点if (rd_load_image("/initrd.image") && ROOT_DEV != Root_RAM0) {	//如果/initrd.image中存在initrd,则将其装载到/dev/ram中,并且判断/dev/ram是否被指定为最终的文件系统sys_unlink("/initrd.image");								//如果存在image.initrd,且/dev/ram不是指定的根设备,则需要通过initrd挂载实际的根文件系统handle_initrd();											//挂载实际根文件系统return true;}}sys_unlink("/initrd.image");										//如果initrd不存在,或者/dev/ram即为最终根文件系统,则直接返回,通过mount_root进行挂载return false;
}
————————————————
版权声明:本文为CSDN博主「LeoSoldOut」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_35018427/article/details/105441605

【3.3】挂载实际根文件系统

对于存储在除MTD和UBI的其他存储设备的文件系统,若不存在过渡根文件系统,或ramdisk为实际的根文件系统,则尝试直接对实际根文件系统进行挂载。实际上,如果根文件系统所在的驱动程序已经编译进内核,则不需要initramfs和initrd的过渡,内核会跳到此处直接对其进行挂载。

void __init mount_root(void)
{
#ifdef CONFIG_ROOT_NFSif (ROOT_DEV == Root_NFS) {if (mount_nfs_root())return;ROOT_DEV = Root_FD0;}
#endif
#ifdef CONFIG_BLK_DEV_FDif (MAJOR(ROOT_DEV) == FLOPPY_MAJOR) {if (rd_doload==2) {if (rd_load_disk(1)) {ROOT_DEV = Root_RAM1;root_device_name = NULL;}} elsechange_floppy("root floppy");}
#endif
#ifdef CONFIG_BLOCK{int err = create_dev("/dev/root", ROOT_DEV);		mount_block_root("/dev/root", root_mountflags);}
#endif
}

实际根文件系统挂载到“root”目录下,通过sys_mount的MS_MOVE参数将根文件系统的内容移动到“/”目录下,并且将当前目录切换为系统根目录,即此后从init程序派生的子进程的根目录为当前目录。MS_MOVE参数的意义:

mount --move olddir newdir
mount -M olddir newdir
这个mount动作是将原来在olddir中的所有文件内容,都显示到newdir中。原文件内容的保存位置不变。此时olddir必须是一个挂载点。

【文章福利】小编推荐自己的Linux内核技术交流群:【977878001】整理一些个人觉得比较好得学习书籍、视频资料共享在群文件里面,有需要的可以自行添加哦!!!前100进群领取,额外赠送一份价值699的内核资料包(含视频教程、电子书、实战项目及代码)

【4】执行用户初始化程序

此时根文件系统已经挂载完成,则查找根文件系统中的初始化程序并执行,init程序一般由kernel cmdline或dts指定:

		__setup("init=", init_setup);

当“/”目录下不存在init程序时,内核也会从sbin/etc/bin等其他目录下查找默认的可执行程序。

写在后面

实际上是否采用initramfs/initrd由实际需求而定,当实际存储根文件系统的存储设备驱动已安装时,没必要采用initramfs/initrd的过渡。如果需要initramfs/initrd的过渡,则需要根据以上的根文件系统挂载流程判断使用哪种格式的过渡根文件系统,然后对其进行配置。cpio格式的文件系统根据init程序决定该文件系统是过渡文件系统还是本身已是最终的根文件系统,该init程序的内容由用户自行定义。

init程序为用户空间的第一个程序,是其他所有程序的祖先,可以将init程序成为用户空间的内核,为用户空间其他程序的运行提供基础环境的初始化,init程序在多个init管理系统中呈现不同的运行、配置规则,如sysvinit、busybox init、systemd等,如果我们需要在init程序中添加自己对环境的配置,则需要了解这些init系统的运行规则。


http://chatgpt.dhexx.cn/article/2G3gkkGQ.shtml

相关文章

【linux kernel】挂载根文件系统之rootfs

挂载根文件系统之rootfs 文章目录 挂载根文件系统之rootfs一、开篇二、rootfs根文件系统(2-1)初始化rootfs(2-2)挂载rootfs文件系统(2-3)创建简单的rootfs根文件系统目录和文件(2-4)…

2021年Linux技术总结(三):根文件系统(rootfs)

# 一、根文件系统简介 Linux系统三大块:U-boot、kernel以及最后这个rootfs,跟文件系统。在kernel中,启动流程的最后会调用 prepare_namespace 函数,挂载根文件系统,这里就是挂载的本篇要说的。 根文件系统保存了内核代…

详解制作根文件系统

目录 前言 具体步骤 一.编译/安装busybox,生成/bin、/sbin、/usr/bin、/usr/sbin目录 二.利用交叉编译工具链,构建/lib目录 三.手工构建/etc目录 四.手工构建最简化的/dev目录 五.创建其它空目录 六.配置系统自动生成/proc目录和构建…

根文件系统理解

文件系统概念 文件系统是一些代码,是一套软件,这套软件的功能就是对存储设备的扇区进行管理,将这些扇区的访问变成了对目录和文件名的访问。我们在上层按照特定的目录和文件名去访问一个文件时,文件系统会将这个目录文件名转换成对…

linux之根文件系统

前言 1、板卡上电后首先由UBOOT启动初始化板卡,将Linux内核移到内存中运行 2、由linux内核自行做了初始化等操作,挂在了第一个应用程序上(根文件系统/linuxrc) 3、根文件系统会提供磁盘管理服务,glibc设备节点&…

Linux:根文件系统构建

文章目录 一、编译 BusyBox 构建根文件系统1.创建BusyBox路径并解压2.修改顶层Makefile3.修改 busybox 源码4.配置busybox5.编译busybox6.向 rootfs 的“/lib”目录添加库文件7.向 rootfs 的“usr/lib”目录添加库文件创建其他文件夹 二、NFS挂载根文件系统1.bootargs 环境变量…

制作嵌入式Linux根文件系统

文章目录 1. 根文件系统布局2. 使用BusyBox生成二进制工具2-1. 获取BusyBox源码2-2. 配置BusyBox2-2-1. 选择编译静态库2-2-2. 选择交叉编译工具链2-2-3. 选择安装目录2-2-4. 编译安装 3. 构建根文件系统3-1. 完善目录结构3-2. 添加C运行库文件3-3. 添加初始化配置脚本3-3-1. 修…

[架构之路-30]:目标系统 - 系统软件 - Linux OS根文件系统rootfs的概念、组成、制作以及用busybox制作根文件系统

目录 前言: 第1章 什么是根文件系统 1.1 什么是文件 1.2 什么是文件系统 1.3 文件系统组织文件的方式:树形结构 1.4 统一的虚拟文件系统 1.5 物理存储介质与物理文件系统类型 1.5 什么是根文件系统 第2章 根文件系统的标准结构 2.1 根文件系统…

安装Ubuntu 16.04时出现:没有定义根文件系统,请到分区菜单修改

在安装Ubuntu 16.04时,尤其是选项空闲硬盘新建分区安装时,容易出现这种情况,这个是由于没有配置挂载点导致的,解决方法如下: 在挂在点输入“/”。 原理: Linux和Windows的文件系统不一样&#x…

Ubuntu提示“没有根文件系统 ”

安装Ubuntu时,提示“没有定义根文件系统” 原因:分区错误 解决: 将Ubuntu分区,删除重新创建分区,注意挂载点为“/” 此时就可以单击“继续”,进行下一步安装 以上分区方式,是没有交换分区的,下面提供两种带交换分区的分区方式: 第一种,包含4个分区,分别为 /分区(…

安装linux显示没有定义根文件系统,XP用Wubi安装Ubuntu提示“没有定义根文件系统,请返回分区菜单...

在安装Ubuntu时,到自定义分区一步时,会出现“没有根文件系统”,这时千万别将硬盘分区表重建,那样会让硬盘到数据都格式化到,你只需在ext4 或者 ext3 分区项上双击,加上挂载点为“\”就可以了。 有图有真相&…

在虚拟机安装中遇到的问题

问题一:在命令行模式下输入ifconfig时,显示系统不能识别这个命令,需要安装,输入sudo apt install net-tools。 问题二:sudo命令输入密码时,光标不移动,只要输入正确密码,回车就可以。…

安装Ubuntu时,提示“没有根文件系统 ”

安装Ubuntu时,提示“没有定义根文件系统” 原因:分区错误 解决: 将Ubuntu分区,删除重新创建分区,注意挂载点为“/” 此时就可以单击“继续”,进行下一步安装 以上分区方式,是没有交换分区的&a…

synchronized,voliate-详解

一.synchronized底层原理: synchronized关键字,在底层编译后的jvm指令中,会有monitorenter(枷锁)和monitorexit(释放锁)两个指令. monitorenter指令执行的时候会干什么呢? 每个对象都有一个关联的monitor,比如一个对象实例就有一个monitor…

volatile的作用及原理

前言 voliate关键字的两个作用: 1、 保证变量的可见性:当一个被volatile关键字修饰的变量被一个线程修改的时候,其他线程可以立刻得到修改之后的结果。当一个线程向被volatile关键字修饰的变量写入数据的时候,虚拟机会强制它被值刷…

voliate和synchronized

线程安全考虑三个方面:原子性,可见性,有序性 为什么使用voliate关键字? 正常情况下编译器为了加快程序运行的速度,对一些变量的写操作会先在寄存器或者是CPU缓存上进行,最后才写入内存.而在这个过程,变量的新值对其他线程是不可…

关于voliate关键字

现象 private static boolean is false;public static void main(String[] args) {new Thread(new Runnable() {Overridepublic void run() {System.out.println("thread 1 start");while (!is) {}System.out.println("thread 1 end");}}).start();try {T…

并发专题之---Voliate引发的各种原理问题

文章目录 前言JMMvoliate不保证原子性不保证原子性的解释AtomicInteger解决不保证原子性的问题为什么AtomicInteger可以解决原子性问题?CASCAS的内部原理CAS的缺点ABA问题原子引用解决ABA问题 禁止指令重排多线程环境下单例模式出现的问题双端检索机制解决办法双端检索机制的隐…

voliate类型的原理及用法

voliate类型,机器才读取执行代码读取内存后,将读取的内容存到高速缓冲区里,在硬件里是寄存器,这样在一下次读取的时候就可以直接从高速缓存区里面读取(cache),这也是读取速度加快的原因,但是如果…

voliate(轻量级的同步机制)

voliate特点: JMM内存模型三大特性:可见性,原子性,有序性。 1.禁止指令重排序: voliate实现禁止指令重排,避免多线程环境下程序出现乱序执行的现象。 多线程环境中线程交替执行,由于编译器优化重排的…