Uboot 命令是如何被使用的?

有什么問題請 發郵件至syyxy@outlook.com, 歡迎交流~??

?

在uboot代碼中命令的模式是這個樣子:

?

?

?

這樣是如何和命令行交互的呢?

?

在command.h 中, 我們可以看到如下宏定義

?

將其拆分出來:

#define U_BOOT_CMD(name,maxargs,rep,cmd,usage,help) \

???????? U_BOOT_CMD_COMPLETE(name,maxargs, rep,cmd,usage,help,NULL)

我們可以看到

U_BOOT_CMD(

???????? md,? 3,????? 1,????? do_mem_md,

???????? "memory display",

???????? "[.b, .w, .l] address [# of objects]"

);

?被define成了:

U_BOOT_CMD_COMPLETE(

???????? md,? 3,????? 1,????? do_mem_md,

???????? "memory display",

???????? "[.b, .w, .l] address [# of objects]",NULL

);

?

繼續:

?

#define U_BOOT_CMD_COMPLETE(name,maxargs,rep,cmd,usage,help,comp) \

???????? cmd_tbl_t __u_boot_cmd_##name Struct_Section = \

?????????????????? U_BOOT_CMD_MKENT_COMPLETE(name,maxargs,rep,cmd,usage,help,comp)

?

其中,##name 意思是 使用 name 替換 ##name

?

由此可見,上述被define成了:

cmd_tbl_t? ?__u_boot_cmd_md? Struct_Section = \

U_BOOT_CMD_MKENT_COMPLETE(md,????? 3,????? 1,????? do_mem_md, "memory display","[.b, .w, .l] address [# of objects]",NULL);

?

?

繼續:

?

#define U_BOOT_CMD_MKENT_COMPLETE(name,maxargs,rep,cmd,usage,help,comp) \

?

