2019年3月26日

Dnsmasq - Linux小型DNS伺服器

介紹:
Dnsmasq是一套全功能的網路工具,它包含了DNS、DHCP、Gateway...等幾項功能,
設計目的是能提供小型家用路由器上網服務,
或是PDA、SmartPhone...等小型裝置能透過USB連接電腦上網。
Dnsmasq介紹參考網頁:

介紹目的:
目前AmazonS3要能使用API,必須綁定s3.amazonaws.com才能使用,
這造成需要設定DNS Server才能讓運作正常。
DNS Server建置常見的是Bind9,但Bind9設定複雜,
透過Dnsmasq,我們可以簡單的達成相同目的,但設定簡化。

設定:
Dnsmasq依靠dnsmasq.conf這支設定檔,要修改的部份如下:
  1. Resolv設定
Resolv設定在Dnsmasq中,是告知Dnsmasq,環境中上層DNS主機是誰,
也就是說,當Dnsmasq沒有這條DNS時,要去哪些DNS詢問。
它設定預設會讀取
/etc/resolv.conf

在我自己的使用情境中,我不要它的DNS Server設定和本機的DNS Server設定混在一起,
所以我指定讓它分開,如下:
resolv-file=/usr/local/etc/resolv.conf.dnsmasq

  1. 指定要查詢的主機和IP
設定Dnsmasq,當被詢問到這些網址時,回應的IP是什麼?
設定Dnsmasq,當被詢問到這些網域時,透過哪台DNS Server查詢?

如下:
server=/yahoo.com/168.95.1.1
address=/yimg.com/127.0.0.1
address=/ads.yahoo.com/::1

幾個範例是,當指定server時,表示是設定「網域」,當指定address時,表示是設定「網址」。
設定可以是IPv4或IPv6,::1表示IPv6的127.0.0.1。

  1. 指定服務的網路界面
設定Dnsmasq使用哪張網路界面卡提供服務,如下:
interface=eth0

  1. 設定不要提供DHCP服務
設定Dnsmasq不要提供DHCP服務,如下:
no-dhcp-interface=

這條是要去除最前面的註解,告知Dnsmasq不要提供DHCP服務。

systemd介紹

前言:
這篇是之前針對systemd的研究。
systemd是Linux新的啟動管理器,專門用來取代init(SysV init),
但它和舊的init有非常大的差異性。

systemd介紹參考:

systemd的爭議:

systemd的大麻煩除了設定比較複雜外,
systemd本身包裹了非常多的系統服務,也相依了非常多的library和kernel功能,
從wiki上描述到,kFreeBSD無法納入就是個關鍵實例,
看看systemd的組件圖:
ad64a4bc-81c0-46d0-a7dd-2188ac58addb.jpg

光看組件圖就覺得非常複雜。

區分3部份來看這個組件圖:
1. Library
它相依了dbus、pam、crypt、audit、notify這些services library,
這些library實際上都還相依在更底層的library,
像dbus、pam、audit、notify這幾個都很不單純,本身都相依不少library。

2. Kernel
它直接使用了Linux Kernel中cgroup、autofs、dbus這幾個「Linux Kernel功能」,
光1和2就能知道為何GNU/kFreeBSD無法納入,
GNU/Debian考慮半天後,決定先用分支方式支援,
這擺明了就是Linux Only的設計阿.............
試問,FreeBSD如何支援cgroup?如何支援autofs?如何支援dbus?
重新實做會引用的License問題如何解決?

3. systemd內部管理器
systemd不只有原先init的功能,看看systemd Daemons,
就我目前的了解,它至少包含或取代了下列功能:
  • init
  • inetd
  • crontab
  • syslog
  • 網路管理ifupdown/NetworkManager
  • 系統時間、時區管理
  • 登入管理器/gdm

  • crontab部份參考:
如何使用 systemd 中的定時器

  • syslog部份:
它透過journald來取代rsyslog和syslog

  • 系統時間、時區管理:
如何在 systemd 下管理Linux系統的時間和日期

  • 網路管理:
