Linux 根文件系统的挂载分析

2023-10-26

在介绍根文件系统挂载之前先介绍一些基础知识

initramfs

当linux内核启动后,会找到并执行第一个用户程序,一般是init。这个程序存在于文件系统当中,文件系统存在于设备上,但不知道init存在哪个设备上,于是有了内核命令列选项root=,用来指定root文件系统存在于哪个设备上。

然后由于后来的设备类型越来越来多,比如可能在scsi,sata,flash这些设备,还有的存在于网络设备上,不可能把这些设备的驱动编译进内核,这样内核就会越来越来大。为了解决这些问题,出现了基于ram的文件系统,initramfs,这个文件系统可以包含多个目录和程序init,然后通过这个程序,内核再用这个程序去挂载真正的要文件系统。如果没有这个程序,内核可以来寻找和挂载一个根分区,接着执行一些/sbin/init的变种。

ramfs

ramf是一个小型的基于内存的文件系统,由于linux中页的数据被缓存在内存中,然后标识为可用,为防止别用,ramfs就是基于这种机制产生的。只是放在ramfs中的目录和页的缓存,不在写回。

rootfs

rootfs是一种特定的ramfs的实例,它一直存在于系统中,不能卸载。大部分其他的文件系统安装于rootfs之上。

initramfs和rootfs之间的关系

当内核启动的时候,会先注册和挂载一个虚拟的根文件系统,也就是rootfs,然后会把做好的initramfs(这个可以自己制作)中的文件解压到rootfs中。然后系统会挂载真的根文件系统,rootfs隐藏之后。

我的开发板上的u-boot传送的参数为noinitrd root=/dev/mtdblock3 init=/linuxrc console=ttySAC0,115200 mem=64M。

noinitrd的含义

(仅当内核配置了选项 CONFIG_BLK_DEV_RAM和CONFIG_BLK_DEV_INITRD)现在的内核都可以支持initrd了,引导进程首先装载内核和一个初始化的ramdisk,然后内核将initrd转换成普通的ramdisk,也就是读写模式的根文件系统设备。然后linuxrc执行,然后装载真正的根文件系统,之后ramdisk被卸载,最后执行启动序列,比如/sbin/init。

选项noinitrd告诉内核不执行上面的步骤,即使内核编译了initrd,而是把initrd的数据写到 /dev/initrd,只是这是一个一次性的设备。

01void __init vfs_caches_init(unsigned long mempages)

02{

03    unsigned long reserve;

04

05    /* Base hash sizes on available memory, with a reserve equal to

06           150% of current kernel size */

07

08    reserve = min((mempages - nr_free_pages()) * 3/2, mempages - 1);

09    mempages -= reserve;

10

11    names_cachep = kmem_cache_create("names_cache", PATH_MAX, 0,

12                  SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL);

13

14    dcache_init();

15    inode_init();

16    files_init(mempages);

17    mnt_init();

18    bdev_cache_init();

19    chrdev_init();

20}

第14行为页目录缓存的初始化

第15行索引结点缓存的初始化

第16行文件的初始化

第17行虚拟文件系统挂载的初始化

第18行块设备缓存初始化。

第19行字符设备初始化

01void __init mnt_init(void)

02{

03    unsigned u;

04    int err;

05

06    init_rwsem(&namespace_sem);

07

08    mnt_cache = kmem_cache_create("mnt_cache", sizeof(struct vfsmount),

09                  0, SLAB_HWCACHE_ALIGN | SLAB_PANIC, NULL);

10

11    mount_hashtable = (struct list_head *)__get_free_page(GFP_ATOMIC);

12

13    if (!mount_hashtable)

14           panic("Failed to allocate mount hash table\n");

15

16    printk("Mount-cache hash table entries: %lu\n", HASH_SIZE);

17

18    for (u = 0; u < HASH_SIZE; u++)

19           INIT_LIST_HEAD(&mount_hashtable[u]);

20

21    err = sysfs_init();

22    if (err)

23           printk(KERN_WARNING "%s: sysfs_init error: %d\n",

24                  __func__, err);

25    fs_kobj = kobject_create_and_add("fs", NULL);

26    if (!fs_kobj)

27           printk(KERN_WARNING "%s: kobj create error\n", __func__);

28    init_rootfs();

29    init_mount_tree();

30}

第6行命明空间信号量的初始化

