每特17劃

及時當勉勵 2004/06/07

發佈的內容將搬離 Facebook — 2022-10-31

發佈的內容將搬離 Facebook

近期決定把 Facebook 上自己發佈的內容,搬離 Facebook,並減少在 Facebook 上活動的方式 。

具體的細節大略是:

  • 在我個人空間,由我自己發佈的內容,會搬到 Blog 上。
  • 於發佈內容下的留言跟按讚等資料,會盡可能依原樣搬遷。(留言會顯示,但按讚資料不顯示)
  • 搬完後的內容,會自 Facebook 上刪除。
  • 自己儲存的書籤、訂閱清單,會慢慢備份至其他地方後清除
  • 若未來有發文之需,會以先在 Blog 發表後,再轉載至 Facebook 的方式。

此計劃預計於通知發佈一週後逐步進行。

暫不更動的部份為:

  • 別人 Tag 我的照片,或別人 Tag 我的貼文,則仍保留不動。(多數於 2015 年以前的不會動到)
  • 我上傳到其他群組、朋友的照片、留言、…等資料仍保留不動。

主要原因如下:

  • Facebook 容易漏失朋友的資訊。
  • 多數的動態,來自於少數的活躍人士。其他人往往被廣告和 KOL (註: Key Opinion Leader, 意見領袖)給插隊跟淹沒了。
    • 大多數的時間是消耗在從沒見過面的人,以及反覆跳過都跳過不完的廣告的身上。
  • Facebook 已反賓為主的干擾並介入並我對資訊的挑選跟判斷。
  • 演算法之下,人際關係變得更加極端,親者越親、疏者越疏、仇者越仇。

基於上述種種因素, Facebook 早已不再是個良性的社群網站。 然而 Facebook 裡仍留有過往許多回憶跟照片,也有很多朋友仍透過 Facebook 聯繫。 因此,此次非完全離開,仍會維持使用作為交流互動的管道之一。

若想了解更多關於我對 Facebook 的觀察與看法,可參考我的網頁如下:

https://6bcf7279.info/2022/10/31/7e88ea6c/ (內容如下)

日常的訊息來自於活躍的少數

約莫 2018 年前後,我發現 Facebook 有一個角落功能,知道的人不多。 (註: 此功能已經被 Facebook 拿掉了,現在已經找不到了。) 具體進入該功能頁的路徑已經忘記了,但瀏覽器裡還紀錄著的書籤如下:

https://www.facebook.com/〈Username〉/friends?ft_ref=flsa

其中 〈Username〉 是每個人自訂的使用者名稱,可在 Facebook 的 General Account Settings 查到。 例如: 若你的個人頁面網址是

https://facebook.com/xyz321

那麼你的 〈Username〉 就是 xyz321,而對應的網址就是

https://www.facebook.com/xyz321/friends?ft_ref=flsa

在這功能頁面裡,會列出自己所有的朋友清單。而最重要的是,他還會顯示朋友清單中哪些還有未讀訊息

我原本需要在動態牆滑個半小時、一小時,才勉強獲得部份朋友的動態。 有這個功能之後,每次只需要 5 ~ 10 分鐘,就能大致跟完最近一兩天的消息。

在切換到這樣的模式之後,使用了一陣子,觀察到了一些現象如下:

  • 若有好的工具的幫助,追蹤朋友動態的真正時間不會太多。
  • 一週之內,約有 4~5 % 的人有新發文(約20幾位)。絕大多數人都很少發文。
  • 而這 4 ~ 5 % 的人之中,約有 2~3 成的人(約 4~6 位),是密集發文者。
    • 每天都有發文,也可能幾小時就貼一句心得、照片、或是轉貼文章。
    • 換算一下,約佔總比例 0.8 ~ 1.5 %

(註: 當時的朋友清單約 500 人上下)

當時對照 Facebook 預設的動態牆的訊息後,感覺動態牆的成份佔比如下(註: 主觀感受,非嚴格統計)

  • 密集發文者約佔 45 ~ 55 % (約滑 1 ~ 2 下看到一則)
  • Facebook 推播轉貼(朋友按讚或留言、熱門、背後有贊助) 約佔 25 ~ 35 % (約滑 2 ~ 4 下看到一則)
  • 廣告約佔 10~15 % (約滑 4 ~ 6 下看到一則)
  • 常發文的朋友 15 % (約滑 5 ~ 7 下看到一則)
  • 較少發文的朋友 5 % (約滑 15 ~ 20 下看到一則)

