Openwrt学习笔记(四)——系统开机启动

2023-11-07

1. 内核启动

bootloader将kernel从flash中拷贝到RAM以后,bootloader将退出舞台,并将这个舞台交给了kernel。中间有些交接的细节过程,这里不赘述,我们直接从kernel的启动开始分析。

不同平台的kernel启动时,最开始部分的汇编脚本会有些不一样,但是从汇编跳转到C语言的代码过程中的第一条命令大多数都是start_kernel函数,比如arm平台,它汇编代码的最后一个跳转是“b   start_kernel” (linux-3.14/arch/arm/kernel/head-common.S),然后执行start_kernel函数(linux-3.14/init/main.c),这个函数完成一些cpu,内存等初始化以后就会执行rest_init(linux-3.14/init/main.c)函数,该函数创建两个内核线程init和kthreadd之后,进入死循环,即所谓的0号进程。

kenrel_init()(init/main.c)函数,在kernel_init函数中,该函数首先会调用kernel_init_freeable,该函数主要完成以下工作:

1.打开/dev/console,而且该打开句柄的文件描述符是0(标准输出),接着调动sys_dup复制两个文件描述符,分别是1和2,用于标准输入和标准出错。因为它是第一个打开的文件,所以文件描述符是0,如果打开的是其他文件,标准输出就在是0了。

2.第二件事是看以下uboot有没有传启动ramdisk的命令过来,如果没有,就判断/init文件是否存在,如果存在则调用prepare_namespace函数,这个函数会完成根文件系统的挂载工作。

因为从开机的log可以看到uboot传来的启动命令[    0.000000] Kernel command line:  rootwait rootfsname=rootfs rootwait clk_ignore_unused,

所以saved_root_name=rootfs, 那么prepare_namespace()会调用name_to_dev_t()得到主次设备号并存放在ROOT_DEV(31:12),


得到主次设备号后会调用 mount_root, 该函数会调用  mount_block_root("/dev/root", root_mountflags);

mount_block_root 首先调用 get_fs_names 得到根文件系统的类型(通常由rootfstype=来指定), 然后调用 do_mount_root, 该函数会调用 sys_mount 完成任务,将根文件系统 mount 到 /root 后以后,会调用 chroot 将根目录切换到 /root 目录, 使其根文件系统变成真正的根。而原来的根只是一个虚拟的内存根。

成功log:[    1.681344] VFS: Mounted root (squashfs filesystem) readonly on device 31:12.

31:12是mtd12 的主次设备号,我们可以用下面的命令来确认:

root@test:/dev# file /dev/mtdblock12
/dev/mtdblock12: block special (31/12)

而从flash分区情况可以知道该分区存放的是rootfs,分区表如下:

[    1.453252] Creating 14 MTD partitions on "spi0.0":
[    1.458100] 0x000000000000-0x000000040000 : "0:SBL1"   //0号分区
[    1.464274] 0x000000040000-0x000000060000 : "0:MIBIB"
[    1.469425] 0x000000060000-0x0000000c0000 : "0:QSEE"
[    1.474479] 0x0000000c0000-0x0000000d0000 : "0:CDT"
[    1.479346] 0x0000000d0000-0x0000000e0000 : "0:DDRPARAMS"
[    1.484785] 0x0000000e0000-0x0000000f0000 : "0:APPSBLENV"
[    1.490212] 0x0000000f0000-0x000000170000 : "0:APPSBL"
[    1.495430] 0x000000170000-0x000000180000 : "0:ART"
[    1.500384] 0x000000180000-0x000000190000 : "config"
[    1.505436] 0x000000190000-0x0000001a0000 : "pot"
[    1.510249] 0x0000001a0000-0x0000001b0000 : "data"
[    1.515434] 0x0000001b0000-0x000001fc0000 : "0:HLOS"
[    1.520486] 0x000000540000-0x000001fc0000 : "rootfs"  //12号分区
[    1.525471] mtd: device 12 (rootfs) set to be root filesystem
[    1.530832] 1 squashfs-split partitions found on MTD device rootfs
[    1.536393] 0x000001130000-0x000001fc0000 : "rootfs_data"