第8行分配空间

第11行挂载点哈希表分配空间

第18行初始化所有的挂载点哈希表。

第25行生成名为fs的kobject对象。

第28行初始化rootfs文件系统

第29行初始化mount树

第一部分 rootfs文件系统的注册

01int __init init_rootfs(void)

02{

03    int err;

04

05    err = bdi_init(&ramfs_backing_dev_info);

06    if (err)

07           return err;

08

09    err = register_filesystem(&rootfs_fs_type);

10    if (err)

11           bdi_destroy(&ramfs_backing_dev_info);

12

13    return err;

14}

第5行初始化

第9行注册rootfs文件系统

1static struct file_system_type rootfs_fs_type = {

2     .name             = "rootfs",

3     .get_sb           = rootfs_get_sb,

4     .kill_sb    = kill_litter_super,

5};

第二部分挂载rootfs文件和创建根目录

01static void __init init_mount_tree(void)

02{

03    struct vfsmount *mnt;

04    struct mnt_namespace *ns;

05    struct path root;

06

07    mnt = do_kern_mount("rootfs", 0, "rootfs", NULL);

08    if (IS_ERR(mnt))

09           panic("Can't create rootfs");

10    ns = kmalloc(sizeof(*ns), GFP_KERNEL);

11    if (!ns)

12           panic("Can't allocate initial namespace");

13    atomic_set(&ns->count, 1);

14    INIT_LIST_HEAD(&ns->list);

15    init_waitqueue_head(&ns->poll);

16    ns->event = 0;

17    list_add(&mnt->mnt_list, &ns->list);

18    ns->root = mnt;

19    mnt->mnt_ns = ns;

20

21    init_task.nsproxy->mnt_ns = ns;

22    get_mnt_ns(ns);

23

24    root.mnt = ns->root;

25    root.dentry = ns->root->mnt_root;

26    set_fs_pwd(current->fs, &root);

27    set_fs_root(current->fs, &root);

28}

这个函数的主要作用是是生成/目录的。

第3行定义一个挂载点

第4行定义一个命名空间

第5行定义一个根路径

第7行挂载rootfs文件系统,返回挂载点

第10行为命名空间分配空间

第13行设定命名空间的引用数为1

第14行初始化命名空间链表

第15行初始化等待对列

第18行命名空间的根结点指向挂载点

第19行挂载点指向命名空间

第21行第一个进程的命名空间第向刚才初始化的。

第24行路径的挂载点为命名空间的根结点

第25行路径的目录为命名空间所指向的挂载点的根目录

第26行设置/目录为当前的目录

第27行设置/目录为根目录

01struct vfsmount * do_kern_mount(const char *fstype, int flags, const char *name, void *data)

02{

03    struct file_system_type *type = get_fs_type(fstype);

04    struct vfsmount *mnt;

05

06    if (!type)

07           return ERR_PTR(-ENODEV);

08    mnt = vfs_kern_mount(type, flags, name, data);

09

10    if (!IS_ERR(mnt) && (type->fs_flags & FS_HAS_SUBTYPE) &&

11        !mnt->mnt_sb->s_subtype)

12           mnt = fs_set_subtype(mnt, fstype);

13    put_filesystem(type);

14    return mnt;

15}

do_kern_mount的参数介绍

fstype 要安装的文件系统的类型名

flag   安装的标志

name  存放文件系统的块设备的路径名

data    指向传递给文件系统中read_super方法的附加指针

第3行得到文件系统的类型,这里是rootfs,当然也会有其它的文件系统,比如proc,pipefs等

第8行返回挂载点

第13行增加对文件系统的引用

01struct vfsmount *

02vfs_kern_mount(struct file_system_type *type, int flags, const char *name, void *data)