參考:
嘗鮮: 新的網絡連接管理工具 systemd-networkd

  • systemd-network man page

  • ArchLinux systemd-networkd

  • Introduction to networkd, network management from systemd

  • 登入管理器
不熟,但應該就是logind搭配pam
參考:

systemd除了這些外,它將udev專案整個併入,也將dbus幾乎併入,
加上它侵入多數的Linux發行版本,systemd一統Linux指日可待。

4. Application
按前面參考的爭議提到,現在GNOME對systemd的依賴度越來越高,
Ubuntu 14.04的GNOME suspend動作,裡面很多都直接透過systemd進行處理。

當systemd往下吃掉低階系統服務(udev、dbus),再往下依賴Kernel,往上咬住桌面系統GNOME,
那未來Linux就只剩下systemd,FreeBSD也不用搞下去了。

因為systemd包山包海的特性,因此直接可以想到,它在各發行版不一定會是full support,
systemd本身是Fedora的開發專案,RedHat系列不用說,全包應該是正常的,
但Ubuntu自己也開發和主導了不少專案,中間就沒這麼單純了。
參考:

裡面給出了幾個發行版使用systemd的部份的參考連結。





Linux啟動流程

前言:
這篇主要描述Linux啟動流程,並從啟動流程帶出現代作業系統的開機流程設計,
並進一步延伸non-OS系統的設計概念。


Linux啟動流程:
Linux啟動的完整流程原則上如下圖:


硬體:
不論電腦使用哪種CPU,CPU在通電做完硬體極少的工作後,就會跳到某個固定的記憶體位址執行。
對於PC來說,PC有BIOS,CPU通電後會執行BIOS,BIOS做完系統檢查後,
會根據CMOS中選擇的開機裝置(軟碟、光碟、硬碟、USB光碟、USB硬碟...等),
跳到特定位址執行。
對於嵌入式系統來說,就是跳到固定位址,BootLoader接手執行。


BootLoader:
BootLoader中文就是「開機程式」,在PC Linux上,常見的就是GRUB,
在嵌入式系統上有幾套常見的BootLoader,目前來說,uboot是最常見的BootLoader。
BootLoader主要工作其實只有2個:
  1. 將Kernel從各種奇怪的媒體裝置(光碟、硬碟、網路)讀取到記憶體(RAM)中
  2. 跳到Kernel在記憶體中的位址執行


這邊標注一下,嵌入式系統中,牽涉到開機部份,經常看到「跳躍」、「跳到」之類的名詞,
實際意義是,CPU就是不斷執行下個指令的裝置,要讓它到某個位址執行,
就直接用組合語言的JUMP 0xoooooo,它就會跳過去執行了,
所以會用「跳躍」、「跳到」某個位址執行來描述。


initrd/initramfs:
這是Linux特有的設計,它其實跟rootfs是一樣的東西,可以把它看作是小rootfs。
在Linux上,initrd/initramfs用途是:
在正式進入系統前的特殊操作,都可以放入initrd/initramfs中處理。
例如:
  • Linux Kernel module載入工作
  • 系統硬體檢查
  • 系統故障時的處置


在Linux上,不一定需要有initrd/initramfs,
可以在Kernel載入後,直接跳入rootfs,並沒有硬性規定一定要有initrd/initramfs;
同樣的,Linux上也沒有硬性規定一定要有rootfs,
可以直接把initrd/initramfs當rootfs直接使用。
initrd和initramfs這2個稱呼都有人使用,早期的Linux是稱叫initrd,
在Linux 2.6版將它重新設計,重新設計後稱為initramfs,
所以有人繼續沿用initrd稱呼,有人稱initramfs,但不論哪種稱呼,都是描述這個東西。
initrd/initramfs和rootfs有項最大的不同,initrd/initramfs整個是執行在RAM當中,
它的執行環境直接是RAM Disk,這有2項特色:
  1. 執行週期(在initrd/initramfs中執行過程)的檔案變動,都不會影響initrd/initramfs的內容
  2. initrd/initramfs越大,佔用RAM越大
  3. 因為執行在RAM中,可以在此時對Linux各部份進行修改、變動, 不會有媒體佔用(disk busy...等)的問題。