在得出實際觀察的結果後,我深刻了解到幾個事實:

  • 我們絕大多數在 Facebook 所獲得的資訊跟感受,很大程度是由少數人所左右。
  • 假設朋友清單的數量在 250 人上下,那麼只要有 2~4 位的朋友是密集發文者,且密集發文或轉貼同一主題的文章時。(例如: 電影、時事、體育新聞、…)。相關的主題就可能佔據動態訊息中的 3 成以上。
  • 當你在 Facebook 上看到
    • 你有幾個朋友在活躍的提及一個主題
    • 其他人也再討論這一個主題,例如: KOL、新聞轉貼、…(背後由 Facebook 演算法推播給你)
    • 上述加乘下來,可能佔比達 30 ~ 50 %。 (約滑 1 ~ 2 下就看到一則)
    • 此時,就會產生一種,好像身邊所有人都在關注這件事 的錯覺
    • 但跟實際的情況往往有一定的落差
  • 約 95 % 不常發文的,是沉默的大多數。在 Facebook 動態牆的佔比很弱勢。
    • 假設有 2 位密集發文者跟 5 位較少發文者,分別在一週內對同一主題表示意見。
    • 結果通常是,2位密集發文者的訊息,你當天就會注意到。而另5位較少發文者,你可能過一兩天,才看到其中一兩則。另有三位,你可能會在很久以後,偶然之下才發現原來他有發過文。

在這樣的環境下,沉默的多數感到不受重視,而更少發文。而密集發文的少數,感受到更多關注與互動,而更加的活躍的發文。

之前透過只列未讀訊息的功能,大致能彌補這些狀況。 可惜好景不常,這功能在我發現並使用不到 1 年後(約 2019 年前後),就被 Facebook 拿掉了…。

社群訊息的傳遞消聲

早期在社群,發佈活動訊息時,大多數人都能在短時間內收到通知訊息 。 但自從商業廣告,以及政治活動大舉進入 Facebook 後,也開始有了更多管制曝光的機制。 因此,即使有 2000 人的社群,發佈活動訊息,往往也只有 60 ~ 120 人次的曝光。

聽說付點錢去推播訊息,情況會改善很多。 但若連發個社群公告訊息,也需要用錢來換取曝光率的話,顯然己背離我們的初衷。

這感受在 Facebook 的後期越是明顯,群組的後台功能改版的幾次。 後面改版的方向,越來越偏向廣告經營的方式。 例如:

  • 有個分頁就叫 "成長(Growth)",提供你群組成長的數據
  • 顯示成員的統計訊息,像是 "男姓/女姓", "國家","地區", "年齡分佈"…
  • 顯示按讚數量跟互動行為的數據…

這些功能對於想要投放廣告的組織確實很有幫助。 但對於我們這些,以共同興趣為交集,不以營利為目的而自我組織的社群來說。 這些作為,都離耕耘社群的初衷越來越遠,長久下來讓社群經營越來越力不從心而漸漸走向枯萎。

寫照比喻:

長期以來,我們都自主提著水,去灌溉一顆樹。
不求什麼果實回報,只要樹長得茂盛有活力,看著開心就好。
但不知從何時開始,有管理員開始限制你,
你一公升的水,只能給你澆 100 CC。
有人按讚,就讓你多澆 30 CC。
想要倒完全部的水,得加錢。
管理員接著說,我們現在還提供統計數據後台
能告訊你,現在樹有幾公分高,有多少葉子,跟葉子的各項統計資訊。
並幫你分析樹的成長速度跟含金量,方便你計算灌溉的投資回報率如何如何。

聽完這一切,心中感嘆、百般無奈,只想回覆說,
你說的那些功能我們通通不想要,
能不能還原到那個能單純灌溉跟澆水的環境就好?!

動態牆會丟包朋友的訊息

假設,我一天有 200 則朋友的發文訊息,而 Facebook 另外想推播 200 則訊息的話。 我原本以為,我努力滑完 400 ( = 200 + 200 ) 則訊息,或更多訊息之後,Facebook 的動態牆最後都還是會上完所有 200 則朋友的發文訊息。

