物联网4(嵌入式系统启动过程、U-Boot、Linux内核、根文件系统)

6.5.1.Menuconfig工具操作说明
按键描述
上下方向键切换菜单
左右方向键切换Menuconfig工具底部、、三个按钮
Enter键确认键,起到确认作用
高亮字母如上图菜单和底部按钮的第一个字母是高亮。在键盘上按下对应高亮字母键,可直接选择对应的菜单或底部按钮
空格键选择目标菜单后按空格键切换启用、禁用和可加载模块(启用是指菜单对应的功能会被编译;禁用是指菜单对应功能不会被编译;可加载模块是指将菜单对应的功能编译为可加载模块,可加载模块特点在于功能不被编译进镜像,在编译时会被单独处理,生成独立的文件(通常以.ko为扩展名)。在需要这个功能时,可通过insmod、rmmod或modprobe等命令将这个功能动态地加载进内核;而不需要时,可以将它从内存中卸载掉,从而避免功能驻留在内存中,造成内存浪费)。启用、禁用和可加载模块对应的快捷键分别是Y、N和M
双击Esc键放回上一级或退出Menuconfig工具。功能同底部键
?键查看相应菜单的帮助信息。功能同底部键
/键按下/键后进入搜索界面,然后输入文字进行搜索
其它说明: [ * ]和< * >:表示启用 [ ]和< >:表示禁用 < M >:表示可加载模块
6.5.2.定制Linux内核步骤

第1步:执行sudo apt-get install libncurses5-dev命令安装ncurses库(ncurses库为Menuconfig工具提供界面支持)

第2步:在顶层目录下输入make menuconfig命令打开Menuconfig工具。注意:若报Your display is too small to run Menuconfig!错误,则说明终端窗口太小,请调大终端窗口

第3步:根据自己的需求按照“Menuconfig工具操作说明”章节操作菜单

第4步:退出Menuconfig工具,在退出时请选择“yes”按钮保存配置,如下图:

保存成功后,会将配置结果更新于顶层目录的.config文件中,以便编译时使用。

第5步:编译并烧写

6.6.添加Linux内核功能

为满足特定的需求,用户可以根据需求编写代码添加到Linux内核中。

6.6.1.过程

第1步:在相应目录中创建一个C源文件

第2步:在C源文件中编写功能代码

第3步:在C源文件所在目录中的Kconfig文件中添加配置选项代码

  • 备注1:若目录中没有Kconfig文件,则需要创建Kconfig文件
  • 备注2:Kconfig文件的语法规则参考“Kconfig系统”章节
  • 备注3:此步骤可选,若想要此功能像“定制Linux内核”章节介绍的一样可配置,则需要此步骤,否则可忽略此步骤

第4步:在C源文件所在目录中的Makefiel文件中添加如下代码:

obj-y += <创建的.c文件名>.o
或
obj-$(CONFIG_<第②步添加的配置选项标识>) += <创建的.c文件名>.o

  • 备注1:若在Makefile文件中添加的是上述第一句代码,则创建的C源文件会被无条件的编译
  • 备注2:若在Makefile文件中添加的是上述第二句代码,则创建的C源文件是否被编译由第3步添加的配置选项控制

第5步:编译并烧写

第6步:测试是否添加成功

6.6.2.示例:添加Beep驱动

第1步:在drivers/char目录中创建一个beep_driver.c文件

第2步:在beep_driver.c文件中编写功能代码,代码如下:

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/gpio.h>
#include <linux/io.h>
#include <mach/gpio.h>
#include <linux/delay.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/device.h>
#include <mach/soc.h>
#include <mach/platform.h>

#define LED_0 0

struct class *beep_class;
struct device *beep_device;
static int major = 0;

static int beep_open(struct inode *pnode, struct file *filp)
{
	return 0;
}

static ssize_t beep_read(struct file *filp, char __user *buff, size_t count, loff_t *offp)
{
	int value = 0;
	
	if(count!=1)
		return -1;
	if(nxp_soc_gpio_get_io_dir(PAD_GPIO_C+14))
		value = nxp_soc_gpio_get_out_value(PAD_GPIO_C+14);
	else
		value = nxp_soc_gpio_get_in_value(PAD_GPIO_C+14);

	return sizeof(int)-copy_to_user(buff,&value,sizeof(int));
}