03{

04    struct vfsmount *mnt;

05    char *secdata = NULL;

06    int error;

07

08    if (!type)

09           return ERR_PTR(-ENODEV);

10

11    error = -ENOMEM;

12    mnt = alloc_vfsmnt(name);

13    if (!mnt)

14           goto out;

15

16    if (data && !(type->fs_flags & FS_BINARY_MOUNTDATA)) {

17           secdata = alloc_secdata();

18           if (!secdata)

19                  goto out_mnt;

20

21           error = security_sb_copy_data(data, secdata);

22           if (error)

23                  goto out_free_secdata;

24    }

25

26    error = type->get_sb(type, flags, name, data, mnt);

27    if (error < 0)

28           goto out_free_secdata;

29    BUG_ON(!mnt->mnt_sb);

30

31   error = security_sb_kern_mount(mnt->mnt_sb, flags, secdata);

32   if (error)

33          goto out_sb;

34

35    mnt->mnt_mountpoint = mnt->mnt_root;

36    mnt->mnt_parent = mnt;

37    up_write(&mnt->mnt_sb->s_umount);

38    free_secdata(secdata);

39    return mnt;

40out_sb:

41    dput(mnt->mnt_root);

42    deactivate_locked_super(mnt->mnt_sb);

43out_free_secdata:

44    free_secdata(secdata);

45out_mnt:

46    free_vfsmnt(mnt);

47out:

48    return ERR_PTR(error);

49}

第4行定义挂载点

第12行分配一个新的已安装文件系统的描述符,存放在局部变量mnt中

第26行调用文件系统get_sb回调函数,这里是rootfs_get_sb,来初始化一个新的超级块,同时会创建/目录.后面会单独介绍

第35行挂载点根目录指向与文件系统根目录对应的目录项对象的地址

第36行挂载点父目录指向自己

第39行返回局部变量mnt


第三部分解压initramfs文件系统中的内容到rootfs

01static int __init populate_rootfs(void)

02{

03    char *err = unpack_to_rootfs(__initramfs_start,

04                  __initramfs_end - __initramfs_start);

05    if (err)

06           panic(err);      /* Failed to decompress INTERNAL initramfs */

07    if (initrd_start) {

08#ifdef CONFIG_BLK_DEV_RAM

09           int fd;

10           printk(KERN_INFO "Trying to unpack rootfs image as initramfs...\n");

11           err = unpack_to_rootfs((char *)initrd_start,

12                  initrd_end - initrd_start);

13           if (!err) {

14                  free_initrd();

15                  return 0;

16           } else {

17                  clean_rootfs();

18                  unpack_to_rootfs(__initramfs_start,

19                         __initramfs_end - __initramfs_start);

20           }

21           printk(KERN_INFO "rootfs image is not initramfs (%s)"

22                         "; looks like an initrd\n", err);

23           fd = sys_open("/initrd.image", O_WRONLY|O_CREAT, 0700);

24           if (fd >= 0) {

25                  sys_write(fd, (char *)initrd_start,

26                                initrd_end - initrd_start);

27                  sys_close(fd);

28                  free_initrd();

29           }

30#else

31           err = unpack_to_rootfs((char *)initrd_start,

32                  initrd_end - initrd_start);

33           if (err)

34                  printk(KERN_EMERG "Initramfs unpacking failed: %s\n", err);

35           free_initrd();

36#endif

37    }

38    return 0;

39}

第3行解压initramfs文件到rootfs文件系统中,第一个参数为开始位置,第二个参数为长度。

第7行initrd_start值为0,下面不执行

01static int __init kernel_init(void * unused)

02{

03    ............

04    do_basic_setup();

05    /*

06    * check if there is an early userspace init.  If yes, let it do all

07    * the work

08    */

09

10    if (!ramdisk_execute_command)

11           ramdisk_execute_command = "/init";

12

13    if (sys_access((const char __user *) ramdisk_execute_command, 0) != 0) {

14           ramdisk_execute_command = NULL;

15   

16    prepare_namespace();

17    }

18

19    /*

20    * Ok, we have completed the initial bootup, and

21    * we're essentially up and running. Get rid of the

22    * initmem segments and start the user-mode stuff..

23    */

24

25    init_post();

26    return 0;

27}

第4行初始化init段里面的函数

第10行判断ramdisk_execute_command的值,如果为空,就给它赋于/init,这个也是启动是第一个进程。

第13行如果这个init个程序,访问它,其实也就是initramfs里面解压出来有这个程序的话,就不用再执行下面的函数,用这个init进程可以挂载真正的根文件系统。

第16行为进程0准备命名空间

第25行进行真正根文件系统中的init进程运行

第四部分准备命名空间,挂载flash上的根文件系统

01void __init prepare_namespace(void)