执行完上面的代码后,会返回kernel_init函数,接着执行下面的代码,它首先会检查内核的启动参数中是否有设置init参数,如果有,则会使用该参数指定的程序作为init程序,否则会按照如下代码中所示的顺序依次尝试启动,如果都无法启动就会kernel panic。

如果没有给init传递参数,那么系统就会从“/etc/preinit” 开始执行,启动文件系统。

2. “/etc/preinit”

(openwrt/package/base-files/files/etc)

    #!/bin/sh  
    # Copyright (C) 2006 OpenWrt.org  
    # Copyright (C) 2010 Vertical Communications  
      
    [ -z "$PREINIT" ] && exec /sbin/init  
      
    export PATH=/bin:/sbin:/usr/bin:/usr/sbin  
      
    pi_ifname=  
    pi_ip=192.168.1.1  
    pi_broadcast=192.168.1.255  
    pi_netmask=255.255.255.0  
      
    fs_failsafe_ifname=  
    fs_failsafe_ip=192.168.1.1  
    fs_failsafe_broadcast=192.168.1.255  
    fs_failsafe_netmask=255.255.255.0  
      
    fs_failsafe_wait_timeout=2  
      
    pi_suppress_stderr="y"  
    pi_init_suppress_stderr="y"  
    pi_init_path="/bin:/sbin:/usr/bin:/usr/sbin"  
    pi_init_cmd="/sbin/init"  
      
    . /lib/functions.sh  
      
    boot_hook_init preinit_essential  
    boot_hook_init preinit_main  
    boot_hook_init failsafe  
    boot_hook_init initramfs  
    boot_hook_init preinit_mount_root  
      
    for pi_source_file in /lib/preinit/*; do  
        . $pi_source_file  
    done  
      
    boot_run_hook preinit_essential  
      
    pi_mount_skip_next=false  
    pi_jffs2_mount_success=false  
    pi_failsafe_net_message=false  
      
    boot_run_hook preinit_main  
这个初始化过程遵循如下主线:

 

下面我们一步一步分析这个过程。
在/etc/preinit脚本中,第一条命令如下:
        [ -z "$PREINIT" ] && exec /sbin/init

在从内核执行这个脚本时,PREINIT这个变量时没有定义的,所以会直接执行/sbin/init。/sbin/init程序主要做了一些初始化工作,如环境变量设置、文件系统挂载、内核模块加载等,之后会创建两个进程,分别执行/etc/preinit和/sbin/procd,执行/etc/preinit之前会设置变量PREINIT,/sbin/procd会带-h的参数,当procd退出后会调用exec执行/sbin/proc替换当前init进程(具体过程可参见procd程序包中的init和procd程序)。这就是系统启动完成后,ps命令显示的进程号为1的进程名最终为/sbin/procd的由来,中间是有几次变化的。

继续看/etc/preinit脚本,出来变量设置外,接下来是执行了三个shell脚本:

                . /lib/functions.sh

                . /lib/functions/preinit.sh

                . /lib/functions/system.sh

注意“.”和“/”之间是有空格的,这里的点相当与souce命令,但souce是bash特有的,并不在POSIX标准中,“.”是通用的用法。使用“.”的意思是在当前shell环境下运行,并不会在子shell中运行。这几个shell脚本主要定义了shell函数,特别是preinit.sh中,定义了hook相关操作的函数。

之后会使用boot_hook_init定义五个hook结点如下:
                boot_hook_init preinit_essential
                boot_hook_init preinit_main
                boot_hook_init failsafe
                boot_hook_init initramfs
                boot_hook_init preinit_mount_root

之后会向这些结点中添加hook函数。在之后就是一个循环,依次在当前shell下执行/lib/preinit/目录下的脚本,
                for pi_source_file in /lib/preinit/*; do
                . $pi_source_file

                done


这些脚本包括:

02_default_set_state
10_indicate_failsafe
10_indicate_preinit
10_sysinfo
30_failsafe_wait
40_run_failsafe_hook
50_indicate_regular_preinit
70_initramfs_test

80_mount_root     //这里会对overlay目录进行挂载


99_10_failsafe_login
99_10_run_init
由于脚本众多,因此openwrt的设计者将这些脚本分成下面几类:
preinit_essential
preinit_main
failsafe
initramfs
preinit_mount_root
每一类函数按照脚本的开头数字的顺序运行。
等目录用于安装真正的根。
/lib/preinit/目录下的脚本具体类似的格式,定义要添加到hook结点的函数,然后通过boot_hook_add将该函数添加到对应的hook结点。
最后,/etc/preinit就会执行boot_run_hook函数执行对应hook结点上的函数。在当前环境下只执行了preinit_essential和preinit_main结点上的函数,如下:
                boot_run_hook preinit_essential
                boot_run_hook preinit_main

到此,/etc/preinit执行完毕并退出。如果需要跟踪调试这些脚本,可以 在/etc/preinit的最开始添加一条命令set -x,这样就会打印出执行命令的过程, 当并不会真正执行。


#####################################

preinit执行的最后一个脚本为99_10_run_init,运行
exec env - PATH=$pi_init_path $pi_init_env $pi_init_cmd
pi_init_cmd为
pi_init_cmd="/sbin/init"

因此开始运行busybox的init命令

##########################################

上面这些是在旧的openwrt下面的实现,在新的openwrt中没有pi_init_cmd这样的命令了,它在procd中实现。因为/sbin/init进程的最后一个函数preinit()函数会创建两个新的进程,一个是procd,一个是/etc/preinit,下面来仔细分析一下:

/sbin/init进程是来自procd这个package里面的,不再使用busybox了,而且它是从内核调用过来的,所以它的pid是1,pid 0是内核本身。fork创建父子进程,子进程做一些procd的配置后退出,注意这时procd并不算真正起来,它的pid不是1;父进程继续创建父子进程,子进程调用/etc/preinit后退出。在这过程中/sbin/init的pid为1,始终没有让位。

    创建子进程执行/etc/preinit脚本时,此时PREINIT环境变量被设置为1,主进程(pid=1)同时使用uloop_process_add()把/etc/preinit子进程加入uloop进行监控,当/etc/preinit执行结束时回调plugd_proc_cb()函数把监控/etc/preinit进程对应对象中pid属性设置为0,表示/etc/preinit已执行完成
    创建子进程执行/sbin/procd -h/etc/hotplug-preinit.json,主进程同时使用uloop_process_add()把/sbin/procd子进程加入uloop进行监控,当/sbin/procd进程结束时回调spawn_procd()函数,spawn_procd()函数繁衍后继真正使用的/sbin/procd进程,这时procd的进程号将是1。

下面这个函数会用procd将/sbin/init进程替换,从而procd的进程号为1:



    从/tmp/debuglevel读出debug级别并设置到环境变量DBGLVL中,把watchdog fd设置到环境变量WDTFD中,最后调用execvp()繁衍/sbin/procd进程 

 

3. “/sbin/init”(下面内容主要来自网络)

这个进程以前是由busy box实现,但是现在由procd来实现了,找代码时不要找错位置。

int  main(int argc, char **argv)  
{  
    pid_t pid;  
  
    sigaction(SIGTERM, &sa_shutdown, NULL);  
    sigaction(SIGUSR1, &sa_shutdown, NULL);  
    sigaction(SIGUSR2, &sa_shutdown, NULL);  
  
    early();//-------->early.c  
    cmdline();  
    watchdog_init(1); //------->../watchdog.c  
  
    pid = fork();  
    if (!pid) {  
        char *kmod[] = { "/sbin/kmodloader", "/etc/modules-boot.d/", NULL };  
  
        if (debug < 3) {  
            int fd = open("/dev/null", O_RDWR);  
  
            if (fd > -1) {  
                dup2(fd, STDIN_FILENO);  
                dup2(fd, STDOUT_FILENO);  
                dup2(fd, STDERR_FILENO);  
                if (fd > STDERR_FILENO)  
                    close(fd);  
            }  
        }  
        execvp(kmod[0], kmod);  
        ERROR("Failed to start kmodloader\n");  
        exit(-1);  
    }  
    if (pid <= 0)  
        ERROR("Failed to start kmodloader instance\n");  
    uloop_init();  
    preinit();    //-------------->watchdog.c  
    uloop_run();  
  
    return 0;  
}  

early()

  • mount /proc /sys /tmp /dev/dev/pts目录(early_mount)
  • 创建设备节点和/dev/null文件结点(early_dev)
  • 设置PATH环境变量(early_env)
  • 初始化/dev/console

cmdline()

  • 根据/proc/cmdline内容init_debug=([0-9]+)判断debug级别

watchdog_init()

  • 初始化内核watchdog(/dev/watchdog)

加载内核模块

  • 创建子进程/sbin/kmodloader加载/etc/modules-boot.d/目录中的内核模块

preinit()

  • 创建子进程执行/etc/preinit脚本,此时PREINIT环境变量被设置为1,主进程同时使用uloop_process_add()把/etc/preinit子进程加入uloop进行监控,当/etc/preinit执行结束时回调plugd_proc_cb()函数把监控/etc/preinit进程对应对象中pid属性设置为0,表示/etc/preinit已执行完成

  • 创建子进程执行/sbin/procd -h/etc/hotplug-preinit.json,主进程同时使用uloop_process_add()把/sbin/procd子进程加入uloop进行监控,当/sbin/procd进程结束时回调spawn_procd()函数

  • spawn_procd()函数繁衍后继真正使用的/sbin/procd进程,从/tmp/debuglevel读出debug级别并设置到环境变量DBGLVL中,把watchdog fd设置到环境变量WDTFD中,最后调用execvp()繁衍/sbin/procd进程

watchdog

如果存在/dev/watchdog设备,设置watchdog timeout等于30秒,如果内核在30秒内没有收到任何数据将重启系统。用户状进程使用uloop定时器设置5秒周期向/dev/wathdog设备写一些数据通知内核,表示此用户进程在正常工作

/**
 * 初始化watchdog
 */