static ssize_t beep_write(struct file *pnode,const char __user *buff,size_t count,loff_t *offp)
{
	int value = 0;
	int len = 0;
	
	if(count>sizeof(int))
		return -1;
	len = copy_from_user(&value,buff,sizeof(int));
 	nxp_soc_gpio_set_out_value(PAD_GPIO_C+14,(!(!value)));
	
	return (1-len);
}

static long beep_ioctl(struct file *filp, unsigned int cmd, unsigned long data)
{
	if(cmd)
		nxp_soc_gpio_set_out_value(PAD_GPIO_C+14,1);
	else
		nxp_soc_gpio_set_out_value(PAD_GPIO_C+14,0);
	
	return 0;
}

static int beep_release(struct inode *pnode, struct file *filp)
{
	return 0;
}

static struct file_operations fops = {
	.owner = THIS_MODULE,
	.read = beep_read,
	.write = beep_write,
	.open = beep_open,
	.unlocked_ioctl = beep_ioctl,
	.release =  beep_release,
};


static int __init beep_init(void) 
{
	major = register_chrdev(0,"vmotor_drv", &fops);
	if(major<0)
	{
		printk(KERN_CRIT "error register_chrdev\n");
		goto err_reg;
	}
	beep_class=class_create(THIS_MODULE, "vmotor_cls");
	if(IS_ERR(beep_class))
	{
		printk(KERN_CRIT "error class_create\n");
		goto err_cls;
	}
	beep_device=device_create(beep_class, NULL,MKDEV(major,0),NULL,"vmotor");
	if(IS_ERR(beep_device))
	{
		printk(KERN_CRIT "error device_create\n");
		goto err_dev;
	}
	nxp_soc_gpio_set_io_dir(PAD_GPIO_C+14, 1);

	
	return 0;
err_dev:
	class_destroy(beep_class);
err_cls:
	unregister_chrdev(major,"vmotor_drv");
err_reg:
	return -1;
};

static void __exit beep_exit(void) 
{
	unregister_chrdev(major,"vmotor_drv");
	device_destroy(beep_class,MKDEV(major,0));
	class_destroy(beep_class);

	return;
};

module_init(beep_init);
module_exit(beep_exit);
MODULE_LICENSE("GPL");

第3步:在drivers/char目录中的Kconfig文件中添加如下代码

config BEEP_DRIVER
	bool "Provide the on and off interface for the Beep"
	default y

第4步:在drivers/char目录中的Makefiel文件中添加如下代码

obj-$(CONFIG_BEEP_DRIVER) += beep_driver.o

第5步:编译并烧写

第6步:测试

  • 第①步:在Ubuntu中创建beep.c文件,代码如下。此代码用于调用beep_driver.c提供的接口,以便测试“beep驱动”是否添加成功
#include <stdio.h>
#include <fcntl.h>
#include <linux/fb.h>
#include <sys/mman.h>
#include <string.h>
#include <asm/ioctl.h>
#include <linux/input.h>

#define BCT3288A_CLOSE  _IO('B',1)
#define BCT3288A_CLEAR  _IO('B',2)

int main(int argc,char *argv[])
{
	int fd;
	int value;
	unsigned char buf[2];
	
	fd = open("/dev/beep",O_RDWR);
	if(fd<0)
	{
		perror("dev tain open:");
		return 1;
	}
	if(argc > 0)
	{
		value = atoi(argv[1]);
	}

	ioctl(fd,value);
	close(fd);
	
	return 0;
}

  • 第②步:交叉编译beep.c文件,执行交叉编译的代码如下
arm-linux-gcc -o beep beep.c
  • 第③步:通过nfs、U盘或SD卡等方式将交叉编译生成的beep文件拷贝到开发板
  • 第④步:在开发板的用户模式下执行如下命令
./beep 1        // 此命令会使开发板开启Beep响
./beep 0        // 此命令会使开发板关闭Beep响

6.7.Kconfig系统

Kconfig系统是Linux内核的一个配置系统,方便用户通过配置界面浏览和修改内核的选项,如上述“Linux内核选项裁剪”章节就是通过Kconfig系统的配置界面选中或取消选中相应选项来决定对应功能是否被编译。

Kconfig系统通过Kconfig文件来定义和描述内核中的选项,这些Kconfig通常位于内核源码的各子目录中,如顶层目录的Kconfig文件是Kconfig系统的入口点,arch/arm/Kconfig文件用于定义ARM架构相关的选项,driver/char/Kconfig文件用于定义字符设备驱动的选项。