02{

03    int is_floppy;

04    if (root_delay) {

05           printk(KERN_INFO "Waiting %dsec before mounting root device...\n",

06                  root_delay);

07           ssleep(root_delay);

08    }

09

10    /*

11    * wait for the known devices to complete their probing

12    *

13    * Note: this is a potential source of long boot delays.

14    * For example, it is not atypical to wait 5 seconds here

15    * for the touchpad of a laptop to initialize.

16    */

17    wait_for_device_probe();

18

19    md_run_setup();

20

21    if (saved_root_name[0]) {

22           root_device_name = saved_root_name;

23           if (!strncmp(root_device_name, "mtd", 3) ||

24               !strncmp(root_device_name, "ubi", 3)) {

25                  mount_block_root(root_device_name, root_mountflags);

26                  goto out;

27           }

28           ROOT_DEV = name_to_dev_t(root_device_name);

29           if (strncmp(root_device_name, "/dev/", 5) == 0)

30                  root_device_name += 5;

31}

32

33    if (initrd_load())

34                  goto out;

35

36    /* wait for any asynchronous scanning to complete */

37    if ((ROOT_DEV == 0) && root_wait) {

38           printk(KERN_INFO "Waiting for root device %s...\n",

39                  saved_root_name);

40           while (driver_probe_done() != 0 ||

41                  (ROOT_DEV = name_to_dev_t(saved_root_name)) == 0)

42                  msleep(100);

43           async_synchronize_full();

44    }

45

46    is_floppy = MAJOR(ROOT_DEV) == FLOPPY_MAJOR;

47

48    if (is_floppy && rd_doload && rd_load_disk(0))

49           ROOT_DEV = Root_RAM0;

50

51    mount_root();

52out:

53    sys_mount(".", "/", NULL, MS_MOVE, NULL);

54    sys_chroot(".");

55}

第21行检查saved_root_name是否为真,这里是有值的。

第22行把它赋给root_device_name,此时为/dev/mtdblock3

第23-27行不执行

第25行挂载根文件系统

第28行名字到设备号的转变,这个设备号是设备的设备号,下面会用到的。我这里是flash的第三个分区

第29行/dev/mtdblock3的前5个字符与/dev/比较,这里是相等的。

第30行root_device_name加5,所以此时root_device_name为mtdblock3.

第37-44行由于设备号不为0,所以这里面没有执行。

第53行移动根文件系统的根目录为/

01void __init mount_block_root(char *name, int flags)

02{

03    char *fs_names = __getname();

04    char *p;

05#ifdef CONFIG_BLOCK

06    char b[BDEVNAME_SIZE];

07#else

08    const char *b = name;

09#endif

10

11    get_fs_names(fs_names);

12retry:

13    for (p = fs_names; *p; p += strlen(p)+1) {

14           int err = do_mount_root(name, p, flags, root_mount_data);

15           switch (err) {

16                  case 0:

17                         goto out;

18                  case -EACCES:

19                         flags |= MS_RDONLY;

20                         goto retry;

21                  case -EINVAL:

22                         continue;

23           }

24            /*

25           * Allow the user to distinguish between failed sys_open

26           * and bad superblock on root device.

27           * and give them a list of the available devices

28           */

29#ifdef CONFIG_BLOCK

30           __bdevname(ROOT_DEV, b);

31#endif

32           printk("VFS: Cannot open root device \"%s\" or %s\n",

33                         root_device_name, b);

34           printk("Please append a correct \"root=\" boot option; here are the available partitions:\n");

35

36           printk_all_partitions();

37#ifdef CONFIG_DEBUG_BLOCK_EXT_DEVT

38           printk("DEBUG_BLOCK_EXT_DEVT is enabled, you need to specify "

39                  "explicit textual name for \"root=\" boot option.\n");

40#endif

41           panic("VFS: Unable to mount root fs on %s", b);

42    }

43

44    printk("List of all partitions:\n");

45    printk_all_partitions();

46    printk("No filesystem could mount root, tried: ");

47    for (p = fs_names; *p; p += strlen(p)+1)

48           printk(" %s", p);

49    printk("\n");

50#ifdef CONFIG_BLOCK

51    __bdevname(ROOT_DEV, b);

52#endif

53    panic("VFS: Unable to mount root fs on %s", b);

54out:

55    putname(fs_names);

56}

第3行申请空间

第11行fs_name指向内核里面编译文件系统的第一个

第13行循环把这些文件系统挂到根目录下,我的内核里面分别有,ext3,ext3,vfat等,这里用到的是yaffs文件系统