void watchdog_init(int preinit)

/**
 * 设备通知内核/dev/watchdog频率(缺省为5秒)
 * 返回老频率值
 */
int watchdog_frequency(int frequency)

/**
 * 设备内核/dev/watchdog超时时间
 * 当参数timeout<=0时,表示从返回值获取当前超时时间
 */
int watchdog_timeout(int timeout)

/**
 * val为true时停止用户状通知定时器,意味着30秒内系统将重启
 */
void watchdog_set_stopped(bool val)

signal

信息处理,下面为procd对不同信息的处理方法

  • SIGBUS、SIGSEGV信号将调用do_reboot() RB_AUTOBOOT重启系统
  • SIGHUP、SIGKILL、SIGSTOP信号将被忽略
  • SIGTERM信号使用RB_AUTOBOOT事件重启系统
  • SIGUSR1、SIGUSR2信号使用RB_POWER_OFF事件关闭系统

procd

procd有5个状态,分别为STATE_EARLYSTATE_INITSTATE_RUNNINGSTATE_SHUTDOWNSTATE_HALT,这5个状态将按顺序变化,当前状态保存在全局变量state中,可通过procd_state_next()函数使用状态发生变化

STATE_EARLY状态 - init前准备工作

  • 初始化watchdog
  • 根据"/etc/hotplug.json"规则监听hotplug
  • procd_coldplug()函数处理,把/dev挂载到tmpfs中,fork udevtrigger进程产生冷插拔事件,以便让hotplug监听进行处理
  • udevstrigger进程处理完成后回调procd_state_next()函数把状态从STATE_EARLY转变为STATE_INIT

