3. 添加修改启动配置

在buildroot系统中/etc/init.d/目录下的文件是系统初始化脚本,在系统启动、关闭或运行级别切换等过程中起着重要作用。

如果我们需要添加初始化配置也可以在/etc/init.d/目录下添加自己的脚本。

3.1. 系统默认配置脚本

本小节使用“Buildroot根文件系统的构建”章节编译出来的最基础的文件系统进行说明,排除额外软件包添加的配置脚本。

查看/etc/init.d/目录下的文件,有以下的配置脚本:

1
2
3
ls /etc/init.d/
S01syslogd  S02sysctl   S20urandom  rcK
S02klogd    S10mdev     S40network  rcS

配置脚本说明如下:

  • S01syslogd:用于启动系统日志服务,负责将系统中的各种日志信息记录到指定的日志文件中。

  • S02sysctl:用于在系统启动时设置内核参数,如内存管理、进程调度等。

  • S02klogd:用于处理内核日志,负责从内核获取日志消息,并将其传递给系统日志机制进行记录或显示。

  • S10mdev:用于管理和创建设备节点,负责在系统启动时根据内核检测到的设备信息,动态地创建和删除相应的设备节点。

  • S20urandom:用于初始化系统的随机数生成器,为系统提供随机数源。

  • S40network:用于配置和启动网络服务,包括设置网络接口的IP地址、子网掩码、网关等参数。

  • rcS:是系统启动时,用于按顺序执行/etc/init.d目录下以S开头的初始化脚本,从而启动系统所需的各项服务。

  • rcK:是系统关闭或重启等需要停止服务的场景时,用于按逆序来停止/etc/init.d目录下以S开头的初始化脚本。

3.1.1. rcS

rcS是系统启动阶段的关键脚本,负责初始化系统的基本服务和环境,确保系统在启动后能够正常运行。它会遍历/etc/init.d目录下符合特定命名规则的脚本,并根据脚本的类型采用不同的执行方式来启动相应的服务。

rcS内容如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#!/bin/sh


# Start all init scripts in /etc/init.d
# executing them in numerical order.
#
for i in /etc/init.d/S??* ;do

    # Ignore dangling symlinks (if any).
    [ ! -f "$i" ] && continue

    case "$i" in
        *.sh)
            # Source shell script for speed.
            (
                trap - INT QUIT TSTP
                set start
                . $i
            )
            ;;
        *)
            # No sh extension, so fork subprocess.
            $i start
            ;;
    esac
done
  • 第4-5行:注释说明了脚本的主要功能,即启动/etc/init.d目录下的所有初始化脚本,并按照文件名中的数字顺序执行。

  • 第7行:使用for循环遍历/etc/init.d目录下以S开头且后面跟着两位字符的所有文件和目录。

  • 第10行:检查当前遍历到的$i是否为一个普通文件,如果不是普通文件则跳出本次循环。

  • 第12-25行:使用case语句根据脚本的文件名是否以 .sh 结尾来决定执行方式。

  • 第16行:用于忽略INT(中断信号),QUIT(退出信号)和TSTP(暂停信号)。

  • 第17行:设置脚本的参数为start。

  • 第18行:在当前shell环境中执行该脚本,这样可以避免创建新的进程,提高执行速度。

  • 第23行:直接在新的子进程中执行该脚本,并传递start参数,以启动相应的服务。

3.1.2. rcK

系统在关闭、重启或进入特定维护状态时,需要确保所有正在运行的服务和进程能够被正确地停止,以保证系统资源的干净释放, 避免数据丢失或系统错误,rcK脚本就是用于实现这一功能的关键脚本。

rcK内容如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#!/bin/sh


# Stop all init scripts in /etc/init.d
# executing them in reversed numerical order.
#
for i in $(ls -r /etc/init.d/S??*) ;do

    # Ignore dangling symlinks (if any).
    [ ! -f "$i" ] && continue

    case "$i" in
        *.sh)
            # Source shell script for speed.
            (
                trap - INT QUIT TSTP
                set stop
                . $i
            )
            ;;
        *)
            # No sh extension, so fork subprocess.
            $i stop
            ;;
    esac
done