然而,實際狀況並非如此。不管是用預設的 "熱門貼文(Top posts)" 還是用 "最新(Most Recent)" 的排序方式,我一直滑,發現即使滑了十倍的量約 2000 則,200 則的朋友訊息中,還是只出現部份朋友的訊息。估計至少還有 3 成或更多的訊息沒顯示出來。甚至滑到後半段, Facebook 都開始重複顯示已經出現過的廣告時,就是不會顯示還沒讀過的朋友貼文。(註: 我有一一點入朋友清單作會對照,確認不是單獨個案,無法用被封鎖或反追蹤來解釋)。

這個現象說明,在 Facebook 動態牆上沒看到部份朋友的消息,並不是滑的訊息數不夠多,而是部份朋友的消息根本就沒有被排進去。 Facebook 不只是插入它的廣告而已,它還決定了讓你知道誰的消息,以及決定了讓你不知道誰的消息。

簡單來說,

  • Facebook 對於要推播給你的資訊跟廣告,一定是排好排滿。
  • 你所有朋友的訊息,Facebook 只挑部份的顯示,剩下的就直接丟包了。
  • 若你不想漏朋友的消息,你必須要自己一個一個點進去才會發現。(花太多時間了,幾乎不可能)
  • Facebook 會無形之中代替你決定,你看到誰的消息,你看不到誰的消息。

這個問題,證明 Facebook 從根本上已不再是一個可信任的社群網站平台。

Facebook 上的互動變得越來越極端

不確定從何時開始。感覺 Facebook 上的留言跟討論,有越來越激化的傾向。 之前還只侷限於時事、政治相關議題的討論下才有。但現在各類話題,都開始有這樣的狀況。

印象中,早期在使用時,良性互動比較多。當時留言互動的,大多有共同好友交集,即使意見不合,也會互留個情面。 但現在遇到越來越多案例,是在發文或留言不久後,就遇到不認識的人,開頭就來個劈個幾句,甚至有人身攻擊的味道,好似跟對方有仇似的。但明明就是個素未謀面的人。這現象,不只自己遇到,不時也看到其他朋友的貼文也有如此遭遇。

次數頻率越來越高之後,我開始認為此現象的背後可能不單純。在此,作了一次思想實驗如下:

假設一個人發文或留言後,而社群網站會透過一個機制演算法,產生一個通知清單來通知相關的人。在此先稱之為頭香通知名單。而這份名單,猜測可能會是由下列來源所組成:

  • 發文者的朋友清單
  • 留言者的朋友清單
  • 文章主題的訂閱者
  • 文章所屬的粉絲頁成員清單
  • …等等

這名單加總一下,數量多半有上千人。 而社群網站的演算法,將決定誰會先看到,並有更早的機會留言。 假設有兩種通知策略如下:

  • (A) 優先通知該發文者或留言者之朋友清單中的友好清單
  • (B) 優先通知最能刺激更多留言跟討論的活躍清單

若處於 (A) 的通知策略下,那發文者跟留言者,不難預期會感受到更多友好的良性互動。

但若是處於 (B) 的通知策略下呢?

一個 能刺激更多留言跟討論的清單 或許可能也包含有:

  • 以戳人為樂的戳樂(Troller)
  • 喜歡煽風點火,以引戰為樂的人 (註: 因為敵意螺旋而招致雙方更多回文辯論)
  • 心直口快,看到標題就先嘴個一兩句再說 (回應快速)
  • 常駐在網路上的專業小編(對指定議題回應極快,且有辯必回)
  • KOL (後面會有一堆人跟著回文附和)
  • … 以及任何系統演算法判別有 "會快速回文" 且 "帶出更多回文" 的活躍用戶

因此,在 (B) 的通知策略下,不難預期,會更容易遇到 "回應快速但來者不善" 的負面互動。

藉由上述的思考,可以推測出社群網站的演算法差異,很可能產生兩種完全不同的環境風氣。而這環境風氣,並不一定來自於社會的人性變化。個性溫和的社會,在演算法煽風點火下,可能會變得極端互戰;個性飆悍的社會,在演算法的安撫下,也可能變得友善熱情。

也就是說,我們目前遇到的問題,很大的可能是由社群網站的演算法所引起的。而如果問題是由演算法所引起,自然也無法靠個人調整來修正。

舉例來說,在發文遇到不友善的回應時,比較溫和內向的人常傾向先自我檢討跟退讓,以免引發更多爭端。然而,在 頭香通知名單 上千人的名單中,總是會有最刁鑽、最能雞蛋裡找骨頭的人。而當社群網站的演算法,目標設定是優先把這些最極端的人往該文章推送曝光時,問題總是能一再發生。畢竟欲加之罪、何患無辭。那麼這是發文者的問題呢? 還是社群網站的演算法的問題呢?