6.7.1.Kconfig文件的语法
关键字描述
#注释
source作用:引用Kconfig文件,相当于C语言的include。父目录中的Kconfig文件通常使用source关键字引用其子目录中的Kconfig文件。 格式:source <Kconfig文件名> 示例:source “arch/arm/Kconfig”
menu/endmenu作用:定义一个菜单。menu和endmenu两个关键字成对出现,选项写在两个关键字之间 格式:menu <菜单名称>                   # 选项           endmenu 示例:menu “First Menu”                   config FIRST_CONFIG                           bool “Enable first config”                             config SECOND_CONFIG                           bool “Enable second config”                          default n            endmenu
config作用:选项的标识。 格式:config <标识名称> 示例:config FIRST_CONFIG
bool tristate string hex int作用:定义选项的类型 类型:         bool:布尔类型,使用此类型选项只能选中(即功能被编译进镜像)或不选中(即功能不被编译)。         tristate:三态类型,使用此类型的选项只能选中(即功能编译进镜像)、不选中(即功能不被编译)或选择为M(即功能编译成模块。)。         string:字符串类型,使用此类型的选项需要输入字符串。         hex:十六进制类型,使用此类型的选项需要输入十六进制。         int:整型类型,使用此类型的选项需要输入整数。
prompt作用:定义选项在配置界面上显示的文本,用于介绍选项,或可以理解为选项的标题 格式:prompt <文本>           也可以省略“prompt”关键字,<文本>直接写在选项类型后面 示例:bool                   prompt “用于介绍选项”                  或                  bool “用于介绍选项” 注意:若选项没有定义prompt,且类型后面没有写<文本>,则配置界面不会显示此选项
default作用:设置选项的默认值           bool类型的值可为y(选中)和n(不选中)           tristate类型的值可为y(选中)、n(不选中)和m(编译成模块) 格式:default <值> 示例:bool                   default y           tristate m                   default m           string                   default hellowrold           hex                   default 0x12           int                   default 10
help作用:用于详细描述选项,帮助用户理解该选项的作用 格式:help           <描述选项的文本>
depends on用:用于指定选项依赖于另一个选项。如果依赖的选项没有被选中,那么此选项将不会显示 格式:depends on <依赖的选项的标识> 示例:config PARENT                   bool “被依赖的配选项”            config CHILD                   bool “子选项”                   depends on PARENT
select作用:当一个选项被选中时,自动选中另一个选项 格式:select <要自动选中的选项的标识> 示例:config ACTIVE_SELECTED                   bool “手动选中的选项”                   select AUTO_SELECTED           config AUTO_SELECTED                   bool “自动选中的选项”
choice/endchoice作用:定义一组选项,用户只能从组中选择一个选项 格式:choice                    prompt <组介绍文本>                    # 选项            endchoice 示例:choice                    prompt “选项组”                    config GROUP_OPTION_ONE                            bool “组成员1”                    config GROUP_OPTION_TWO                            bool “组成员2”                    config GROUP_OPTION_THREE                            bool “组成员3”                    config GROUP_OPTION_FOUR                            bool “组成员4”            endchoice

上表关键字只是Kconfig语法的部分关键字,其它关键字请自行百度

6.7.2.Kconfig系统示例

第1步:创建myconfig/Kconfig文件

第2步:在顶层目录中的Kconfig文件添加source “myconfig/Kconfig”

第3步:在“myconfig/Kconfig”文件中编写代码,代码如下:

menu "Example Menu"                           # 定义一个菜单

    config FIRST                              # 创建一个标识为FIRST的选项
        bool "Bool类型选项"                    # 定义选项的类型为bool,且标题为“Bool类型选项”
        default y                             # 选项的默认值为 y

    config SECOND
        tristate "Tristate类型选项"            # 定义选项的类型为tristate
        default m                             # 选项的默认值为 m

    config THIRD
        string "String类型选项"                # 定义选项的类型为string
        default third                         # 选项的默认值为 third

    config FOURTH
        hex "Hex类型选项"                      # 定义选项的类型为hex
        default 0x11                          # 选项的默认值为 0x11

    config FIFTH
        int "Int类型选项"                      # 定义选项的类型为int
        default 10                            # 选项的默认值为 10

    config SIXTH
        bool
        prompt "使用prompt定义选项配置的标题"

    config SEVENTH
        bool                                  # 没有定义选项的名称,则此选项不会在配置界面上显示

    config EIGHTH
        bool "测试help关键词"
        help
            这里用于详细描述此选项的用法


    config PARENT                             # 测试选项间的依赖性
        bool "被依赖的选项"                     # PARENT选项被选中,则显示CHILD选项,否则隐藏
                                              #
    config CHILD                              #
        bool "子选项"                          #
        depends on PARENT                     #


    config ACTIVE_SELECTED                    # 测试选项间的联动性
        bool "手动选中的选项"                   # ACTIVE_SELECTED选项被选中,则AUTO_SELECTED选项也被选中
        select AUTO_SELECTED                  #
                                              #
    config AUTO_SELECTED                      #
        bool "自动选中的选项"                   #


    choice                                    # 单选
        prompt "Option group"                 #
                                              #
        config OPTION_A                       #
            bool "A"                          #
                                              #
        config OPTION_B                       #
            bool "B"                          #
                                              #
        config OPTION_C                       #
            bool "C"                          #
                                              #
        config OPTION_D                       #
            bool "D"                          #
    endchoice                                 #