rcK和rcS内容相似,区别如下:

  • 第7行:使用for循环遍历/etc/init.d目录下以S开头且后面跟着两位字符的所有文件和目录,并按照逆序排列。

  • 第17行:设置脚本的参数为stop。

  • 第23行:直接在新的子进程中执行该脚本,并传递stop参数,以停止相应的服务。

3.1.3. S40network

S40network用于管理系统的网络服务,包括启动、停止和重启网络连接。

S40network内容如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#!/bin/sh
#
# Start the network....
#

# Debian ifupdown needs the /run/network lock directory
mkdir -p /run/network

case "$1" in
start)
        printf "Starting network: "
        /sbin/ifup -a
        [ $? = 0 ] && echo "OK" || echo "FAIL"
        ;;
stop)
        printf "Stopping network: "
        /sbin/ifdown -a
        [ $? = 0 ] && echo "OK" || echo "FAIL"
        ;;
restart|reload)
        "$0" stop
        "$0" start
        ;;
*)
        echo "Usage: $0 {start|stop|restart}"
        exit 1
esac

exit $?
  • 第7行:创建/run/network目录,ifupdown工具需要这个目录作为锁目录,用于管理网络接口的操作。

  • 第12行:ifup -a命令的作用是启动/etc/network/interfaces文件中配置的所有网络接口。

  • 第13行:$?获取ifup命令的退出状态码,如果状态码为0打印 “OK”,否则打印 “FAIL”。

  • 第17-18行:和第12-13行类似,只是停止操作。

  • 第20-22行:传递restart或者reload参数,先执行stop或者执行start,用于重启操作。

/etc/network/interfaces是一个重要的网络配置文件,通常用于配置Linux系统中的网络接口和网络参数。 它在Debian系统及其衍生版本(如 Ubuntu)中广泛使用,用于定义网络接口的配置信息,如IP地址、子网掩码、网关、DNS等。

/etc/network/interfaces的内容如下:

1
2
3
4
# interface file auto-generated by buildroot

auto lo
iface lo inet loopback

默认只对lo网卡进行配置,lo也即本地回环网卡,没有对板卡eth0、eth1网卡进行配置,因此eth0、eth1网卡默认是down状态的。

在interfaces文件添加eth0、eth1网卡配置,修改后内容如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
# interface file auto-generated by buildroot

auto lo
iface lo inet loopback

auto eth0
iface eth0 inet dhcp

auto eth1
iface eth1 inet static
    address 192.168.103.180·
    netmask 255.255.255.0
    gateway 192.168.103.254

以上eth0使用dhcp动态获取网络信息,eth1作静态配置,可根据需求自行修改,配置完成后重启系统或者重启S40network:

1
/etc/init.d/S40network restart

3.2. 添加或修改配置脚本

3.2.1. 网络相关

3.2.1.1. 网口配置

除了使用/etc/network/interfaces配置网络,也可以自己写一个脚本配置网络,在/etc/init.d/添加一个名为S40eth0的脚本,内容如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#!/bin/sh

. /etc/profile

if [ ! -d "/boot" ] ; then
    mkdir /boot
fi

start() {
    printf "start ethernet: "
    if [ ! -e /boot/boot_mac_eth0 ]; then
        #不存在boot_mac_eth0则获取网口mac地址并保存
        mac_address_eth0=$(ifconfig eth0 | grep HWaddr | awk '{print $5}')
        echo "$mac_address_eth0" > /boot/boot_mac_eth0
    else
        #存在boot_mac_eth0则使用保存的mac地址
        ifconfig eth0 down
        mac_address_eth0=$(cat /boot/boot_mac_eth0)
        ifconfig eth0 hw ether $mac_address_eth0
        ifconfig eth0 up
    fi

    if [ ! -e /boot/eth.nodhcp ]
    then
        #udhcpc动态获取eth0网络信息
        (udhcpc -i eth0 -t 10 -T 1 -A 5 -b -p /run/udhcpc.eth0.pid) &
    fi
    echo "OK"
}

stop() {
    kill `cat /run/udhcpc.eth0.pid`
    rm /run/udhcpc.eth0.pid
}

restart() {
    stop
    start
}

case "$1" in
start)
        start
        ;;
stop)
        stop
        ;;
restart|reload)
        restart
        ;;
*)
        echo "Usage: $0 {start|stop|restart}"
        exit 1
esac

给脚本添加执行权限:

1
chmod 777 /etc/init.d/S40eth0