我認為,社群網站的演算法的問題更大一些。

然而,社群演算法掌握在 Facebook 手中,視為商業機密,從未公開揭露,也未有公示資訊揭露,真相無從得知。 現存許多大型社群網站也有相同的問題,例如: Twitter。

目前尚沒有任何可信任的獨立機構、機制,可檢視並制衡這些社群網站的演算法問題。

結論

Facebook 仍是一個強大的網路服務,也提供許多便利的功能。 但對於在意資訊收集自主性的人來說,建議降低對 Facebook 的依賴,並保持一定的距離。

備註

在 Google Chrome 瀏覽器下,可按 F12 並在 Console 分頁的輸入列,輸入下述的 javascript script:

function to_scroll()
{
	console.log('Scroll to the bottom...');
	window.scrollTo(0, document.body.scrollHeight);
	setTimeout(to_scroll, 7*1000);
}

to_scroll();

瀏覽器會自動定時隔 7 秒滑至頁面的底端,效果相當於每 7 秒自動按一次 〈End〉 鍵。

不過現在這個方法可能要修改,因為 Facebook 後來有修改,在往下滑時,會將先前的內容刪除掉。

竹筷捲軸法 Chopstick Scroll Craft method — 2022-10-23

竹筷捲軸法 Chopstick Scroll Craft method

3D列印完,常常會需要清潔噴頭。尤其是較黏的材質,例如: PETG ,若沒清潔的話,殘留的部份,可能會影響到後面第一層列印的成功率。

先前試過噴頭清潔銅刷,有效果,但會刮噴頭跟週邊的矽膠套,感覺比較暴力。 加上刷毛用了容易叉開,幾次之後就不好用了。

後來,某次看到一個網頁有介紹到用棉花棒來清潔: https://vmaker.tw/archives/4860 。 嘗試後,覺得比銅刷好用,之後便持續用了好一陣子。 棉花棒雖然好用,但是長期下來,消耗的速度也蠻快的。 常常用沒幾下就得丟掉換一根,感覺有點浪費。 此外,在清潔時,力道不易掌握,容易滑開。

直到去年(2021-09-30)在逛網頁時,偶然發現國外有人創作了這個東西:

其中,我對墊子自兩邊夾擠噴頭的清潔方式,印象特別深刻。

受到上述的啟發之後,幸運地在某天就突然靈光一閃,想出用竹筷和廚房紙巾作出了這個小工具。

在此分享如下:

準備

材料項目如下:

  • 廚房紙巾
  • 竹筷(圓柱為佳)
  • 雙面魔鬼氈x2(各約 8 公分長)

其中,魔鬼氈也用其他可彈性固定的材料取代,例如:橡皮筋、鬆緊帶、束帶…

步驟

撕一張紙巾,平攤在桌上,並將竹筷分別置於兩端。 (註:建議此處筷子一正一反擺放)

於紙巾兩側往內折一小段,覆蓋並固定筷子。 然後以筷子為捲軸軸心,由兩端同時往中間捲收。 (註: 若操作不熟悉,可利用少許膠帶輔助固定)

收齊併儱之後,以魔鬼氈將竹筷的頭尾都套住固定起來。 (註: 可調整力道,讓筷子不鬆開,但仍保有捲動的空間)

完成後,成品外觀如下:

使用方式

在竹筷捲軸的中間有一道溝槽。 將溝槽對準靠入噴頭 ,然後將竹筷依著噴頭接觸面前後拉動,即可清潔噴頭 。 也可變換角度跟方位,清潔噴頭的各個接觸面。

在清潔之後,接觸面已髒掉時,可轉動捲軸,轉動調整至新的乾淨紙面。 使用得宜的話,一張廚房紙巾的使用次數可達 5~8 次。

結論

這個方法的優點有:

  • 操作簡單、快速、安全
  • 材料易於取得
  • 耗材(廚房紙巾)便宜

自去年(2021) 發想出來後,覺得非常好用,一直用到現在。 但一直擱著沒有正式發表出來,今天終於花了點時間,把照片跟內容弄一弄上傳,完成這件事。

希望以上的內容對大家有幫助。

為什麼刷 LineageOS 時是用 `.zip` 而不是 `.img`? — 2022-10-11

為什麼刷 LineageOS 時是用 `.zip` 而不是 `.img`?