endmenu

第4步:测试。输入make menuconfig命令打开配置界面

代码配置界面截图
menu “Example Menu”
config FIRST                                        bool "Bool         default y
config SECOND         tristate “Tristate类型选项”               default m
config THIRD         string “String类型选项”                      default third选中此选项,按Enter键,可输入文字,如下图:
config FOURTH         hex “Hex类型选项”                            default 0x11选中此选项,按Enter键,可输入十六进制,如下图:
config FIFTH         int “Int类型选项”                            default 10选中此选项,按Enter键,可输入十进制整数,如下图:
config SIXTH         bool         prompt “使用prompt定义选项配置的标题”
config SEVENTH         bool在配置界面不显示
config EIGHTH         bool “测试help关键词”         help                 这里用于详细描述此选项的用法选中此选项,选此,按Enter键,可查看此选项详细描述,如下图:
config PARENT                                         bool “被依赖的选项”                                                                    config CHILD                                         bool “子选项”                                depends on PARENT未选中隐藏 选中显示
config ACTIVE_SELECTED         bool “手动选中的选项”         select AUTO_SELECTED config AUTO_SELECTED                                bool “自动选中的选项”前者未选中 前者选中,后者自动选中
choice         prompt “Option group”         config OPTION_A                 bool “A”         config OPTION_B                 bool “B”         config OPTION_C                 bool “C”         config OPTION_D                 bool “D” endchoice选中此选项,按Enter键,可选择成员,如下图:

7.跟文件系统

7.1.磁盘分区

磁盘分区是指通过分区工具将磁盘划分为几个逻辑部分,如Windows系统上有C盘、D盘和E盘,就是磁盘的三个分区。

磁盘分区又分为主分区和扩展分区。主分区是能够安装操作系统和进行计算机启动的分区,通常情况下Windows系统的C盘是主分区。

Windows系统是基于盘符进行分区的,如C盘、D盘、E盘,对盘符的操作相当于对于分区的操作。

Linux系统是基于设备文件进行分区的,如sda1,sda2,hda1,hdb1(设备文件的前两个字符表示磁盘的接口类型,如sd表示SATA接口的磁盘,hd表示IDE接口的磁盘,第三个字符表示不同的磁盘,最后一个字符表示该磁盘上的分区编号)。若要操作分区需要将设备文件与目录关联起来,关联的过程被称为挂载,被关联的目录被称为挂载点。例如,将sda2挂载到/home目录上,对/home目录操作就相当于对sda2对应的分区进行操作。

7.2.文件系统

文件系统是负责管理和存储文件信息的程序,简化了用户对文件的各项操作,为用户提供了共享和保护等功能。若是没有文件系统,会给用户带来大的操作成本,如用户需要熟悉外存的物理特性、文件的属性和文件在外存上的存储位置等。

文件系统用于管理外存,即我们通常所说的硬盘、闪存、光盘等存储设备,不用于管理内存。

分区和文件系统的关系:

分区是硬件,文件系统是软件,前者负责存储文件信息,后者负责管理存储在分区上的文件信息。分区和文件系统是相辅相成的,前者是后者的载体,文件系统只有挂载到分区上才可以使用,而文件系统为分区存储的信息提供管理。

一个分区只能挂载一个文件系统,执行如下命令可以查看分区所挂载的文件系统的类型。

mount | grep "<分区所对应的设备文件>"
// 示例:mount ! grep “/dev/sda1”

7.3.文件系统类型