???????? {#name, maxargs, rep, cmd, usage, _CMD_HELP(help) _CMD_COMPLETE(comp)}

?

?

?

其中,#的作用是字符串化,意思是將 name 轉化成一個字符串。

?

具體## 和 # 請參閱如下文檔。

?

https://wenku.baidu.com/view/56ed000216fc700abb68fcdd.html

?

?

?

可以看到,U_BOOT_CMD_MKENT_COMPLETE(md,???????? 3,????? 1,????? do_mem_md, "memory display","[.b, .w, .l] address [# of objects]"); ??被define成了

{“md”, 3, 1, do_mem_md, "memory display", _CMD_HELP("[.b, .w, .l] address [# of objects]")? ?_CMD_COMPLETE(NULL)}

?

因此 U_BOOT_CMD_COMPLETE被定義成了:

cmd_tbl_t? ?__u_boot_cmd_md? Struct_Section = \

{

“md”,

?3,

1,

?do_mem_md,

?"memory display",

?_CMD_HELP("[.b, .w, .l] address [# of objects]")? _CMD_COMPLETE(NULL)

}

?

繼續:

由于 #define Struct_Section? __attribute__((unused, section(".u_boot_cmd"), aligned(4))),而

# define _CMD_COMPLETE(x)? ?x,

# define _CMD_HELP(x)? ?x,

(注意最后都有一個逗號) , 因此 上述被定義成了

cmd_tbl_t ?__u_boot_cmd_md ?__attribute__((unused, section(".u_boot_cmd"), aligned(4))) =

{

“md”,

?3,

1,

?do_mem_md,

?"memory display",

?"[.b, .w, .l] address [# of objects]",

?NULL

}

上述是我們的分析,接下來?通過將cmd_mem.c 預編譯得到

cmd_tbl_t __u_boot_cmd_md __attribute__((unused, section(".u_boot_cmd"), aligned(4))) =

{"md",

?3,

?1,

do_mem_md,

?"memory display",

"[.b, .w, .l] address [# of objects]",

((void *)0),};

完全一致,因此上述分析正確。

=================================我是分割線===================================

?

接下來第二步,這些命令是如何被使用的?

首先分析我們在第一步得到的結構體:

?

cmd_tbl_t __u_boot_cmd_md ?__attribute__((unused, section(".u_boot_cmd"), aligned(4))) =

{"md",

?3,

?1,

do_mem_md,

?"memory display",

"[.b, .w, .l] address [# of objects]",

((void *)0),};

?

__attribute__((unused, section(".u_boot_cmd"), aligned(4))) 說明,將這個結構體放到 .u_boot_cmd 段中,該段4字節對其,unused 代表 告訴編譯器 該函數或者變量可能不使用,GGC不要對其報警告。

請注意, 這里是將該結構體放到編譯出來的.o 文件的 .u_boot_cmd段,? ?沒有放到輸出文件的.u_boot_cmd。當鏈接的時候才能放到 輸出文件的?.u_boot_cmd段。

我們知道 GCC 默認的段中沒有 .u_boot_cmd 這個段,那么就要手動創建這個段。Uboot通過鏈接腳本創建的這個段, 圖片截取自u-boot.lds

?

?

?

既然說在會將指令放到 .u_boot_cmd 這個段,那么能不能證明呢?

以瀾起平臺為例,找到生成的中間文件 u-boot 通過objdump –t 找到symbol table 然后 grep 出u_boot_cmd使用如下指令,

/opt/Montage-tech/mips-4.3/bin/mips-linux-gnu-objdump  -t u-boot |grep u_boot_cmd >~/u-boot.txt

可以得到 如下cmd , 其中加紅 的是我們上述 md 命令 :8017964c l    d  .u_boot_cmd  00000000 .u_boot_cmd80179b70 g     O .u_boot_cmd 0000001c __u_boot_cmd_nf80179b1c g     O .u_boot_cmd 0000001c __u_boot_cmd_macmode801799e8 g     O .u_boot_cmd 0000001c __u_boot_cmd_cpu80179dbc g     O .u_boot_cmd 0000001c __u_boot_cmd_freeze_with_dioff80179c6c g     O .u_boot_cmd 0000001c __u_boot_cmd_sspi80179a90 g     O .u_boot_cmd 0000001c __u_boot_cmd_macinit8017964c g     O .u_boot_cmd 0000001c __u_boot_cmd_runapp801798d0 g     O .u_boot_cmd 0000001c __u_boot_cmd_mm80179b8c g     O .u_boot_cmd 0000001c __u_boot_cmd_env80179da0 g     O .u_boot_cmd 0000001c __u_boot_cmd_av_launch801797b8 g     O .u_boot_cmd 0000001c __u_boot_cmd_fatls8017987c g     O .u_boot_cmd 0000001c __u_boot_cmd_loady80179d14 g     O .u_boot_cmd 0000001c __u_boot_cmd_version80179b54 g     O .u_boot_cmd 0000001c __u_boot_cmd_ntt80179cf8 g     O .u_boot_cmd  0000001c __u_boot_cmd_usbboot80179748 g     O .u_boot_cmd 0000001c __u_boot_cmd_coninfo80179be0 g     O .u_boot_cmd 0000001c __u_boot_cmd_setenv80179d68 g     O .u_boot_cmd 0000001c __u_boot_cmd_tsi80179940 g     O .u_boot_cmd 0000001c __u_boot_cmd_cp801796bc g     O .u_boot_cmd 0000001c __u_boot_cmd_reset80179ca4 g     O .u_boot_cmd 0000001c __u_boot_cmd_false80179994 g     O .u_boot_cmd 0000001c __u_boot_cmd_loop80179a04 g     O .u_boot_cmd 0000001c __u_boot_cmd_nand801796d8 g     O .u_boot_cmd 0000001c __u_boot_cmd_bootm80179a58 g     O .u_boot_cmd 0000001c __u_boot_cmd_mpw80179978 g O .u_boot_cmd 0000001c __u_boot_cmd_base 80179bc4 g O .u_boot_cmd 0000001c __u_boot_cmd_printenv 80179c34 g O .u_boot_cmd 0000001c __u_boot_cmd_snf 801799cc g O .u_boot_cmd 0000001c __u_boot_cmd_sleep 80179ac8 g O .u_boot_cmd 0000001c __u_boot_cmd_macrx 80179c18 g O .u_boot_cmd 0000001c __u_boot_cmd_sf 80179d84 g O .u_boot_cmd 0000001c __u_boot_cmd_show_logo 80179764 g O .u_boot_cmd 0000001c __u_boot_cmd_echo 8017980c g O .u_boot_cmd 0000001c __u_boot_cmd_help 80179844 g O .u_boot_cmd 0000001c __u_boot_cmd_itest 80179df4 g .u_boot_cmd 00000000 __u_boot_cmd_end 80179d4c g O .u_boot_cmd 0000001c __u_boot_cmd_secure 80179780 g O .u_boot_cmd 0000001c __u_boot_cmd_exit 801796a0 g O .u_boot_cmd 0000001c __u_boot_cmd_goo 80179bfc g O .u_boot_cmd 0000001c __u_boot_cmd_run 80179c88 g O .u_boot_cmd 0000001c __u_boot_cmd_test 80179828 g O .u_boot_cmd 0000001c __u_boot_cmd_question_mark 80179908 g O .u_boot_cmd 0000001c __u_boot_cmd_mw 80179dd8 g O .u_boot_cmd 0000001c __u_boot_cmd_jpeg_logo 80179d30 g O .u_boot_cmd 0000001c __u_boot_cmd_vid 80179a3c g O .u_boot_cmd 0000001c __u_boot_cmd_mpr 80179ae4 g O .u_boot_cmd 0000001c __u_boot_cmd_bootp 80179ba8 g O .u_boot_cmd 0000001c __u_boot_cmd_editenv 80179898 g O .u_boot_cmd 0000001c __u_boot_cmd_loadimg 80179924 g O .u_boot_cmd 0000001c __u_boot_cmd_cmp 8017972c g O .u_boot_cmd 0000001c __u_boot_cmd_iminfo 80179860 g O .u_boot_cmd 0000001c __u_boot_cmd_loadb 801798ec g O .u_boot_cmd 0000001c __u_boot_cmd_nm 801797f0 g O .u_boot_cmd 0000001c __u_boot_cmd_fatwrite 801799b0 g O .u_boot_cmd 0000001c __u_boot_cmd_mtest 8017995c g O .u_boot_cmd 0000001c __u_boot_cmd_crc32 80179b38 g O .u_boot_cmd 0000001c __u_boot_cmd_dhcp 8017979c g O .u_boot_cmd 0000001c __u_boot_cmd_fatload 801797d4 g O .u_boot_cmd 0000001c __u_boot_cmd_fatinfo 80179cc0 g O .u_boot_cmd 0000001c __u_boot_cmd_true  801798b4 g O .u_boot_cmd 0000001c __u_boot_cmd_md 80179c50 g O .u_boot_cmd 0000001c __u_boot_cmd_source 80179aac g O .u_boot_cmd 0000001c __u_boot_cmd_mactx 80179cdc g O .u_boot_cmd 0000001c __u_boot_cmd_usb 80179b00 g O .u_boot_cmd 0000001c __u_boot_cmd_tftpboot 80179668 g O .u_boot_cmd 0000001c __u_boot_cmd_bdinfo 80179710 g O .u_boot_cmd 0000001c __u_boot_cmd_bootd 80179a20 g O .u_boot_cmd 0000001c __u_boot_cmd_nboot 8017964c g .u_boot_cmd 00000000 __u_boot_cmd_start 801796f4 g O .u_boot_cmd 0000001c __u_boot_cmd_boot 80179684 g O .u_boot_cmd 0000001c __u_boot_cmd_go 80179a74 g O .u_boot_cmd 0000001c __u_boot_cmd_macfilter 

如上,就是定義的結構如何被使用, 并且得到了證明。

=================================我是分割線===================================

接下來是最后一步,我們從命令行輸入指令是是如何和我們的 .u_boot_cmd 段連接起來的?

?

在 command.c 中的 num command_ret_t cmd_process(int flag, int argc, char * const argv[],

??????????????????????????? ?????? int *repeatable) 函數中 調用了 find_cmd, 以下為其實現。

cmd_tbl_t *find_cmd (const char *cmd)

{

???????? int len = &__u_boot_cmd_end - &__u_boot_cmd_start;

???????? return find_cmd_tbl(cmd, &__u_boot_cmd_start, len);

}

?

其中 __u_boot_cmd_end 和 __u_boot_cmd_start 為在u-boot.lds 中定義的字段

.u_boot_cmd : {

?__u_boot_cmd_start = .;

?*(.u_boot_cmd)

?__u_boot_cmd_end = .;

?}

?

等號后面的 ' . '?代表鏈接后的當前位置。在上一步中我們查到了 __u_boot_cmd_start 位置為 0x8017964c , __u_boot_cmd_end 位置為 0x80179df4。Len = 0x7A8

?

當我們輸入一個存在的指令后, ?find_cmd_tbl(cmd, &__u_boot_cmd_start, len); 會得到該指令的地址, ?然后調用 cmd_call 執行, 具體請看command.c 下的cmd_process 函數。

=================================我是分割線===================================

?

至此,Uboot 命令的流程已經說明白了。

?

=================================我是分割線===================================

此外, 對Uboot 有所研究的朋友在makefile 中發現了這么一句話:

?

GEN_UBOOT = \
UNDEF_SYM=`$(OBJDUMP) -x $(LIBBOARD) $(LIBS) | \
  sed -n -e 's/.*\($(SYM_PREFIX)__u_boot_cmd_.*\)/-u\1/p'|sort|uniq`;\
  cd $(LNDIR) && $(LD) $(LDFLAGS) $(LDFLAGS_$(@F)) $$UNDEF_SYM $(__OBJS) \
  --start-group $(__LIBS) --end-group $(PLATFORM_LIBS) \
  -Map u-boot.map -o u-boot

這句話解析如下 :

首先 在Makefile中,??`?` 代表 執行shell代碼, 讓我們把該代碼拿出來 :

$(OBJDUMP) -x $(LIBBOARD) $(LIBS) | \
  sed -n -e 's/.*\($(SYM_PREFIX)__u_boot_cmd_.*\)/-u\1/p'|sort|uniq?

$(OBJDUMP)? 代表 objdump 工具,??$(LIBBOARD)??$(LIBS) 明顯代表一些庫文件, $(SYM_PREFIX) 代表前綴,這些對我們分析問題無關緊要 ,這里我們將其簡化。于是上述代碼簡化成了:

objdump -x libtest.a | sed -n -e? 's/.*\(__u_boot_cmd_.*\)/-u\1/p'|sort|uniq?

objudmp -x??libtest.a? 可以得到?libtest.a 庫中所有的 symbol, 包括函數,數組,文件等,這里我們簡稱為符號 .

然后將得到的符號通過"管道" 傳遞給sed 進行編輯。接下來對sed 進行分析:

sed -n -e??'s/.*\(__u_boot_cmd_.*\)/-u\1/p'?

sed -n -e 代表使用 silent 模式,以及直接在命令行進行編輯動作。

'? ?'? 兩個單引號之間表示將要進行的動作 。?

標記成黑色的?s 代表會進行替換操作('s/要被取代的字串/新的字串/').

p 代表打印出來。

因此上面的表達式的最淺顯的意思是:

將? ?.*\(__u_boot_cmd_.*\)??替換為??-u\1? ??

并且由于sed 支持正則表達式, 在正則表達式中括號()代表分組, 使用 \1 \2 \3 獲取第一個分組,第二個分組, 第三個分組。因此上述表達式進一步被理解為:

將???.*__u_boot_cmd_.*??替換為??-u.*__u_boot_cmd_.*??

請注意,? 上面的字符串和上上面字符串的不同。

在正則表達式中 . 代表任意字符(不包括空格),*? 代表任意個, 因此我們將上述語句按照漢語翻譯一遍:

使用?objdump -x? 解析???libtest.a 得到一些符號,將這些符號使用sed 進行處理, 處理方式是: 找到符號中有__u_boot_cmd_字段的那一部分, 然后在這些字段前面加上-u。

接下來?|sort|uniq? 就比較好理解了,就是進行排序和消除重復。

然后將其賦值給??UNDEF_SYM?. 至此?

UNDEF_SYM=`$(OBJDUMP) -x $(LIBBOARD) $(LIBS) | \
  sed -n -e 's/.*\($(SYM_PREFIX)__u_boot_cmd_.*\)/-u\1/p'|sort|uniq`;? 這一部分分析完畢, 總結來說就是 找到一些字段,在字段前面加上-u ,最后賦值給?UNDEF_SYM 這個變量。

?

接下來分析:

cd $(LNDIR) && $(LD) $(LDFLAGS) $(LDFLAGS_$(@F)) $$UNDEF_SYM $(__OBJS) \
  --start-group $(__LIBS) --end-group $(PLATFORM_LIBS) \
  -Map u-boot.map -o u-boot

其實這一點沒什么好說的, 無非就是執行ld 鏈接,然后生成u-boot這個文件, 這里需要注意的是 在其中使用了??UNDEF_SYM 變量,UNDEF_SYM 代表-uxxx__u_boot_cmd_xxx ,這是什么意思呢??

通過 man ld 可以找到 :

  -u symbol--undefined=symbolForce symbol to be entered in the output file as an undefined symbol.  Doing this may, for example, trigger linking of additional modules from standard libraries.  -u may be repeated with different option argumentsto enter additional undefined symbols.  This option is equivalent to the "EXTERN" linker script command.

?


也就是說在可執行文件中通過-u可以插入未定義變量。

?這樣一來, 也就明白了 這段代碼的目的是u_boot_cmd?變量插入到可執行文件中。?

?

接下來繼續最開始的問題:? 既然u-boot 使用?__attribute__((unused, section(".u_boot_cmd"), aligned(4)))? 的方式將uboot 的指令插入到 輸出文件的? .u_boot_cmd? 段, 那??GEN_UBOOT? 這個 ‘函數' 的意義是什么呢? 將重復的工作在做一遍嗎 ?

?

這個問題也困擾了我很久,直到本篇文檔寫完也沒有頭緒, 我的分析過程簡單的說一下,?

使用ld -u 插入符號, 一般都會在 ?.strtab? ?這個段中,? 可以通過 readelf -S test.out,可以找到???.strtab? ?這個段的index ,比如說是 38, 然后通過 readelf? -x38??test.out ,查看源文件。可以清楚的看到這些字段。

但是使用???__attribute__((unused, section(".u_boot_cmd"), aligned(4)))?? 的方式鏈接器會自動的將 我們定義的變量的字段系寫入到? ??.strtab? 這個section 中(大家可以寫一個簡單的小程序驗證下,我之前驗證過,但是代碼沒有保存下來)。 這樣看來 就重復了。 因此從目前來看,?GEN_UBOOT? 是沒有什么用的。當然, 如果果有哪位朋友知道原因,請發郵件給我 syyxy@outlook.com 或者評論一下,感激不盡。

?

轉載于:https://www.cnblogs.com/syyxy/p/8992446.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/389737.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/389737.shtml
英文地址,請注明出處:http://en.pswp.cn/news/389737.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

2029. 石子游戲 IX

2029. 石子游戲 IX Alice 和 Bob 再次設計了一款新的石子游戲。現有一行 n 個石子,每個石子都有一個關聯的數字表示它的價值。給你一個整數數組 stones ,其中 stones[i] 是第 i 個石子的價值。 Alice 和 Bob 輪流進行自己的回合,Alice 先手…

大數據可視化應用_在數據可視化中應用種族平等意識

大數據可視化應用The following post is a summarized version of the article accepted to the 2020 Visualization for Communication workshop as part of the 2020 IEEE VIS conference to be held in October 2020. The full paper has been published as an OSF Preprint…

Windows10電腦系統時間校準

有時候新安裝電腦系統,系統時間不對,需要主動去校準系統時間。1、點擊時間 2、日期和時間設置 3、其他日期、時間和區域設置 4、設置時間和日期 5、Internet 時間 6、點擊立即更新,如果更新失敗就查電腦是否已聯網,重試點擊立即更…

webpack問題

Cannot find module webpack/lib/node/NodeTemplatePlugin 全局:npm i webpack -g npm link webpack --save-dev 轉載于:https://www.cnblogs.com/doing123/p/8994269.html

397. 整數替換

397. 整數替換 給定一個正整數 n ,你可以做如下操作: 如果 n 是偶數,則用 n / 2替換 n 。 如果 n 是奇數,則可以用 n 1或n - 1替換 n 。 n 變為 1 所需的最小替換次數是多少? 示例 1: 輸入:…

pd種知道每個數據的類型_每個數據科學家都應該知道的5個概念

pd種知道每個數據的類型意見 (Opinion) 目錄 (Table of Contents) Introduction 介紹 Multicollinearity 多重共線性 One-Hot Encoding 一站式編碼 Sampling 采樣 Error Metrics 錯誤指標 Storytelling 評書 Summary 摘要 介紹 (Introduction) I have written about common ski…

td

單元格td設置padding,而不能設置margin。轉載于:https://www.cnblogs.com/fpcbk/p/9617629.html

清除浮動的幾大方法

對于剛接觸到html的一些人經常會用到浮動布局,但對于浮動的使用和清除浮動來說是大為頭痛的,在這里介紹幾個關于清除浮動的的方法。如果你說你要的就是浮動為什么要清除浮動的話,我就真的無言以對了,那你就當我沒說。 關于我們在布…

xgboost keras_用catboost lgbm xgboost和keras預測財務交易

xgboost kerasThe goal of this challenge is to predict whether a customer will make a transaction (“target” 1) or not (“target” 0). For that, we get a data set of 200 incognito variables and our submission is judged based on the Area Under Receiver Op…

2017. 網格游戲

2017. 網格游戲 給你一個下標從 0 開始的二維數組 grid ,數組大小為 2 x n ,其中 grid[r][c] 表示矩陣中 (r, c) 位置上的點數。現在有兩個機器人正在矩陣上參與一場游戲。 兩個機器人初始位置都是 (0, 0) ,目標位置是 (1, n-1) 。每個機器…

HUST軟工1506班第2周作業成績公布

說明 本次公布的成績對應的作業為: 第2周個人作業:WordCount編碼和測試 如果同學對作業成績存在異議,在成績公布的72小時內(截止日期4月26日0點)可以進行申訴,方式如下: 畢博平臺的第二周在線答…

幣氪共識指數排行榜0910

幣氪量化數據在今天的報告中給出DASH的近期買賣信號,可以看出從今年4月中旬起到目前為止,DASH_USDT的價格總體呈現出下降的趨勢。 轉載于:https://www.cnblogs.com/tokpick/p/9621821.html

走出囚徒困境的方法_囚徒困境的一種計算方法

走出囚徒困境的方法You and your friend have committed a murder. A few days later, the cops pick the two of you up and put you in two separate interrogation rooms such that you have no communication with each other. You think your life is over, but the polic…

2016. 增量元素之間的最大差值

2016. 增量元素之間的最大差值 給你一個下標從 0 開始的整數數組 nums &#xff0c;該數組的大小為 n &#xff0c;請你計算 nums[j] - nums[i] 能求得的 最大差值 &#xff0c;其中 0 < i < j < n 且 nums[i] < nums[j] 。 返回 最大差值 。如果不存在滿足要求的…

Zookeeper系列四:Zookeeper實現分布式鎖、Zookeeper實現配置中心

一、Zookeeper實現分布式鎖 分布式鎖主要用于在分布式環境中保證數據的一致性。 包括跨進程、跨機器、跨網絡導致共享資源不一致的問題。 1. 分布式鎖的實現思路 說明&#xff1a; 這種實現會有一個缺點&#xff0c;即當有很多進程在等待鎖的時候&#xff0c;在釋放鎖的時候會有…

resize 按鈕不會被偽元素遮蓋

textarea默認有個resize樣式&#xff0c;效果就是下面這樣 讀 《css 揭秘》時發現兩個亮點&#xff1a; 其實這個屬性不僅適用于 textarea 元素&#xff0c;適用于下面所有元素&#xff1a;elements with overflow other than visible, and optionally replaced elements repre…

平臺api對數據收集的影響_收集您的數據不是那么怪異的api

平臺api對數據收集的影響A data analytics cycle starts with gathering and extraction. I hope my previous blog gave an idea about how data from common file formats are gathered using python. In this blog, I’ll focus on extracting the data from files that are…

709. 轉換成小寫字母

709. 轉換成小寫字母 給你一個字符串 s &#xff0c;將該字符串中的大寫字母轉換成相同的小寫字母&#xff0c;返回新的字符串。 示例 1&#xff1a;輸入&#xff1a;s "Hello" 輸出&#xff1a;"hello"示例 2&#xff1a;輸入&#xff1a;s "here…

前端技術周刊 2018-09-10:Redux Mobx

前端快爆 在 Chrome 10 周年之際&#xff0c;正式發布 69 版本&#xff0c;整體 UI 重新設計&#xff0c;同時iOS 版本重新將工具欄放置在了底部。API 層面&#xff0c;支持了 CSS Scroll Snap、前端資源鎖 Web Lock API、WebWorker 里面可以跑的 OffscreenCanvas API、toggleA…

PPT制作

0.【整體風格】整體風格統一 界面排版 0.1 字體大小&#xff1b; 0.2 字體顏色&#xff1b; 0.3 字體的種類統一(不是指只取一種字體)&#xff09; 1.【表達】結構化表達&#xff1b; 2.【取色】取色風格統一&#xff1b; 技巧&#xff1a;主色不超過三種&#xff0c;色彩不宜多…