近期在 Hacking Thursday 聚會和 Pellaeon Lin 在協助阿寬刷 LineageOS 的過程中,阿寬問道:

為什麼是刷 `.zip` 檔,而不是 `.img` 檔?

這個疑問題的背景是在,LineageOS 的刷機過程中的兩個步驟:

  • fastboot flash boot lineage-19.1-20220925-recovery-flame.img
  • adb sideload lineage-19.1-20220925-nightly-flame-signed.zip

前者,是將 image 檔案資料寫進 boot 分割區,容易理解。但後者卻不同,是採用 .zip 檔而不採用 .img 的方式寫入。為什麼呢?

當下,我簡單的回應說,這個 .zip 檔裡,還是有包 image 檔案,Android 的工具會解壓縮並解析裡面的描述檔,然後再依裡面的參數去刷 image 。因為當時是憑印象回答,且自己之前也有些疑惑沒解開,回想起來總感到心虛。所以決定進一步爬一下 source code 來確認跟檢驗一下。

Q: 這個 zip 檔案內容是什麼?

解開 lineage-19.1-20220925-nightly-flame-signed.zip 並瞧了一下,結構大致如下:

.
├── apex_info.pb
├── care_map.pb
├── META-INF
│   └── com
│       └── android
│           ├── metadata
│           ├── metadata.pb
│           └── otacert
├── payload.bin
└── payload_properties.txt

其中 payload.bin 就是將刷入的 image 檔案,同時有 payload_properties.txt 輔助 checksum 檢查。

FILE_HASH=WlmU5qsJP4G449TJz3DYQjQuHiPKCJJGj/7fyHcICRI=
FILE_SIZE=1094414259
METADATA_HASH=Husi2CZRsqijFVEd5aWTK4gnxiN3kTGKfmd8EliY8dU=
METADATA_SIZE=134083

另外,還有 META-INF/com/android/metadata 的檔案,描述參數如下:

ota-property-files=payload_metadata.bin:41:134350,payload.bin:41:1094414259,payload_properties.txt:1094414352:156,care_map.pb:1094414549:732,metadata:1094416436:610,metadata.pb:1094415385:992  
ota-required-cache=0
ota-streaming-property-files=payload.bin:41:1094414259,payload_properties.txt:1094414352:156,care_map.pb:1094414549:732,metadata:1094416436:610,metadata.pb:1094415385:992  
ota-type=AB
post-build=google/flame/flame:12/SQ3A.220705.003.A1/8672226:user/release-keys
post-build-incremental=6fd1676fd5
post-sdk-level=32
post-security-patch-level=2022-09-05
post-timestamp=1664086215
pre-device=flame

Q: adb 的 source code 放在那裡?

因為 .zip 是透過 adb sideload 指令執行,所以先查 adb source code。但找 adb 的 source code 花了點時間。一開始 google 搜尋找到 platform/system/core/adb/adb.c 的路徑,但在最新的 git repository 卻找不到。

後循線索發現:

  • 先是 adb/adb.c 於 2015-02-25 時 (commit: bac3474) 改為 C++ 的 adb/adb.cpp
  • 再於 2020-10-23 (commit: a88ef8c) 從 system/core/adb 搬到 packages/modules/adb

所以目前 source code 的最新位置是在:

https://android.googlesource.com/platform/packages/modules/adb

Q: 這些參數和檔案是在那裡處理的?

查了 adb 的 source code 之後發現 adb sideload 只是將 .zip 檔案傳進去,並非真正處理的地方。真正處理的地方,後來發現是在 recovery 相關的程式碼,位置如下:

https://android.googlesource.com/platform/bootable/recovery

其中,在 install/install.cpp 有函式呼叫流程如下:

… => InstallPackage() => VerifyAndInstallPackage() => TryUpdateBinary()

而在 TryUpdateBinary() 的函式中,有分成兩個 cases:

  • SetUpAbUpdateCommands() ,這是用於 AB type 的 case
    • 這個 case 會根據 payload_properties.txt, payload.bin 作刷 image 的動作
  • SetUpNonAbUpdateCommands() , 這是用於 AB type 的 case
    • 這個 case 會去找這個檔案 META-INF/com/google/android/update-binary 並執行之