例如:重新燒錄Kernel、rootfs、initrd/initramfs


rootfs:
在Linux中,rootfs有2項含意:
  1. rootfs是實際的系統所在,它的概念等同於Windows的C磁碟或系統碟
  2. 在啟動流程中,rootfs代表系統實際進入rootfs(系統碟), 並由rootfs(系統碟)內的啟動程序接手執行


當rootfs的啟動程序完成後,就是我們看到的Linux畫面,此時所有的操作,都是在rootfs中進行。
rootfs不一定需要,嵌入式系統中,很可能因為Flash太小,直接捨棄rootfs,
將initrd/initramfs當rootfs使用。


現代作業系統的開機流程:
前面描述了Linux的開機流程,大致系統開機,所以這邊簡單的描述現代作業系統的開機流程。


現代作業系統的開機流程圖:

目前主流的作業系統,開機都是類似的流程,去除Linux特有的initrd/initramfs後,
就是大部分作業系統的開機流程,以Windows為例:


Windows的動作流程其實一致,但名稱不同。


BootLoader:
Windows稱為NTLDR。
在開機時,如果有多個OS,會出現一個中文的選單,
像是WinXP會顯示「Microsoft Windows XP」,這個就是Windows的BootLoader。
在以前MCSE課程中,關於開機啟動的Troubleshooting部份,
就有提到NTLDR missing時的處理,這其實就等於GRUB出現找不到開機程式時的問題解決方式。


Kernel:
Windows的Kernel稱為ntoskrnl.exe,早期放在C:\,並用系統檔案屬性隱藏。


rootfs:
Windows的rootfs稱為「系統碟」。
在安裝Windows時,一個部份是選擇安裝磁碟,你可以將系統裝在D:\、E:\,
系統碟的最重要辨識方式是,Windows資料夾和Program Files資料夾在哪個磁碟,
這個磁碟就是系統碟。

non-OS系統的設計概念:
所謂non-OS系統的意思,就是這個電腦系統沒有作業系統,一個盲點就出現了,
電腦沒有作業系統可以跑?
答案是肯定的,電腦沒有作業系統也是可以跑的,在很多低階單晶片系統中,
沒有作業系統一樣跑得很開心。


non-OS系統有什麼不同?從執行流程來看,如下:


有前面作業系統執行流程的理解後,這邊再看看non-OS系統的執行流程,
此時會發現,其實啟動概念相同。
在non-OS系統中同樣有硬體、BootLoader,
但Kernel和rootfs都沒有,而是自行撰寫的嵌入式程式。
嵌入式程式:
嵌入式程式最重要的關鍵是Loop,因為CPU的功能就是不斷地執行下個指令,
因此嵌入式程式內的設計就是要設計個while-loop,
讓CPU在執行到最後時,會回到while開頭重頭執行。

更低階的單晶片系統設計更簡化,連BootLoader都沒有,因此執行流程變為:

最基本的Kernel設計:
Kernel內設計很複雜,詳細的細節原則上就是參考「作業系統」教科書,
裡面大致區分了Scheduler、Memory Manager...等一堆設計。
其實我們把Kernel單純化到極限,從non-OS系統的角度來看Kernel設計,
其實Kernel就是個超大型的while-loop程式,如下圖:

看起來其實和non-OS系統差不多,最主要就是Kernel內多了Scheduler這個元件,
透過它動態選擇不同的Task(在Linux上就是Process)執行。

從Qt設計看視窗系統設計

GUI,算是賈伯斯時代最屌的發明之一。
目前所有的GUI設計,從Windows、UNIX、Linux...等,幾乎都遵循了類似的設計,
這個設計,就我的理解,應該是賈伯斯在Mac上發明的,直到現在,它幾乎沒有太大的變化。


Qt的GUI設計:


在Qt的設計中,Qt會在背景維護一個Event Queue,Qt Event包括了所有的UI動作,
像是滑鼠移動、滑鼠點擊、視窗畫面更新、鍵盤輸入...等,
Qt最主要的工作,就是不斷的檢查Event Queue,有任何Event,就立刻執行它。
我們所有的UI操作,在Qt中就是將操作的「動作」,建立成Qt Event,送入Event Queue中,
Qt就會在檢查Event Queue時,按操作去執行。
以上圖為例,我們的動作很簡單,「移動到視窗的輸入欄,滑鼠點一下,輸入abcd」。
轉換成Qt內部行為,就會是產生下面9個Qt Event:
  1. 滑鼠移動
  2. 畫面更新
  3. 滑鼠移動
  4. 畫面更新
  5. 滑鼠左鍵點擊
  6. 鍵盤輸入a
  7. 鍵盤輸入b
  8. 鍵盤輸入c
  9. 鍵盤輸入d


Qt就會在背景檢查,陸續將這些動作完成。

Qt的GUI設計與程式碼的關聯:
當我們透過Qt開發工具(Qt Creator)建立出一個最基本的視窗程式時,它的主程式如下:
#include "widget.h"
#include


int main(int argc, char *argv[])
{
  QApplication a(argc, argv);
  Widget w;
  w.show();


  return a.exec();
}


一開始看很陌生,長得很奇怪,沒關係,這裡解釋後就一目了然了。


我們這裡要描述的主角叫做QApplication,也就是:
QApplication a(argc, argv);
a.exec();


相信大家都有個疑問,a.exec()後,Qt在幹嘛?
這樣的語法不通阿,程式無法運作吧?
我們的程式要寫在哪裡?
這就是我要帶領大家了解的,我們回到前面「Qt的GUI設計」,裡面曾經提到:
Qt會在背景維護一個Event Queue;Qt最主要的工作,就是不斷的檢查Event Queue,
有任何Event,就立刻執行它。


實際上,在Qt中是「誰」不斷的檢查Event Queue並執行Queue裡面的Event?
它就是QApplication。


套入後就很明白了,Qt主程式的最主要工作,就是產生一個QApplication,
然後把QApplication執行起來,它內部就是不斷的檢查Event Queue,
當有任何動作,QApplication就會按動作執行它。
因此,Qt講物件導向,所有UI元件,都是物件,
所有行為,實際上在內部都轉化成Event送入Event Queue,交給QApplication執行,
我們的程式碼在哪?
我們的程式實際上在Qt的其他物件中,要動作,就透過Qt提供的method,
它就會幫我們產生出Event給QApplication執行。

Qt與視窗系統設計:
前面我們描述了QApplication、Qt的Event在和整個視窗的行為,這裡帶出2個問題:
  1. 這是Qt獨有的設計嗎?
  2. 這個設計背後的實作是什麼?


這是Qt獨有的設計嗎?
這並不是Qt獨有的設計,這裡看Java的UI - SWT,SWT的最基本程式碼如下:
參考:Example
Display display = new Display();
Shell shell = new Shell(display);
shell.open();

// run the event loop as long as the window is open
while (!shell.isDisposed()) {
   // read the next OS event queue and transfer it to a SWT event
 if (!display.readAndDispatch())
  {
 // if there are currently no other OS event to process
 // sleep until the next OS event is available
   display.sleep();
  }
}

// disposes all associated windows and their components
display.dispose();
注意到Shell,看到while(!shell.isDisposed()),看到它的註解。


在Java的SWT中,它也是同樣的設計,只是Qt中的QApplication換成了Shell,原理是相同的。


在Windows中,我們可以直接參考下面這篇文章:


這篇文章是800萬年前的老文章,重點是它描述了Windows UI的內部機制 - Message Loop,
看完後你會發現,跟上述描述的根本如出一轍,
原來,全世界的GUI設計,根本都一模一樣,這設計哪裡來的?
我的了解是,這個設計應該就是來自於賈伯斯的Mac,
至於是不是賈伯斯本人設計或獨創的,我不清楚,但這設計很屌,
而且延續至今,沒意外的話,應該會繼續存在下去,直到GUI或電腦消失為止。