STATE_INIT状态 - 初始化工作

  • 连接ubusd,此时实际上ubusd并不存在,所以procd_connect_ubus函数使用了定时器进行重连,而uloop_run()需在初始化工作完成后才真正运行。当成功连接上ubusd后,将注册servicemain_object对象,system_object对象、watch_event对象(procd_connect_ubus()函数),
  • 初始化services(服务)和validators(服务验证器)全局AVL tree
  • 把ubusd服务加入services管理对象中(service_start_early)
  • 根据/etc/inittab内容把cmd、handler对应关系加入全局链表actions中
  • 执行inittab的脚本,该脚本来自
    package/base-files/files/etc/inittab
    ::sysinit:/etc/init.d/rcS S boot
    ::shutdown:/etc/init.d/rcS K stop
    tts/0::askfirst:/bin/ash --login
    ttyS0::askfirst:/bin/ash --login
    tty1::askfirst:/bin/ash --login
    sysinit为系统初始化运行的 /etc/init.d/rcS S boot脚本
    shutdown为系统重启或关机运行的脚本
    tty开头的是,如果用户通过串口或者telnet登录,则运行/bin/ash --login

    askfirst和respawn相同,只是在运行前提示"Please press Enter to activate this console."
  • 顺序加载respawnaskconsoleaskfirstsysinit命令
  • sysinit命令把/etc/rc.d/目录下所有启动脚本执行完成后将回调rcdone()函数把状态从STATE_INITl转变为STATE_RUNNING
    当前启动转到运行 /etc/init.d/rcS S boot,该脚本来自
    package/base-files/files/etc/init.d/rcS
    和preinit类似,rcS也是一系列脚本的入口,其运行/etc/rc.d目录下S开头的的所
    有脚本(如果运行rcS K stop,则运行K开头的所有脚本)
    K50dropbear S02nvram S40network S50dropbear S96led
    K90network S05netconfig S41wmacfixup S50telnet S97watchdog
    K98boot S10boot S45firewall S60dnsmasq S98sysntpd
    K99umount S39usb S50cron S95done S99sysctl
    上面的脚本文件来自:
    package/base-files/files/etc/init.d
    target/linux/brcm-2.4/base-files/etc/init.d
    还有一些脚本来自各个模块,在install时拷贝到rootfs,比如dropbear模块
    package/dropbear/files/dropbear.init
    这些脚本先拷贝到/etc/init.d下,然后通过/etc/rc.common脚本,将init.d的脚本链接到/etc/rc.d目录下,并且根据 这些脚本中的START和STOP的关键字,添加K${STOP}和S${START}的前缀,这样就决定了脚本的先后的运行次序。