第14行调用do_mount_root函数进行挂载

第15行返回的结果0

第17行增加对文件系统的引用

01static int __init do_mount_root(char *name, char *fs, int flags, void *data)

02{

03    int err = sys_mount(name, "/root", fs, flags, data);

04    if (err)

05           return err;

06

07    sys_chdir("/root");

08    ROOT_DEV = current->fs->pwd.mnt->mnt_sb->s_dev;

09    printk("VFS: Mounted root (%s filesystem)%s on device %u:%u.\n",

10           current->fs->pwd.mnt->mnt_sb->s_type->name,

11           current->fs->pwd.mnt->mnt_sb->s_flags & MS_RDONLY ?

12           " readonly" : "", MAJOR(ROOT_DEV), MINOR(ROOT_DEV));

13    return 0;

14}

第3行挂载文件系统,这里的name为/dev/root,fs为文件的类型,这里是yaffs2,文件类型当然还是ext3,ext2等。

第7行改变到/root目录下

第9行分别显示出挂载出根文件系统和主次设备号,这里是yaffs文件系统,主:次31:3

01void __init mount_root(void)

02{

03#ifdef CONFIG_ROOT_NFS

04    if (MAJOR(ROOT_DEV) == UNNAMED_MAJOR) {

05           if (mount_nfs_root())

06                  return;

07

08           printk(KERN_ERR "VFS: Unable to mount root fs via NFS, trying floppy.\n");

09           ROOT_DEV = Root_FD0;

10    }

11#endif

12#ifdef CONFIG_BLK_DEV_FD

13    if (MAJOR(ROOT_DEV) == FLOPPY_MAJOR) {

14           /* rd_doload is 2 for a dual initrd/ramload setup */

15           if (rd_doload==2) {

16                  if (rd_load_disk(1)) {

17                         ROOT_DEV = Root_RAM1;

18                         root_device_name = NULL;

19                  }

20           } else

21                  change_floppy("root floppy");

22    }

23#endif

24#ifdef CONFIG_BLOCK

25    create_dev("/dev/root", ROOT_DEV);

26    mount_block_root("/dev/root", root_mountflags);

27#endif

28}

这里只执行了CONFI_BLOCK宏开关

第25行创建设备结点,这里我在ubutu上测试了一下,/dev/root是的连接是hda1

第26行挂载根文件系统

第五部分运行真正根目录中的init程序

01static noinline int init_post(void)

02    __releases(kernel_lock)

03{

04    /* need to finish all async __init code before freeing the memory */

05    async_synchronize_full();

06    free_initmem();

07    unlock_kernel();

08    mark_rodata_ro();

09    system_state = SYSTEM_RUNNING;

10    numa_default_policy();

11

12    if (sys_open((const char __user *) "/dev/console", O_RDWR, 0) < 0)

13           printk(KERN_WARNING "Warning: unable to open an initial console.\n");

14

15    (void) sys_dup(0);

16    (void) sys_dup(0);

17

18    current->signal->flags |= SIGNAL_UNKILLABLE;

19

20    if (ramdisk_execute_command) {

21           run_init_process(ramdisk_execute_command);

22           printk(KERN_WARNING "Failed to execute %s\n",

23                         ramdisk_execute_command);

24    }

25

26    /*

27    * We try each of these until one succeeds.

28    *

29    * The Bourne shell can be used instead of init if we are

30    * trying to recover a really broken machine.

31    */

32    if (execute_command) {

33           run_init_process(execute_command);

34           printk(KERN_WARNING "Failed to execute %s.  Attempting "

35                                "defaults...\n", execute_command);

36    }

37    run_init_process("/sbin/init");

38    run_init_process("/etc/init");

39    run_init_process("/bin/init");

40    run_init_process("/bin/sh");

41

42    panic("No init found.  Try passing init= option to kernel.");

43}

第12行打开/dev/console设备文件

第15-16行将文件描述符0复制给文件描述符1和2。

第20-24行ramdisk_execute_command变量中如果指定了要运行的程序,就开始运行这个程序,在u-boot的命令行参数中会指定rdinit=…,这个时候ramdisk_execute_command等于这个参数指定的程序。也可能/init程序存在,哪么ramdisk_execute_command就等于/init,或者为空。本程序没有指定,所以这里为空。