文件系统类型是指操作系统用于管理和存储数据的不同方式。使用sudo cat /proc/filesystems命令可查询Linux操作系统支持的文件系统类型。

常见文件系统类型

类型描述
fat文件分配表。 这是一种由微软发明的文件系统,主要用于MS-DOS和早期的Windows系统。
ext扩展文件系统。 为Linux使用mmc型存储器而设计的一种日志型文件系统。支持大量的文件和目录,以及大容量的存储设备。
ntfs新技术文件系统。 这是Windows NT系列操作系统特有的文件系统,提供了更高的安全性、稳定性和性能。
nfs网络文件系统。 这是一种分布式文件系统,允许计算机客户端远程访问服务器上的文件,实现文件共享。
jffs闪存设备日志文件系统。 这是一种专为闪存设备设计的日志型文件系统,通过记录对文件系统的更改来提高读写性能和可靠性。 当文件系统已满或接近满时,因为垃圾收集的关系会使jffs2的运行速度大大放慢。
cramfs压缩的ROM文件系统。 这是一种只读的文件系统,主要用于嵌入式系统中的ROM或Flash存储器,通过zlib压缩算法对文件进行压缩存储,在读取时需要先解压。

7.4.虚拟文件系统

在“文件系统类型”章节知晓文件系统存在不同的类型,它们都有其独特的数据存储和管理方式。然而,操作系统需要以一种统一、标准化的方式来访问这些文件系统。虚拟文件系统(简称VFS)提供了这样一个抽象层,使得操作系统能够透明地访问各种文件系统,而无需关心它们底层的具体实现。

由于VFS提供了一个抽象层,新的文件系统只需实现VFS所定义的接口,就可以轻松地集成到操作系统中。同时,应用程序在使用open()、read()、write()等系统调用而无需考虑具体文件系统和实际物理介质。

7.5.根文件系统

根文件系统,简称rootfs,是内核启动时挂载的第一个文件系统,它包含了系统运行所必需的基本文件和目录,这些文件包括系统命令、库文件、配置文件、设备文件等。

rootfs是整个文件系统层次结构的起点,所有其他文件系统和分区都需挂载到根文件系统的目录上才可以被操作使用。

7.6.根文件系统的目录结构

目录名称描述
bin存放着最经常使用的命令,例如ls、cp、rm等
boot存放开机时所需的配置文件,包括Linux核心文件、开机菜单等
dev存放外部设备的所有设备文件
etc存放所有系统管理和配置文件的目录。包含了系统启动脚本、网络配置文件、用户账户信息等重要数据
home普通用户的家目录,每个用户都有自己的家目录,用于存放个人文件、文档、程序等
lib存放应用程序所需的基本库,运行时需要共享库,比如C、C++等标准库,GTK、QT等应用程序库
mnt用于临时挂载的文件系统,比如挂载U盘、SD卡等
opt安装应用程序的目录。许多第三方软件都安装在这里
proc存放操作系统运行时的进程信息及内核信息。挂载proc文件系统,用来表示系统的运行状态,只存在内存当中
root存放root用户的相关文件,root用户的家目录
sbin存放了系统启动时所需要的系统管理命令和程序。这些命令和程序通常只能由root用户或具有相应权限的用户执行
sys存放内核和硬件设备的信息,以文件系统的方式呈现给用户。挂载sysfs文件系统
tmp系统产生的临时文件都存放在这里
usr存放由用户自由添加的程序或开源库以及各种不常用的命令等。/usr/bin目录存放了用户安装的程序和文件,/usr/local是系统管理员在本机安装的软件的位置
var这个目录用于存放系统运行过程中经常变动的文件,如日志文件、缓存文件、数据库等。这些文件的大小和内容可能会随着系统的运行而不断变化

7.7.指定根文件系统的位置

通过U-Boot的bootargs参数告诉内核根文件系统在那个分区上以及相关配置,参数如下:

root=/dev/mmcblk0p2 rw rootfstype=ext4 init=/linuxrc
  • root=/dev/mmcblk0p2:root用于告诉内核根文件系统在那个分区。mmcblk0p2是一个设备文件,表示SD卡或eMMC存储设备的第2个分区。
  • rw:这个参数告诉内核以什么模式挂载根文件系统。rw表示读写模式。
  • rootfstype=ext4:这个参数指定了根文件系统的类型,告诉内核如何解释和挂载设备上的文件系统。
  • init=/linuxrc:这个参数指定了内核启动后要运行的第一个进程。值通常是/sbin/init或/init,负责启动和管理系统的其他服务。但在某些嵌入式系统或特定的启动场景中,可能会使用不同的初始化脚本,如这里的/linuxrc。