STATE_RUNNING状态

  • 进入STATE_RUNNING状态后procd运行uloop_run()主循环

trigger任务队列

数据结构

struct trigger {
    struct list_head list;

    char *type;

    int pending;
    int remove;
    int timeout;

    void *id;

    struct blob_attr *rule;
    struct blob_attr *data;
    struct uloop_timeout delay;

    struct json_script_ctx jctx;
};

struct cmd {
    char *name;
    void (*handler)(struct job *job, struct blob_attr *exec, struct blob_attr *env);
};

struct job {
    struct runqueue_process proc;
    struct cmd *cmd;
    struct trigger *trigger;
    struct blob_attr *exec;
    struct blob_attr *env;
};

接口说明

/**
 * 初始化trigger任务队列
 */
void trigger_init(void)

/**
 * 把服务和服务对应的规则加入trigger任务队列
 */
void trigger_add(struct blob_attr *rule, void *id)

/**
 * 把服务从trigger任务队列中删除
 */
void trigger_del(void *id)

/**
 * 
 */
void trigger_event(const char *type, struct blob_attr *data)

service

Name Handler Blob_msg policy
set service_handle_set service_set_attrs
add service_handle_set service_set_attrs
list service_handle_list service_attrs
delete service_handle_delete service_del_attrs
update_start service_handle_update service_attrs
update_complete service_handle_update service_attrs
event service_handle_event event_policy
validate service_handle_validate validate_policy

system

Name Handler Blob_msg policy
board system_board  
info system_info  
upgrade system_upgrade  
watchdog watchdog_set watchdog_policy
signal proc_signal signal_policy
nandupgrade nand_set nand_policy

shell调用接口

代码库路径: package/system/procd/files/procd.sh 设备上路径: /lib/functions/procd.sh

/etc/init.d/daemon

#!/bin/sh /etc/rc.common

START=80
STOP=20

USE_PROCD=1