第37行执行/sbin/init程序,这个程序存在于根文件系统,如果存在,执行它,系统的控制权交给/sbin/init,不再返回init_post函数


http://www.linuxidc.com/Linux/2011-10/45448.htm


本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Linux 根文件系统的挂载分析 的相关文章

  • 初学树莓派——(六)树莓派安装OpenCV及USB摄像头配置

    目录 1 安装OpenCV 1 1前言 1 2换源及源内容更新 1 3安装依赖 1 4下载whl包 1 5安装OpenCV 1 6检查安装 2 USB摄像头配置 同时检查OpenCV安装情况 2 1前言 2 2Python调用cv2库来检查
  • 使用嵌入式linux完全手册光盘的arm-linux-gcc 遇到问题 自己编译

    Redhat9下重新生成交叉编译器gcc 3 4 5 glibc 2 3 6 看到论坛上有兄弟也遇到 arm linux gcc lib tls libc so 6 version GLIBC 2 4 not found required
  • QT基础学习(12)---事件过滤

    文章目录 事件过滤 一 事件过滤 实现该功能的方法就是在目标部件 自定义的图片显示部件 上注册事件过滤器 此时的事件过滤器就是我们所说的监视对象 完成这些步骤之后 当目标部件有事件产生后 首先会传递给监视对象 事件过滤器 进行处理而不是该事
  • 嵌入式学习:stm32学习路线推荐之思维导图

    从9月1日开始学习STM32后 对于STM32的一些个人总结 1 对于STM32和51的区别 对于 STM32来说 基本的大概都和51单片的内容相似 但是由于STM的引脚和寄存器的数量较多 所以需要一个更加完善的管理机制 导致了 时钟 的产
  • STM32的CAN过滤器

    最近开始给足底压力设备加外设 这里外设个主设备之间通过can总线连接 之前使用过can总线 但是对can的过滤器不是很理解 所以这里就借机整理一下 原文地址 再谈STM32的CAN过滤器 bxCAN的过滤器的4种工作模式以及使用方法总结 S
  • Qt creator4.8.0 以上使用SqLite数据库进行数据操作

    文章目录 前言 一 在 pro工程文件中添加sql模块 二 使用步骤 1 添加头文件 2 链接并打开数据库 3 创建用户信息表management info 4 插入数据操作 5 修改数据库操作 6 查询数据库 总结 前言 Qt creat
  • modbus总线协议(一)modbus rtu

    一 介绍 Modbus协议由Modicon公司开发出来 现在Modbus是工业领域全球最流行的协议 硬件支持RS 232 RS 422 RS 485和以太网设备 应用在PLC DCS 智能仪表等工控领域 图片来源于网络 二 modbus协议
  • main.c(31): warning: #223-D: function “uart_init“ declared implicitly

    Keil5编程之warning 223 D function xxx declared implicitly 1 函数没有头文件中进行声明 在头文件中添加声明 2 定义错误 字母大小可能不一致 仔细看一下出现问题的函数是否在声明和调用时使用
  • begin to drop messages due to rate-limiting

    对于syslog保存的日志会有很多重要信息 但是一旦打印的日志数量超过设置的阈值 就会丢掉 imuxsock pid 48 begin to drop messages due to rate limiting 这是在调试时不愿看到的 可以
  • MSP430嵌入式接口编程(惯性测量单元温湿度双音多频磁力计LCD显示等)

    Energia IDE编程MSP430 GPIO 串口通讯 定时中断 添加库 嵌入式器件接口编程 加速度计 include
  • Linux shell中if [ $? -eq 0 ] 语句作用:判断命令是否执行成功

    shell脚本中 是指上一次命令执行的成功或者失败的状态 如果成功就是0 失败为1 语句if eq 0 是判断if语句的上一个命令执行如果成功就执行if中的语句 否则就执行else中的内容 note 使用时要注意 if后面的中括号 eq 0
  • U-Boot启动过程完全分析

    1 1 U Boot工作过程 U Boot启动内核的过程可以分为两个阶段 两个阶段的功能如下 1 第一阶段的功能 硬件设备初始化 加载U Boot第二阶段代码到RAM空间 设置好栈 跳转到第二阶段代码入口 2 第二阶段的功能 初始化本阶段使
  • 在vmware里面看不到已经设置的共享文件夹

    查看你是否设置了共享文件夹 vmware hgfsclient 在上图的虚拟机点击安装vmware tools 之后会在vmware tools文件里面有一个压缩的文件 把它复制到自己创的文件夹并解压 自己创建文件夹使用mkdir p mo
  • 解决Keil调试模式下无法设置断点的问题

    问题描述 使用Keil打开工程文件 进入调试模式后 只有main c文件里面可以设置断点 其余文件都不可以设置断点 可能的原因及解决方案 原因1 工程路径包含中文 解决方案1 更换为全英文路径 原因2 工程没有全部Rebuild 解决方案2
  • STM32F103C8移植uCOSIII(HAL库)

    少年 一 随笔 二 uCOSIII源码 三 项目导入文件整理 四 导入文件和增加头函数路径 五 代码改动 六 参考资料 一 随笔 移植一个嵌入式系统用了一天时间 只能说不愧是我 在不了解的情况下还是少一些自己的操作 自己挖坑埋自己 记录一下
  • stm32 HAL库 Flash操作简介

    stm32 HAL库 Flash操作简介 目录 第一stm32 flash介绍 查看代码段 以判断代码长度 flash的基本操作规则 stm32 HAL库 Flash操作指南 stm32f1xx hal flash c stm32f1xx
  • 32位的Ubuntu16.04安装QT5.8,并编译实现window环境下的代码

    开始 这是本人的第一篇博客 自己经常在网上找问题的解决方法 发现有些问题很难找到合适的解决方法 所以自己也写写 希望能帮助到大家 有错的地方大家指出 安装环境 linux环境 Ubuntu16 04 32位系统 使用apt get命令进行安
  • 计算机基础:一颗芯片是怎样诞生的?

    一颗芯片是怎样诞生的 芯片属于半导体 半导体是介于导体和绝缘体之间的一类物质 元素周期表中硅 锗 硒 硼的单质都属于半导体 这些单质通过掺杂其他元素生成的一些化合物 也属于半导体的范畴 这些化合物在常温下可激发载流子的能力大增 导电能力大大
  • Java的引用类型有几种?区别是什么?

    nbsp Java的引用类型有几种 区别是什么 在开始前我有一些资料 是我根据自己从业十年经验 熬夜搞了几个通宵 精心整理了一份 Java的资料从专业入门到高级教程 工具包 点个关注 全部无偿共享给大家 在评论区回复 888 之后私信回复
  • 跟着野火学FreeRTOS:第一段(任务定义,切换以及临界段)

    在裸机系统中 系统的主体就是 C P U CPU CP U 按照预先设定的程序逻辑在 m a i n