7.8.构建根文件系统

构建根文件系统本质是创建系统启动和运行所必需的目录和基本文件,如创建bin、sbin和usr目录等以及各种基本命令和工具。

根文件系统包含了许多系统所需的基本命令和工具,若要手动配置和安装每个命令和工具是比较繁琐的,为了避免这种情况,常使用BusyBox工具帮忙构建根文件系统。

7.8.1.BusyBox工具

BusyBox工具提供了一系列常用的命令和工具,避免了手动配置和安装每个命令和工具,同时,BusyBox工具还支持定制和扩展,用户可以根据需要添加或删除特定的命令和工具,以满足特定的应用需求

7.8.2.构建根文件系统过程

第1步:获取BusyBox工具

BusyBox工具官网下载:点击下载。本次使用的BusyBox工具是1.29.0版本,如下图:

第2步:解压busybox-1.29.0.tar.bz2压缩包,和创建rootfs目录

第3步:进入busybox-1.29.0目录,编辑Makefile

  • 将164行“CROSS_COMPILE”变量指定交叉编译编译器安装地址,如下图:

  • 将190行“ARCH ?= $(SUBARCH)”改为“ARCH ?= arm”。ARCH用于指定目标设备的CPU架构,S5P6818开发板的CPU架构为arm,因此指定为arm

第4步:配置BusyBox选项(此步骤的目的同配置Linux内核选项一样)

  • 执行make defconfig命令选中默认选项。也可以执行make allyesconfig和make allnoconfig命令,功能分别是选中全部选项和选中小部分选项

  • 执行make menuconfig命令打开Makeconfig工具
  • 取消选中“Build static binary(no shared libs)”
路径:
Settings  --->
  [ ] Build static binary(no shared libs)

选中此选项意味着将BusyBox编译为一个与所需要的库已建立连接的静态二进制文件,因为静态二进制文件已与所需要的库建立链接,所以运行时不需要额外的库就可以单独运行,但生成的二进制文件会较大且DNS无法进行域名解析,因此不要选中,需要的库后续会手动补充。

  • 选中“vi-style line editing commands”
路径:
Settings  --->
  [ * ] vi-style line editing commands

选中此选项意味着允许用户在使用shell时,启用类似于vi编辑器的行编辑命令,如可以使用诸如移动光标、删除字符、复制和粘贴文本等vi风格的命令来编辑当前输入的命令行;通过按上下箭头键,你可以浏览之前输入过的命令历史,快速复用或修改之前的命令;使用vi风格的搜索命令来查找命令行中的特定文本,并进行替换操作。

  • 取消选中“Simplified modutils”
路径:
Linux Module Utilities  --->
  [ ] Simplified modutils

选中此选项意味着提供简易版的insmod和rmmod工具,相应功能和选项会减少。我们需要完整版的insmod和rmmod工具,因此不选中。

  • 选中“mdev (16 kb)”及其子项
路径:
Linux System Utilities  --->
  [ * ] mdev (16 kb)

选中此选项意味着提供精简版的内核动态设备管理器(udev),其可执行文件大小大约为16KB,专为嵌入式系统管理设备而设计。udev用于在设备连接到系统时动态地创建和管理/dev目录下的设备节点。

  • 选中“Support Unicode”及其子项“Check $LC_ALL, $LC_CTYPE and $LANG environment variables”
路径:
Settings  --->
  [ * ] Support Unicode
    [ * ] Check $LC_ALL, $LC_CTYPE and $LANG environment variables

Check $LC_ALL, $LC_CTYPE and $LANG environment variables选项的作用是BusyBox在运行时检查LC_ALL, LC_CTYPE and LANG这几个环境变量,以确定应该使用哪种字符集和本地化设置。

  • 保存并退出Menuconfig工具

第5步:执行如下命令编译BusyBox

make install CONFIG_PREFIX=../rootfs   
// rootfs是在第②步创建rootfs目录,用于存放编译结果

第6步:进入rootfs查看编译结果

第7步:在rootfs目录下执行如下代码补充目录

mkdir usr/lib dev etc etc/init.d lib var proc tmp home root mnt opt sys media

