1. 程式人生 > >Openwrt學習筆記(四)——系統開機啟動

Openwrt學習筆記(四)——系統開機啟動

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 的主次裝置號,我們可以用下面的命令來確認:

[email protected]:/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
}


相關推薦

Openwrt學習筆記——系統開機啟動

1. 核心啟動 bootloader將kernel從flash中拷貝到RAM以後,bootloader將退出舞臺,並將這個舞臺交給了kernel。中間有些交接的細節過程,這裡不贅述,我們直接從kernel的啟動開始分析。 不同平臺的kernel啟動時,最開始部分的彙編指令碼

朱老師ARM裸機學習筆記:S5PV210啟動過程詳解

常用器件特性 記憶體: SRAM 靜態記憶體 特點就是容量小、價格高,優點是不需要軟體初始化直接上電就能用 DRAM 動態記憶體 特點就是容量大、價格低,缺點就是上電後不能直接使用,需要軟體初始化後才可以使用。 微

Unity3D之Mecanim動畫系統學習筆記:Animation State

大致 面板 輸入 jpg any 動畫播放 速度 nsf 顯示 動畫的設置 我們先看看Animation Clip的一些設置: Loop time:動畫是否循環播放。 下面出現了3個大致一樣的選項: Root Transform Rotation:表示為播放動畫

Cocos Creator 系統學習筆記--座標系統

座標系統: cc.Class({ extends: cc.Component, properties: { }, //座標系統 //cc.Vec2是一個建構函式,可以使用new來建立 /* * 1: cc.Vec2

Cocos Creator 系統學習筆記--坐標系統

creator 分享圖片 on() bubuko 向量 xtend inter div type 坐標系統: cc.Class({ extends: cc.Component, properties: { }, /

資料庫系統概論學習筆記:SQL的簡單應用之資料查詢

嗯……最實用的部分,其他都可以不會,唯獨這個必須熟練吧 —— 資料查詢 這篇筆記主要是例子,使用的例子是《筆記(三)》中定義的學生選課關係。使用的SQL語句可能與教材上有些出入,因為我使用的是MySQL 5.7,對SQL語言支援可能會有些許差異。

Cocos2d-x學習筆記 布景層的加入移除

dcl from position 顏色 顯示地圖 idt col 分享 學習 布景層類也就是CCLayer類,每一個遊戲場景中都能夠有非常多層,每一層負責各自的任務。顯示地圖、顯示人物等。同一時候層還是一個容器,能夠放入文本、圖片和菜單。構成遊戲中一個個UI。這次

機器學習筆記機器學習可行性分析

資料 表示 image 隨機 訓練樣本 -s mage 例如 lin 從大量數據中抽取出一些樣本,例如,從大量彈珠中隨機抽取出一些樣本,總的樣本中橘色彈珠的比例為,抽取出的樣本中橘色彈珠的比例為,這兩個比例的值相差很大的幾率很小,數學公式表示為: 用抽取到的樣本作為訓練

Python_sklearn機器學習學習筆記decision_tree決策樹

min n) 空間 strong output epo from 標簽 ict # 決策樹 import pandas as pd from sklearn.tree import DecisionTreeClassifier from sklearn.

Python學習筆記 列表生成式_生成器

rec triangle 小寫 ont 無限 end clas 普通 執行過程 筆記摘抄來自:https://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000/001431

.net core 2.0學習筆記:遷移.net framework 工程到.net core

編譯 its evel hashtable ref 學習筆記 inline null 創建 在遷移.net core的過程中,第一步就是要把.net framework 工程的目標框架改為.net core2.0,但是官網卻沒有提供轉換工具,需要我們自己動手完成了

ES6學習筆記—— async 函數

ons fst cte code span pre getname 普通 聲明 await 是 async wait 的簡寫, 是 generator 函數的語法糖。 async 函數的特點: async 聲明一個方法是異步的,await 則等待這個異步方法執行的完

Hibernate學習筆記 --- 映射基本數據類型的List集合

varchar prim drop n) 進行 lis auth pos 方案 集合按其內元素的數據類型分為兩種:基本數據類型集合及復雜對象類型集合,Hibernate對於兩類集合提供不同的映射方式。(在類上以@Embeddable註解的復雜對象數據類型處理方式同基本數據類

java學習筆記:import語法

employee sign cnblogs java 調用 變量賦值 temp 職位 求職 Import 語法是給編譯器尋找特定類的適當位置的一種方法。 創建一個Employee 類,包括四個實體變量姓名(name),年齡(age),職位(designation)和薪水(s

Cesium學習筆記Camera

ttr can str efault 簡單的 list 事件處理 get provider http://blog.csdn.net/HobHunter/article/details/74909641 Cesium 相機控制場景中的視野。操作相機的方法有很多,如

python學習筆記-數據類型

rand 兩個 urn 浪費 line 平年 randint .com .cn 0. 在 Python 中的數據類型詳解 http://www.cnblogs.com/scios/p/8026576.html 1. 為什麽布爾類型(bool)的 True 和 False 分

Nodejs學習筆記-----Buffer

pretty 成員 保存 n) tin 設置 amp 個數 普通 Node.js Buffer(緩沖區) JavaScript 語言自身只有字符串數據類型,沒有二進制數據類型。 但在處理像TCP流或文件流時,必須使用到二進制數據。因此在 Node.js中,定義了一個 Buf

Elasticsearch學習筆記ElasticSearch分布式機制

clas cse 負載均衡 丟失 數據 不可 分布式 復雜 發生 一、Elasticsearch對復雜分布式機制透明的隱藏特性 1、分片機制: (1)index包含多個shard,每個shard都是一個最小工作單元,承載部分數據,lucen

DeepLearning.ai學習筆記卷積神經網絡 -- week1 卷積神經網絡基礎知識介紹

除了 lock 還需要 情況 好處 計算公式 max 位置 網絡基礎 一、計算機視覺 如圖示,之前課程中介紹的都是64* 64 3的圖像,而一旦圖像質量增加,例如變成1000 1000 * 3的時候那麽此時的神經網絡的計算量會巨大,顯然這不現實。所以需要引入其他的方法來

python學習筆記字符串及字符串操作

默認 小寫字母 是不是 swap git 查找字符 英文 去掉 title 字符串   字符串可以存任意類型的字符串,比如名字,一句話等等。 字符串還有很多內置方法,對字符串進行操作,常用的方法如下: 1 name1=‘hello world‘ 2 print(nam