對應的 source 區段如下:

  if (auto setup_result =
          package_is_ab
              ? SetUpAbUpdateCommands(package_path, zip, pipe_write.get(), &args)
              : SetUpNonAbUpdateCommands(package_path, zip, retry_count, pipe_write.get(), &args);
      !setup_result) {
    log_buffer->push_back(android::base::StringPrintf("error: %d", kUpdateBinaryCommandFailure));
    return INSTALL_CORRUPT;
  }

Q: 那麼系統是如何判別 type 是 AB 或否的呢?

答案是,在之前的 META-INF/com/android/metadata 檔案中,有一欄參數寫有:

ota-type=AB

這參數會在 install/install.cppTryUpdateBinary() 裡採值如下:

  bool package_is_ab = get_value(metadata, "ota-type") == OtaTypeToString(OtaType::AB);

Q: 非 AB type 會怎麼處理?

依前述的流程,它會被系統分類為AB type,而進到 SetUpNonAbUpdateCommands() 的流程去,去找到並執行 META-INF/com/google/android/update-binary 這個檔案。

以另一個可能在刷 LineageOS 時會用到的套件為例:

https://mirrorbits.lineageos.org/tools/copy-partitions-20220613-signed.zip

其檔案結構如下:

.
└── META-INF
    └── com
        ├── android
        │   └── otacert
        └── google
            └── android
                ├── update-binary
                └── updater-script

其中最重要的是 META-INF/com/google/android/update-binary 檔案,這個 Android 會解開並執行之 。若他是 binary excutable 檔,則執行 binary 。而此處它是個 Linux Shebang 執行腳本:

#!/sbin/sh

#####################################################
# Flashize Runtime (2016-04-06)                     #
# Copyright 2016, Lanchon                           #
#####################################################

#####################################################
# The Flashize Runtime is free software licensed    #
# under GNU's Lesser General Public License (LGPL)  #
# version 3 and any later version.                  #
# ------------------------------------------------- #
# Note: The code appended to the Flashize Runtime,  #
# if any, is independently licensed.                #
#####################################################

if [ "$1" != "lanchon-magic" ]; then
    export FLASHIZE_VERSION='2016-04-06'
    log=''
    if [ -f /tmp/flashize-log ]; then
        log="$(cat /tmp/flashize-log)"
        if [ -z "$log" ]; then
            log=/tmp/flashize.log
        fi
    fi
    if [ "$log" == "-" ]; then
        log=""
    fi

...

        echo "Partition $partition"
  	    inactive=$(echo $active | sed "s/${suffix_active}\$/${suffix_swap}/");
        part_active=$(readlink -fn $active);
        part_inactive=$(readlink -fn $inactive);
        if [[ -n "$part_active" && -n "$part_active" && "$active" != "$part_active" && "$inactive" != "$part_inactive" ]]; then
          blockdev --setrw $part_inactive
          dd if=$part_active of=$part_inactive bs=4k
        fi
    fi
done

依 Linux Shebang 的慣例規則,會由 /sbin/sh 載入並執行之。

另一個 updater-script 在此處並未觸發,似乎在別的 use case 會用到。這個留待之後有遇到時,再繼續探討。

Q: 為何不直接寫入 image 而要包成 zip 檔跟層層檔案參數呢?

回到問題的動機面,有很多方法可以寫入需要的 binary data。為何要包裝成這個樣子,這樣不是找自己麻煩嗎?

其實這樣的作法,是韌體更新作業的常態。因為 Reflash/Update/Upgrade Firmware 的作業,通常出現在嵌入法系統(Embedded System), 單晶片(MCU), IoT, … 等情境。有很多狀況一旦環節有錯,就可能再也無法開機,例如:

  • 刷錯檔案 => 於是需要一些 Checksum 機制來驗證
  • 在更新過程中,少了某個檔案 => 需要類似像 Manifest 檔案的清單機制
  • ARCH, type, … 參數搞錯了 => 需要有 metadata 檔案描述這個 image 的性質跟類別
  • 有些 Firmware 版本之間有衝突 => 需要 Firmware Version,跟 Dependencies 檢查機制

把上述種種因素都考慮進來之後,就能理解單靠一個 image 檔本身,是作不到防呆的。不難想見像是 BIOS 的更新檔案,一些 4G/LTE modem 的更新檔, … 等,其更新檔案通常會以打包過的壓縮檔的形式來呈現。

補充

總結爬完 source code 的內容,和之前的印象差不多一致,總算是解掉心中一個懸念。