第8步:在第4步时没有选中“Build static binary(no shared libs)”选项,在编译完成后是缺少库的,因此需要手动添加库。添加库是将交叉编译器的库拷贝到rootfs相应目录中。

  • 拷贝标准库和内核级库

  • 删除链接ld-linux-armhf.so.3,拷贝源ld-linux-armhf.so.3

  • 拷贝用户级库

注意:你的交叉编译器的标准库、内核级库和用户级库所在的目录可能与作者的不同。

第9步:进入rootfs/etc目录,创建fstab文件(fstab用于配置系统启动后要自动挂载的分区),fstab中的代码如下:

# <file system>    <mount point>    <type>    <options>    <dump>    <pass>
proc               /proc            proc      defaults     0         0
tmpfs              /tmp             tmpfs     defaults     0         0
sysfs              /sys             sysfs     defaults     0         0

  • :文件系统标识。这个字段指定了要挂载的文件系统。它可以是设备文件名(如/dev/sda1),也可以是文件系统标签或UUID。
  • :挂载点。这个字段指定了文件系统应该被挂载到的目录位置。
  • :文件系统类型。这个字段指定了文件系统的类型,如ext4、xfs、vfat等。
  • :挂载选项。这个字段列出了挂载文件系统时使用的选项,多个选项之间用逗号分隔。例如,defaults是常用的选项,它包含了rw(读写)、suid(允许set-user-identifier或set-group-identifier位)、dev(解释块和字符特殊设备)、exec(允许执行二进制文件)、auto(允许使用-a选项进行挂载)、nouser(禁止普通用户挂载)等。
  • :是否需要备份。这个字段用于dump备份工具,决定是否需要备份文件系统。如果此字段设置为0,则表示不需要备份;设置为1则表示需要备份。
  • :文件系统检查的顺序。这个字段用于fsck文件系统检查工具,决定了在启动过程中检查文件系统的顺序。根文件系统应该设置为1,其他文件系统可以是2(在根文件系统之后检查)或0(不检查)。

第10步:进入rootfs/etc/init.d目录,创建rcS文件(rcS是系统启动后执行的一个系统初始化脚本),rcS中的代码如下:

#!/bin/sh

# 指定系统查找可执行文件的目录
PATH=/sbin:/bin:/usr/sbin:/usr/bin:$PATH

# 告诉动态链接器在哪里查找库
LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/lib:/usr/lib

#  导出上面创建的两个环境变量
export PATH LD_LIBRARY_PATH

# 使用mount命令挂载所有在/etc/fstab文件中定义的、尚未挂载的文件系统
mount -a

# 创建一个伪文件系统挂载点
mkdir /dev/pts
# 将devpts伪文件系统挂载到/dev/pts挂载点上
mount -t devpts devpts /dev/pts

# 诉内核在热插拔事件发生时使用mdev作为热插拔处理工具
echo /sbin/mdev > /proc/sys/kernel/hotplug
# 启动mdev并扫描系统上的已存在设备,为其创建相应的设备挂载点
mdev -s

第11步:执行chmod 777 rcS命令给予rcS可执行权限

第12步:进入rootfs/etc目录,创建inittab文件(inittab负责在系统启动后启动其他用户进程或服务),inittab中的代码如下:

# 系统启动后运行/etc/init.d/rcS脚本
::sysinit:/etc/init.d/rcS

# 将console作为控制台终端
console::askfirst:-/bin/sh

# 当系统接收到重启信号时,会执行/sbin/init命令来重新启动系统
::restart:/sbin/init 

# 当按下Ctrl+Alt+Delete组合键时,会执行/sbin/reboot命令来重新启动系统
::ctrlaltdel:/sbin/reboot

# 当系统关闭时,执行umount命令来卸载所有文件系统
::shutdown:/bin/umount -a -r

# 当系统关闭时,执行swapoff命令来关闭所有的交换空间
::shutdown:/sbin/swapoff -a

第13步:将rootfs制作为镜像

  • 进入rootfs目录的父目录,执行如下代码,创建一个大小为2000MB的rootfs.img文件系统
dd if=/dev/zero of=rootfs.img bs=1M count=2000

  • 对rootfs.img文件系统格式化为ext4格式
mkfs.ext4 -F -L linuxroot rootfs.img

  • 创建tmpfs目录,并执行如下代码将rootfs.img文件系统挂载于tmpfs目录上
mount rootfs.img tmpfs

  • 执行如下代码,将rootfs目录下的目录和文件拷贝到tmpfs目录中