以上脚本只配置了eth0网口,如需配置eth1可自行参考添加。

3.2.1.2. WIFI配置

在“外设接口使能”章节的开启wifi接口小节,我们已经讲解了如何使能wifi接口、添加网卡固件、连接wifi,本小节将介绍添加启动脚本来自动连接wifi。

在/etc/init.d/添加一个名为S40wifi的脚本,内容如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#!/bin/sh

start() {
    printf "start wifi: "

    if ! ifconfig -a | grep -q "wlan0"; then
        echo "wlan0 does not exist"
        exit 0
    fi

    if [ ! -e /etc/wpa_supplicant/wpa_supplicant.conf ]; then
        echo "wpa_supplicant.conf does not exist"
        exit 0
    fi

    #打开wifi
    ip link set wlan0 up

    #连接wifi
    wpa_supplicant -i wlan0 -c /etc/wpa_supplicant/wpa_supplicant.conf &

    #申请动态ip
    udhcpc -b -i wlan0 -t 10 -T 1 -A 5 > /dev/null 2>&1 &
    echo "OK"
}

stop() {
    ip link set wlan0 down
}

restart() {
    stop
    start
}

case "$1" in
start)
        start
        ;;
stop)
        stop
        ;;
restart|reload)
        restart
        ;;
*)
        echo "Usage: $0 {start|stop|restart}"
        exit 1
esac

给脚本添加执行权限:

1
chmod 777 /etc/init.d/S40wifi

3.2.1.3. RNDIS配置

在野火Debian系统usb otg口支持虚拟串口、RNDIS虚拟网卡、虚拟U盘功能,是怎么实现的呢?其实只需要执行一行命令即可,命令如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#emmc版本执行
modprobe g_multi file=/dev/mmcblk1p1 removable=1 cdrom=0 ro=0 stall=0 nofua=1 iManufacturer=embedfire iProduct=embedfire iSerialNumber=1234fire5678

#sd版本执行
modprobe g_multi file=/dev/mmcblk0p1 removable=1 cdrom=0 ro=0 stall=0 nofua=1 iManufacturer=embedfire iProduct=embedfire iSerialNumber=1234fire5678

#nand执行
#创建40MB的空文件
dd if=/dev/zero of=disk.img bs=1M count=40
#将文件格式化为fat32文件系统
mkfs.vfat -F 32 disk.img
#将文件映射到回环设备
losetup /dev/loop0 disk.img
#加载驱动
modprobe g_multi file=/dev/loop0 removable=1 cdrom=0 ro=0 stall=0 nofua=1 iManufacturer=embedfire iProduct=embedfire iSerialNumber=1234fire5678

#nand驱动信息输出如下
[  544.634653] using random self ethernet address
[  544.639132] using random host ethernet address
[  544.645432] Mass Storage Function, version: 2009/09/11
[  544.650606] LUN: removable file: (no medium)
[  544.655852] LUN: removable file: /dev/loop0
[  544.660058] Number of LUNs=1
[  544.666237] usb0: HOST MAC 22:43:9f:93:d1:18
[  544.670721] usb0: MAC 76:de:6d:f1:2b:5d
[  544.675615] g_multi gadget: Multifunction Composite Gadget
[  544.681291] g_multi gadget: userspace failed to provide iSerialNumber
[  544.687746] g_multi gadget: g_multi ready
[  544.903388] g_multi gadget: high-speed config #1: Multifunction with RNDIS
[  544.910532] gs_console_connect: port num [0] is not support console

也即加载g_multi.ko时指定需要映射的分区,驱动加载后将分区从usb otg口映射成虚拟U盘的同时,也会添加RNDIS虚拟网卡以及虚拟串口功能。其中,nand需要创建disk.img,需要格式化为fat32或exFAT等windows可识别格式,此操作需要格式化工具,板卡如果没有工具可以在虚拟机创建,然后拷贝到板卡。

除了系统分区,也可以将板卡USB口插入的U盘再从usb otg口映射出去,命令如下:

1
2
#/dev/sda1是U盘分区
modprobe g_multi file=/dev/sda1 removable=1 cdrom=0 ro=0 stall=0 nofua=1 iManufacturer=embedfire iProduct=embedfire iSerialNumber=1234fire5678