需要注意到此處討論到的 case ,都是在特殊模式(recovery, sideload, rescue, …)下運行的。跟一般正常運行時,所用的 adb install <some package.apk> 的情況不同。

Reference:

Linux 上的 Shebang 是在那裡定義的? — 2022-08-02

Linux 上的 Shebang 是在那裡定義的?

在 Linux 的指令執行檔的開頭,常常會看到以 #! 字串開頭的宣告,像是:

  • #!/bin/bash
  • #!/usr/bin/env python3
  • #!/bin/sh

這個開頭宣告的作用,是告訴系統要用什麼指令來執行該指令執行檔。 例如: #!/usr/bin/env python3 開頭的,就會以 /usr/bin/env python3 這個指令來執行。

不過,這開頭的 #! 這一行是那裡讀取並判讀的呢? 是 Bash? glibc? … or kernel?

答案是,直接由 Linux kernel 處理的,其對應的 source code 是在

fs/binfmt_script.c 裡的 load_script() 函式:

static int load_script(struct linux_binprm *bprm)
{
	const char *i_name, *i_sep, *i_arg, *i_end, *buf_end;
	struct file *file;
	int retval;

	/* Not ours to exec if we don't start with "#!". */
	if ((bprm->buf[0] != '#') || (bprm->buf[1] != '!'))
		return -ENOEXEC;

(出處: https://github.com/torvalds/linux/blob/v5.18/fs/binfmt_script.c#L41)

同時,Linux kernel config 有個設定選項 BINFMT_SCRIPT ,來決定是否支援 #! 這樣的用法。

(出處: https://github.com/torvalds/linux/blob/v5.18/fs/Kconfig.binfmt#L99)

可以使用下列指令來檢查系統的支援狀況:

zless /proc/config | grep -e "^CONFIG_BINFMT_SCRIPT="

預期會得到:

CONFIG_BINFMT_SCRIPT=y

一般來說,絕大多數的 Linux 系統都內建支援 #! 的語法。 但還是遇有一些 embedded system 或是資安考量的系統,會拿掉這個設定。 若在設定 at , cron 的排程設定時,發現程式未如預期執行時,可順便檢查這一個設定。

Q: 關於 Shebang 的歷史?

可參考維基百科的說明:

https://en.wikipedia.org/wiki/Shebang_(Unix)

Q: Linux 上大約何時支援?

從官方的 git 紀錄,支援 #! 宣告的功能,至少在 v2.6.12 時就己經存在:

https://github.com/torvalds/linux/blob/v2.6.12/fs/binfmt_script.c#L26

實際時間應該更早。 不過現下已經很少遇到這類舊系統,所以暫時沒再往前追溯。

Q: 為什麼有的會用 #!/usr/bin/env 開頭?

以 Bash 為例,有些系統會放在 /bin/bash 的位置,有些會放在 /usr/bin/bash。 若我的 script 寫 #!/bin/bash ,那遇到放在 /usr/bin/bash 位置的系統,就會路徑不符而失效。 所以,就有了這一支 env 的程式,他在所有系統都固定放在 /usr/bin/env 這個位置。 當用下述的指令時:

/usr/bin/env bash

env 就會根據該系統的環境設定(ex: $PATH, /etc/environment, …),去找出對應的 bash 的位置去執行。 如果一來,用上述的宣告在橫跨不同系統時,都能執行,而不需要再調整開頭的 Shebang 路徑。

Facebook post 2021-10-23 — 2021-10-23
Facebook post 2021-09-05 — 2021-09-05
Facebook post 2021-06-24 — 2021-06-24
Facebook post 2021-06-17 — 2021-06-17
Facebook post 2021-06-03 — 2021-06-03

Facebook post 2021-06-03

在 thingiverse 網站上搜尋 "mask strap" 關鍵字,可以找到不少口罩減壓帶的 3D 列印模型。我試了幾款,其中 https://www.thingiverse.com/thing:4284533 這個可解除耳朵束縳,固定較佳,列印也簡單(PLA即可)。提供給有需要的人參考。

Imgur

Comments:

  • Daniel Lin: Mat, 你白髮變好多
    • 李俊裕: 對啊,老了XD
      • Daniel Lin: 李俊裕 我只記得你年輕的樣子(太少見面)
  • Travis Yeh: 好久不見
    • 李俊裕: 希望疫情過后,有機會再來聚聚吧。 Stay safe!

Shares:

  • Shih-Yuan Lee
Facebook post 2021-05-29 — 2021-05-29