cp -rfp rootfs/* tmpfs
  • 执行如下代码,将rootfs.img文件系统从tmpfs目录上卸载
umount tmpfs
  • 执行如下代码,检查rootfs.img文件系统的正确性,和若有错误自动修复
e2fsck -p -f rootfs.img
  • 执行如下代码,调整rootfs.img文件系统的大小
resize2fs -M rootfs.img

第14步:rootfs.img镜像烧写

rootfs.img镜像的烧写与U-Boot的烧写一样,只是在第⑥步有所不同:在Ubuntu上进入rootfs.img所在目录,输入fastboot flash system rootfs.img命令进行烧写

7.8.3./etc/inittab文件配置项说明

文件中的每一项配置通常包括id、runlevels、action和process四个部分,四部分间用“:”隔离开,形式如下:

<id>:<runlevels>:<action>:<process>
  • :每一项配置的唯一标识,通常是一个唯一的数字或字符串
  • < runlevels >:运行级别。定义该配置项在哪个系统运行级别下生效,运行级别可以是0到6之间的数字,一个配置项可以指定多个运行级,如2345表示在运行级别2、3、4和5下都生效。运行级别介绍如下:
运行级别描述
运行级别1单用户工作状态,具有root权限,用于系统维护,禁止远程登录
运行级别2多用户状态,但不支持NFS(网络文件系统)
运行级别3多用户状态,具有NFS支持,登录后进入控制台命令行模式,这是常见的运行级别
运行级别4通常保留未使用
运行级别5图形用户界面模式,登录后进入图形GUI环境
运行级别6系统正常关闭并重启的运行级别,不能设为6作为默认运行级别,否则不能正常启动
  • < action >:操作。定义应如何执行该配置
操作描述
sysinitinit进程启动后会首先执行sysinit操作指定的,只执行一次。 例如:sysinit操作指定/etc/init.d/rcS进程,用于初始化任务。
wait一旦wait操作指定的启动,init进程会等待其执行完成才处理其它任务。 例如:如果有一个进程需要在其他服务启动之前完成,就可以使用 wait 操作。
once与wait操作相反。一旦once操作指定的启动,init进程不会等待其执行完成,就会继续处理其它任务。只执行一次。 这通常用于处理在系统启动时执行一次的任务(如设置硬件参数)。
respawn当respawn操作指定的退出时会重新启动respawn操作指定的。 这通常用于守护进程,确保它们始终在运行。
askfirst和askfirst类似,只是在重新启动askfirst操作指定的前,在控制台上会先显示一条消息(如:“Please press Enter to activate this console”)并等待用户输入(如:按下“Enter”)后才会重新启动。 这通常用于需要管理员确认的敏感操作。
restart当init进程重新启动时会执行restart操作指定的。 例如:如果系统由于某种原因重启init进程,那么restart指定的进程会确保系统状态得到正确的恢复。
ctrlaltdel当按下ctrl+alt+del组合键时会执行ctrlaltdel操作指定的。 这通常用于处理用户的关机请求,如在按下Ctrl+Alt+Del时执行安全的关机过程。
shutdown在系统关闭时会执行shutdown操作指定的。 这通常用于系统关闭前执行必要的清理和保存工作。

最后

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。

因此收集整理了一份《2024年嵌入式&物联网开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img

img

img

img

img

img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上嵌入式&物联网开发知识点,真正体系化!

如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新!!

某种原因重启init进程,那么restart指定的进程会确保系统状态得到正确的恢复。 |
| ctrlaltdel | 当按下ctrl+alt+del组合键时会执行ctrlaltdel操作指定的。 这通常用于处理用户的关机请求,如在按下Ctrl+Alt+Del时执行安全的关机过程。 |
| shutdown | 在系统关闭时会执行shutdown操作指定的。 这通常用于系统关闭前执行必要的清理和保存工作。 |

最后

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。

因此收集整理了一份《2024年嵌入式&物联网开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

[外链图片转存中…(img-02hfcMBP-1715759714716)]

[外链图片转存中…(img-mKw6KXS0-1715759714717)]

[外链图片转存中…(img-hOaYoRvk-1715759714717)]

[外链图片转存中…(img-7Cno6TOy-1715759714718)]

[外链图片转存中…(img-YebvOWNK-1715759714718)]

[外链图片转存中…(img-WvS5DO6g-1715759714719)]

[外链图片转存中…(img-gq8OLfdM-1715759714720)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上嵌入式&物联网开发知识点,真正体系化!

如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新!!

  • 5
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值