在知道如何生成RNDIS虚拟网卡后,下面我们来自启动配置,在/etc/init.d/添加一个名为S40rdnis的脚本,内容如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
#!/bin/sh

start() {
    printf "start rndis: "

    fat_media="/root/disk.img"

    unset root_drive

    #获取系统分区信息
    root_drive="$(cat /proc/cmdline | sed 's/ /\n/g' | grep root=UUID= | awk -F 'root=' '{print $2}' || true)"
    if [ ! "x${root_drive}" = "x" ] ; then
        root_drive="$(/sbin/findfs ${root_drive} || true)"
    else
        root_drive="$(cat /proc/cmdline | sed 's/ /\n/g' | grep root= | awk -F 'root=' '{print $2}' || true)"
    fi

    #区分emmc、sd、nand
    if echo ${root_drive} | grep -q "/dev/mmcblk[0-9]p[0-9]"; then
        actual_image_file="${root_drive%?}1"
    else
        media_loop=$(losetup -f || true)
        losetup ${media_loop} "${fat_media}"
        actual_image_file=${media_loop}
    fi

    #加载驱动
    modprobe g_multi file=${actual_image_file} removable=1 cdrom=0 ro=0 stall=0 nofua=1 iManufacturer=embedfire iProduct=embedfire iSerialNumber=1234fire5678

    #判断usb0网卡是否生成
    if ! ifconfig -a | grep -q "usb0"; then
        echo "usb0 does not exist"
        exit 0
    fi

    #设置静态ip和网关
    ifconfig usb0 192.168.137.10 up
    route add default gw 192.168.137.1 metric 800

    echo "OK"
}

stop() {
    #卸载驱动
    rmmod g_multi.ko
    #卸载/dev/loop0映射
    if losetup -a | grep -q "/dev/loop0:"; then
        losetup -d /dev/loop0
    fi
}

restart() {
    stop
    start
}

case "$1" in
start)
        start
        ;;
stop)
        stop
        ;;
restart|reload)
        restart
        ;;
*)
        echo "Usage: $0 {start|stop|restart}"
        exit 1
esac

给脚本添加执行权限:

1
chmod 777 /etc/init.d/S40rndis

3.2.1.4. SSH配置

如果在添加软件包章节添加了OpenSSH软件包,会在/etc/init.d/多出一个S50sshd的文件,默认配置下加载会很慢很慢,导致登录服务要很久才能弹出,原因是默认配置下每次都执行生成缺失的SSH主机密钥命令以及执行sshd命令启动需要很长时间,因此可优化S50sshd文件,解决长期“堵塞”问题,文件修改后如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#!/bin/sh
#
# sshd        Starts sshd.
#

# Make sure the ssh-keygen progam exists
[ -f /usr/bin/ssh-keygen ] || exit 0

umask 077

start() {
        # Create any missing keys
        if [ ! -e "/etc/ssh/ssh_host_ecdsa_key" ] ; then
                /usr/bin/ssh-keygen -A
        fi
        printf "Starting sshd: "
        /usr/sbin/sshd &
        touch /var/lock/sshd
        echo "OK"
}
stop() {
        printf "Stopping sshd: "
        killall sshd
        rm -f /var/lock/sshd
        echo "OK"
}
restart() {
        stop
        start
}

case "$1" in
start)
        start
        ;;
stop)
        stop
        ;;
restart|reload)
        restart
        ;;
*)
        echo "Usage: $0 {start|stop|restart}"
        exit 1
esac

exit $?
  • 第13-15行:根据判断是否缺失密钥再执行/usr/bin/ssh-keygen -A命令。

  • 第17行:将sshd后台执行,避免“堵塞”。

除了S50sshd配置文件外,/etc/ssh/sshd_config文件也是配置ssh十分重要的配置文件,默认配置了不允许root用户ssh登录,又因为buildroot默认用户为root,因此需要修改sshd_config文件允许root用户ssh登录,找到PermitRootLogin配置项,修改为PermitRootLogin yes

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
/* 省略前面内容 */

# Authentication:

#LoginGraceTime 2m
PermitRootLogin yes
#StrictModes yes
#MaxAuthTries 6
#MaxSessions 10

/* 省略后面内容 */

如果root用户没有密码,还需要为root用户添加密码:

1
passwd root

修改完成后,重启板卡,连接网络,等待ssh服务启动后即可ssh远程登录板卡。