這個設計背後的實作是什麼?
回想看看,以前的程式有所謂的「物件導向」嗎?
再想想,GUI這樣的設計,所有視窗都是獨立的元件,所有視窗動作,都會產生Event,
這...不就是物件導向嗎?
它讓我們將所有視窗都以「物件」的方式思考和設計,所有的「視窗動作」都是「物件行為」,
這應該就很清楚明白了。
但它背後有什麼實作?
我們從寫程式的「物件」發想,再想想前面描述的Event Queue設計,我們就會得出一個有趣的結論:
電腦本身不存在「物件」的概念,它還是一個「程序」的操作邏輯,
賈伯斯將GUI以「物件」設計,讓所有UI程式設計師都使用「物件導向」來寫視窗程式,
可是電腦不認識物件怎麼辦?
所以回頭看看Event Queue的設計,它的本質實際上是將立體變成平面,
把所有物件導向的動作,統統轉化成電腦認識的程序操作。
看看滑鼠、鍵盤、視窗的所有動作,它們會來自於各個位置、各種沒有關聯的行為,
但最終都會進入統一的入口 - Event Queue,並由統一的執行者QApplication執行。


這種將立體轉換成平面,或者平面轉化成立體的設計,在UI中很常見,
例如多個頁面怎麼串在一起輪詢?多個輸入欄怎麼轉成一個陣列的字串?
一組陣列字串怎麼放置到樹狀結構中...等。

GUI設計的限制 - 多執行緒處理:
前面講到了GUI的設計都是透過統一的Event Queue和單一的執行者QApplication執行,
這造成了一個很大的問題,多執行緒在UI下如何處理?
我猜想,這問題產生的原因,可能和設計當時多執行緒不發達有關聯,
但不論如何,這在現在就是個問題。
看看下圖情境:


假設我們一個UI程式中,有3個Frame,每個Frame都是獨立的執行緒複製檔案,
檔案複製過程中會各自更新畫面,請問,它的畫面更新怎麼處理?
在目前的GUI設計中,無法處理...........


因為目前的GUI設計,所有的畫面更新、動作處理,
都必須透過單一的Event Queue和單一的QApplication來動作,
多執行緒的UI畫面更新,違背了由QApplication統一處理UI的設計,所以無法動作,
這樣的設計,往往會造成UI畫面不動,或者動作異常的問題、印出錯誤訊息...等。


變通的辦法是,各個Frame內「複製檔案的動作是獨立執行緒」,
複製檔案過程中,會各自送出Event到Event Queue,
QApplication就能在收到這些Event後,更新每個Frame上的複製進度。
它是多執行緒嗎?
不完全是,它只有核心動作是多執行緒,UI還是維持單一執行緒的設計。

GUI設計的限制 - sleep:
sleep是很常見的設計,電腦跑太快,某些時候還是需要讓它等一下,
但同樣的,這在GUI設計中也會造成困擾。
sleep,會讓CPU真的停下來,多半是執行nop的無限迴圈,直到時間到為止。
還記得QApplication吧,它負責所有Event的執行和所有畫面的更新,
我們的所有程式,實際上都是包裹在Event當中,由QApplication執行的。
現在,我們寫個Qt視窗程式,讓電腦在按下按鈕後sleep 30秒,會發生什麼事情?
會發現,視窗卡住了.....


原因很簡單,也有點白爛,因為QApplication執行按下按鈕的method,裡面要sleep 30秒,
QApplication就等30秒,過程中不做事情,就是等,因此畫面不更新、滑鼠不動作。


變通的辦法是,在Qt的UI中,不能真的用sleep,會造成UI卡死。要透過Qt的計時器,
實作一個類似sleep的功能,過程中使用者不能輸入,
但QApplication實際上跑去執行其他Event,
等到計時器時間到之後,才回來繼續剛剛的程式。


結尾:

Qt的設計不算差,但很多方面受限在GUI設計上,
不了解,寫Qt程式就會撞牆,這邊提出的2個限制,
就是我曾經撞過牆的地方,只有這2個嗎?
當然不是,但這2個印象最深刻,因為都很糟糕,很難處理。