start_service()
{
    procd_open_instance
    procd_set_param command /sbin/daemon
    procd_set_param respawn
    procd_close_instance
}


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

Openwrt学习笔记(四)——系统开机启动 的相关文章

  • openwrt opkg错误的问题解决办法之一

    解决方法 echo nameserver 114 114 114 114 gt tmp resolv conf rm f var lock opkg lock opkg update
  • ZeroTierr的moon云服务器搭建和使用

    搭建moon 本质上是在云服务器上建立一个moon服务器 也加入zerotier的Network ID 服务器记录请求路径来做类似于DNS的解析 让设备之间p2p直连 问题是ZeroTier One本身的服务器都在国外访问速度很慢 可以通过
  • openwrt下crontab定时任务实现

    openwrt下crontab定时任务实现 我的需求是需要加个定时执行脚本来监控智能网关运行的进程 一旦网关进程异常关闭 就能立即把它拉起来 从而保证网关运行的可靠性 这里我们用到了crontab服务 OpenWRT系统默认已经加入了cro
  • 解决GO语言编译程序在openwrt(mipsle架构)上运行提示Illegal instruction问题

    RT 最近在研究openwrt mipsle架构 上运行go语言编译出来的程序 一运行就报 Illegal instruction 这样的错误 百度和Google搜索了一遍 得出两种解决方案 PS 更新一遍 当时写这个文档的时候没有发现Go
  • 启明智显分享

    提示 作为Espressif 乐鑫科技 大中华区合作伙伴及sigmastar 厦门星宸 VAD合作伙伴 启明智显不仅用心整理了你在开发过程中可能会遇到的问题以及快速上手的简明教程供开发小伙伴参考 同时也用心整理了乐鑫及星宸科技的新产品 新方
  • openwrt luci使用本地软件源更新软件包,更新package.sig签名

    官方的源在国外 一般访问速度比较慢 本地源可以快速解决这个问题 有时自己编译的软件升级发布版本使用本地源 能够更好的维护与安装 为了保证兼容性 尽量使用同一个源提供的SDK打包的源软件 把编译出来的ipk文件上传到本地服务器 在索引中添加新
  • openwrt x86 版安装纪实

    1 下载源码 已有编译环境 直接在ubuntu 中 git openwrt 源码 https dev openwrt org wiki GetSource git clone b chaos calmer git github com op
  • 【openwrt】【编译问题】openwrt编译问题

    undefined reference to pthread once 在某次openwrt编译过程中出现了undefined reference to pthread once错误 具体报错信息如下 openwrt staging dir
  • Openwrt GCC 7.5编译sanitizer_internal_defs.h错误

    GCC 7 5 BINUTILS 2 31 1 报错信息 sanitizer internal defs h 72 error size of array assertion failed is negative 解决方法 修改下列文件 删
  • openwrt路由器(RP-LINK)安装python并设置开机启动程序

    由于项目需求 实际条件限制 需要在某台设备上运行一个python小程序 在工业机器人和云服务器之间实现信息转发的功能 因为机器人也需要通过路由器认证连接校园网 出于简化设备的考虑 不想每次跑程序还得开电脑 我决定尝试在路由器上运行这个程序
  • 红米ac2100 刷openwrt以及刷回记录

    redmiac2100 刷机 参考 手动升级漏洞固件 https wwx lanzoux com i6iqxhqp98f 或者百度网盘链接 https pan baidu com s 1H355Ym9p TLrVOux2w2b7Q 提取码
  • 编写 LuCI CBI 模型

    编写 LuCI CBI 模型 CBI模型是描述UCI配置文件结构的Lua文件 并且CBI解析器将lua文件转为HTML呈现给用户 所有 CBI 模型文件都必须返回类型为luci cbi Map的对象 CBI 模型文件的范围由 luci cb
  • Openwrt按键检测分析-窥探Linux内核与用户空间通讯机制netlink使用

    首先看一下Openwrt系统中关于按键功能的使用和修改 以18 06版本为例 按键功能实现在脚本中 比如18 06 package base files files etc rc button reset bin sh lib functi
  • openwrt下使用SDK编译ipk包遇到Package hiOpenwrt is missing dependencies for the following libraries: libc.so.

    openwrt下使用SDK编译ipk包遇到Package hiOpenwrt is missing dependencies for the following libraries libc so 6 问题 缺少 libc so 6 库 但
  • openwrt利用arp获取局域网设备IP

    openwrt利用arp获取局域网设备IP 文章目录 openwrt利用arp获取局域网设备IP 1 前言 2 ARP概念 3 arp局域网搜索设备实现思路和代码 1 前言 目前我们通过arp协议搜索局域网设备 根据局域网设备地址判断子设备
  • linux下安装awk

    sudo apt get install gawk 注意名称
  • openwrt 缺少 libc.so.6 libm.so.6 libpthread.so.0

    在开发openwrt时 编译内核的时候 自己写的代码在openwrt 编译报错 提示缺少依赖库文件 Package Gateway Auto is missing dependencies for the following librari
  • OpenWrt添加软件包(一)

    本文参考https wiki openwrt org zh cn doc devel packages http blog csdn net teddy99999 article details 17537545 OpenWrt是一个比较完
  • 获取openwrt wan口ip方法

    2020年7月30日14点39分 更新 之前写的方法 获取的是wan口的ip和网关 使用过程发现 以有线上网模式为例 获取wan口网关时 若网关中出现连续的1 1 会获取失败 表明该正则表达式是有问题的 即下面这个grep oE 之后的内容
  • jshn - 如何解析 json 包

    我想知道如何在openwrt上轻松解析json 我有 jhsn 来解析 json 这是我的程序 sh 脚本 download weather wget api openweathermap org data 2 5 weather id 2