随机推荐

  • ubutu下ros2实现小车仿真建模与目标检测

    1 安装ros2 这里使用小鱼的一键安装 根据自己的喜好安装 博主用的是ros2的foxy版本 wget http fishros com install O fishros fishros 2 下载代码 这里使用的是古月居的代码 http
  • 用Requests和正则表达式爬取豆瓣图书TOP250

    思路和上文大同小异 import requests from requests exceptions import RequestException import re import json headers User Agent Mozi
  • vue props设置默认值的基本方式

    vue官方文档解释 为该 prop 指定一个默认值 如果该 prop 没有被传入 则换做用这个值 对象或数组的默认值必须从一个工厂函数返回 默认值为字符串 props fieldString type String default 默认值为
  • Python如何绘制误差直方图?曲线的阴影图?shade range graph

    问题抽象 Python如何绘制曲线的阴影图 建议的方案 flights sns load dataset flights flights head sns lineplot data flights x year y passengers
  • 基于stm32蓝牙接收信息并显示在OLED上

    两个单片机基于蓝牙连接通信 可以使能串口 把蓝牙当作串口来使用 例如 蓝牙通过串口发送信息 蓝牙通过串口接收信息 这样就可以使两个单片机进行通信 下面代码是基于STM32蓝牙接收另外一块单片机蓝牙发送过来的温湿度信息 并显示在OLED上 主
  • Squid代理的用户认证(基本认证、集成Windows域认证)

    Squid代理的用户认证 基本认证 集成Windows域认证 2012 07 02 TsengYia 126 com 关于Squid代理服务的用户验证 本文简要介绍了两种方法的实现 basic基本认证 ntlm域认证 basic认证采用账户
  • springboot读取resources目录下文件

    文章目录 前言 1 问题过程 2 解决方案 2 1 文件上传 2 2 ClassPathResource 总结 前言 最近的工作中遇到了复杂的excel报表导出业务 采用的是用excel模板来实现该业务 可以规避大量勾画excel格式的代码
  • 静态测试及评审、测试用例

    7 1静态测试的定义 特点 静态测试通常是指不执行程序代码而寻找代码中可能存在的错误或评估程序代码的过程 其被测对象是各种与软件相关的有必要进行测试的产物 例如各类文档 源代码等 特点 1 不必动态地运行程序 2 可以人工进行 充分发挥人的
  • 8分钟丨教你玩转 API

    欢迎大家前往腾讯云 社区 获取更多腾讯海量技术实践干货哦 本文由织云平台团队发表于云 社区专栏 背景 当下 业界越来越多公司在项目架构设计时 会采用微服务架构 微服务架构 可以让我们的产品有更好的扩展性 更好的伸缩性 但同时也会带来微服务的
  • Gerber文件的输出

    Step 01 首先打开PCB文件 Step 02 文件 制造输出 Gerber Files Step 03 通用设置 Step 04 层设置 如果有Keep out layter层勾选了 把他去掉 Step 05 钻孔图层设置 勾选钻孔图
  • MIPI-DSI协议解析——DCS命令集

    MIPI协议族 定义了一个专门用于显示的命令集 叫做Display Command Set 简称为DCS 这个DCS起什么作用呢 主要是Host和Display之间的一些Command配置和数据传输 以及读Display的数据等 使用过SP
  • 每日一练(代写匿名信-查找字符串)

    问题描述 小Q想要匿名举报XX领导不务正业 小Q害怕别人认出他的字迹 他选择从报纸上剪裁下来英文字母组成自己的举报信 现在小Q找来了报纸 和自己的举报信的Txt 小Q有急事 小Q跑去上厕所了 小Q让你帮他完成 小Q为什么这么坑 工作之前你当
  • 实现字符计数功能

    方法二 使用collections模块 使用Python内置的collections模块 其中Counter类可以快速实现字符计数功能 python from collections import Counter def char coun
  • 区块链主流币数据 API数据接口

    主流币数据 计费模式 免费额度 点数单价 每日限制 会员免费 100次 免费 10000次 更新时间 2022 07 11 02 48 03接口状态 正常 返回区块链主流币的价格等信息 请求地址 HTTPGET POST https www
  • stm32之ADC oled显示

    六路adc的显示结果 一些基本的代码 初始化ADC 初始化ADC void Adc Init void GPIO InitTypeDef GPIO InitStructure ADC CommonInitTypeDef ADC Common
  • 华为OD机试 - 数组二叉树(Java)

    题目描述 二叉树也可以用数组来存储 给定一个数组 树的根节点的值存储在下标1 对于存储在下标N的节点 它的左子节点和右子节点分别存储在下标2 N和2 N 1 并且我们用值 1代表一个节点为空 给定一个数组存储的二叉树 试求从根节点到最小的叶
  • 一行代码可以做些什么?

    点击蓝字 关注我们 众所周知 Python 是目前流行和易学的编程语言 2021 年 Python 获得了 TIOBE 编程指数的年度最受欢迎的编程语言 此外 它也连续蝉联了多个月的榜首 更值得一提的是 一些繁琐的任务 往往都可以用 Pyt
  • hadoop中的两个datanode节点的VERSION文件冲突,导致其中有一个datanode无法启动

    问题 分析 本来是有三个datanode才对 所以有一个datanode丢失 查看丢失的datanode的log日志 第一个报错 是datanode无法启动的报错日志 第二个报错 是因为datanode丢失 数据无法上传的报错 很奇怪的是
  • 2020年前端开发工具大全:50款热门的前端工具汇总

    今天跟大家分享一些目前比较热门新鲜度靠前的50款前端工具 希望对你有所帮助 下面和千锋广州小编一起来看看吧 一 构建工具 1 Parcel 地址 https parceljs org Parcel是一款极速零配置WEB应用打包工具 快速 几
  • Linux 根文件系统的挂载分析

    在介绍根文件系统挂载之前先介绍一些基础知识 initramfs 当linux内核启动后 会找到并执行第一个用户程序 一般是init 这个程序存在于文件系统当中 文件系统存在于设备上 但不知道init存在哪个设备上 于是有了内核命令列选项ro