随机推荐

  • Linux shell 暂停执行脚本

    bin bash current path pwd echo 当前路径 current path echo webapps path current path webapps echo 当前路径webapps子目录 webapps path
  • 玩具城

    官园小商品批发市场 路线 地铁2号线 车公庄 红桥市场路线 地铁5号线 天坛东门 来自 ITPUB博客 链接 http blog itpub net 118838 viewspace 625828 如需转载 请注明出处 否则将追究法律责任
  • 【系统】win11怎么退回win10

    根据微软官方提供的回滚方案显示 在升级Win11之后的10天之内 用户可以通过系统恢复选项将Win11还原Win10 操作方式也比较简单 大家可以打开系统设置 找到相应选项 选择并确认后即可轻松将Win11回退早期版本 详细操作步骤如下 一
  • C++学习(四六六)Multiple parse contexts are available for this file

    Note Multiple parse contexts are available for this file Choose the preferred one from the editor menu 参考 Multiple parse
  • Linux基础篇学习——常见系统命令:ls,pwd,cd,date,hwclock,passwd,su,clear,who,w,uname,uptime,last,dmesg,free,ps,top

    ls 显示指定目录中的内容 ls OPTION FILE OPTION a all 显示所有文件包括隐藏文件 l 列出长属性 显示出文件的属性与权限等数据信息 i 列出inode节点号 结合 l使用 d 显示目录本身 且不列出目录中的列表文
  • MD5算法分析及逆向详解

    题外话 最近在看加密与解密 看到加密算法部分 感觉对于初次接触的新手还是有些难度的 故写下该篇文章 算作一个引导吧 新手飘过 老鸟勿笑 基本原理 MD5的典型应用是对一段信息 Message 产生信息摘要 Message Digest 以防
  • springsecurity 获取token流程分析

    一 oauth token password模式获取token源码主要操作 1 定义请求参数 增加请求头 Authorization 否则 在请求参数使用 client id 注意 此头为base64拼接格式为 client id clie
  • 光耦隔离继电器驱动

    一 前言 因为吃亏了 而且不小 发个贴记录下 以后长记性 问题是由于不做光耦隔离 电路不稳定 做了光耦隔离 上电和断电瞬间被隔离器件上电抖动一下 明显不可以 错误的示范我就不发图了 二 环境 12V电源 stm32 驱动舵机 功率不大也不小
  • 一篇文章,从源码深入详解ThreadLocal内存泄漏问题

    原创文章 经验总结 从校招到A厂一路阳光一路沧桑 详情请戳www coderccc com 1 造成内存泄漏的原因 threadLocal是为了解决对象不能被多线程共享访问的问题 通过threadLocal set方法将对象实例保存在每个线
  • 在ISO/OSI参考模型中,网络层的主要功能是()----百度2016研发工程师笔试题(六)

    在ISO OSI参考模型中 网络层的主要功能是 正确答案 A 你的答案 A 正确 路由选择 拥塞控制与网络互连 提供可靠的端一端服务 透明地传送报文 数据格式变换 数据加密与解密 数据压缩与恢复 在通信实体之间传送以帧为单位的数据 添加笔记
  • Dynamics 365 安装后续

    之前说到Dynamics365安装时遇到的错误 这里将讲述最后一个错误的解决办法 这里指的是蓝色条选中的错误 根据错误的提示 我们需要去对Reporting Services进行安装和配置 首先说Reporting Services的安装
  • 树莓派Linux内核源码

    前期工作 配置好交叉编译工具链 交叉编译工具链详细教程 树莓派Linux内核源码下载 查看树莓派操作版本命令 uname r 源码下载 一 嵌入式裸机和带操作系统启动过程了解 C51 stm32启动过程 c直接操控底层寄存器实现相关业务 业
  • python代码编写规范

    最近两年的工作都是和运维相关 有时运维人员也会写一些python程序 但基本上都没有遵循相应的代码规范 一向粗暴 能用就行 既不考虑可读性也不考虑可维护性 作为一个开发人员有时候看他们写代码就很不舒服 今天就谈谈python写代码时的一些规
  • beforefieldinit释义

    首先让我们认识什么是 当字段被标记为beforefieldinit类型时 该字段初始化可以发生在任何时候任何字段被引用之前 这句话听起了有点别扭 接下来让我们通过具体的例子介绍
  • 黑苹果oc清除nvram_黑苹果完善之路-英特尔蓝牙「开启/关闭」功能完善教程

    小编开篇提醒 玩转黑苹果有风险 建议随时备份好自己的重要数据 很多机友安装黑苹果之后 打开蓝牙选项却发现 关闭蓝牙 选项是灰色的 无法进行选择 但是一些具有强迫症的机友内心可能会接受不了 所以就此出一期关于英格尔蓝牙驱动相关的详细教程 博通
  • 10 种机器学习算法的要点(附 Python 和 R 代码)

    http blog jobbole com 92021 本文由 伯乐在线 Agatha 翻译 唐尤华 校稿 未经许可 禁止转载 英文出处 SUNIL RAY 欢迎加入 翻译组 前言 谷歌董事长施密特曾说过 虽然谷歌的无人驾驶汽车和机器人受到
  • 什么是 IaaS? 基础结构即服务

    基础结构即服务 IaaS 是通过 Internet 配置和管理的即时计算基础结构 它是四类云服务中的一种 其余三种为软件即服务 SaaS 平台即服务 PaaS 和无服务器 IaaS 可根据需求快速纵向扩缩 你只需按实际使用量付费 它让你无需
  • 甘特图生产排程(APS)定制开发

    快速开发完成APS的数据可视化 订单展示 资源调度 智能排程等几乎全部功能模块 自动智能排程功能 提供专业需求分析师及开发团队 按需开发 全自动智能排程 这一APS的主要功能 能够实现自定义排程规则 一键式产生计划等高级功能 无需人工干预既
  • python虚拟环境理解 conda创建虚拟环境 pycharm配置运行环境

    python虚拟环境理解 conda创建虚拟环境 pycharm配置运行环境 一 什么是python的虚拟环境 python虚拟环境是为了让不同的项目能够在不同的python环境中运行 比如项目A需要python2 numpy3 1 项目B
  • Openwrt学习笔记(四)——系统开机启动

    1 内核启动 bootloader将kernel从flash中拷贝到RAM以后 bootloader将退出舞台 并将这个舞台交给了kernel 中间有些交接的细节过程 这里不赘述 我们直接从kernel的启动开始分析 不同平台的kernel