原文發表在個人 博客 上,歡迎訂閱。;)
在兩年前的八月,Microsoft 正式發佈了 Windows 10 Anniversary Update 週年更新(它還有着 RS1,Version 1607,Build 14393 等一大堆別名)。其中最讓包括我在內的衆多開發者感到興奮的特性之一,就是 WSL(Windows Subsystem for Linux,當時還叫 Bash on Ubuntu on Windows)的正式加入。php
在 Windows 上原生運行 Linux 可執行文件,牛逼瘋了!node
然而 Bug10 也不是浪得虛名,本來只提供給 Insider 的 WSL 在正式發佈後依然問題多多(不只 zsh、tmux 等工具沒法使用,網絡相關的操做更是一律欠奉,還有各類各樣 奇妙的 BUG),基本沒有可用性,我在嚐鮮了一段時間後也不得不重回 Cygwin 的懷抱。不過好消息是,在以後的更新中,這些 BUG 都已被逐一消滅。linux
通過了兩年的發展,WSL 已經足夠成熟,我也是時候完成這篇一咕再咕的博文了。git
既然違反廣告法取了這麼個標題,那我天然得先描述一下個人目標,也就是我理想中的命令行界面應該是什麼樣子的(若是你不清楚命令行的概念,能夠看看我以前寫的 這篇文章):github
然而遺憾的是,Windows 上的命令行一直以來都很微妙。web
停停停,那邊的 PowerShell 愛好者 ,咱別動粗成嗎?shell
首先我要對標題作出一些訂正,Windows 原生命令行其實也能夠不那麼難用。雖然 cmd.exe 是公認的難用到反人類(畢竟是用來兼容 DOS 的老古董),但後來推出的 PowerShell 已經足夠強大且現代化,可以稱得上是一個成熟的命令行 Shell 了。若是你願意學習的話,PowerShell 幾乎能夠知足你對命令行的全部期待。這一點能夠參見:Is PowerShell ready to replace my Cygwin shell on Windows?ubuntu
可是,PowerShell 與 Bash 等類 Unix 系統上的 Shell 程序幾乎是兩個徹底不一樣的世界。不只語法不一樣,其平臺上各種經常使用的命令行工具也基本不一致(好比類 Unix 系統中的 grep
對應 PowerShell 中的 Select-String
,uniq
對應 Select-Object -Unique
等)。往深了說,他們的系統設計理念都是不同的,好比不少人推崇的 Unix 哲學,在 Windows 上就基本不見蹤跡;而 COM 等概念也是 Windows 獨一份。windows
▲ 圖片來源:シス管系女子 BEGINS 特別編 まんがでわかる WSL瀏覽器
固然,我無心在此挑起操做系統間的聖戰。Windows 和類 Unix 系統中的命令行哪一個好用,見仁見智。不過對於包括我在內的不少用戶都認爲 Windows 命令行不怎麼好用,僅此而已。
回到正題。
雖然 Windows 的命令行一直遭人詬病,可是人家的圖形界面牛逼啊。因而無數工程師前赴後繼,試圖在 Windows 上創造出不輸給類 Unix 系統的命令行體驗 —— 卻絕大多數以失敗了結。曾經努力過的人,或者回到可愛的 Linux 上,或者進入高貴冷豔的 macOS 的世界。其中有先輩留下了 Cygwin、GnuWin32 等工具集,讓咱們能夠在 Windows 下使用類 Unix 系統中常見的命令行工具,成爲了避免少 Windows 用戶的救贖。
然而,就當你們都以爲「也就這樣了」的時候,Microsoft 出人意料地站了出來。
帶着他新鮮出爐的 WSL。
你們都把 WSL 吹得這麼牛逼,那 WSL 到底是個什麼玩意兒呢?
簡單來講,WSL 是一個 兼容層,有點像反過來的 Wine。
首先,我問個問題,爲何 Linux 上的程序沒法在 Windows 上運行呢?
瞭解過一點操做系統原理的同窗應該都知道,這是 Windows 與 Linux 的內核提供的接口不一樣(系統調用、API 等)致使的。舉個栗子,咱們想知道某目錄下的內容,在 Linux 下咱們會使用 ls
命令,而在 Windows 下咱們會使用 dir
命令。
當咱們在 Linux 上執行 ls
命令,ls
會調用 getdents
這個系統調用,Linux 內核收到請求,將目錄的內容返回給應用程序;當咱們在 Windows 上執行 dir
命令,dir
會調用 NtQueryDirectoryFile
這個 API,NT 內核收到請求,將目錄的內容返回給應用程序。雖然系統不一樣,但基本上都是一個道理。
然而,當咱們把 Linux 上的應用程序拿到 Windows 上運行時,應用程序和內核就雙雙懵逼了。好比 ls
會嘗試調用 getdents
系統調用(理想化的狀況下,暫不考慮可執行文件格式等問題),Windows 的 NT 內核一看,心說:「這他孃的什麼東西,老子不認識啊,啥狀況啊」,ls
也想:「尼瑪,內核怎麼不回話啊,咋回事兒啊」……兩邊語言不通,應用程序天然沒法正確執行。
可是有了 WSL,狀況就不同了。
依然拿 ls
舉例,當咱們在 WSL 中運行 ls
命令時,ls
會調用 getdents
系統調用(這個系統調用接口是 WSL 提供的,Windows 自己並無這個接口),WSL 收到這個請求,明白了應用程序是想要知道目錄的內容,因而把 Linux 的系統調用轉換爲 NT API NtQueryDirectoryFile
。NT 內核收到 WSL 的請求,將目錄的內容返回給 WSL,WSL 再把返回的內容包裝好後返回給 ls
。
也就是說,WSL 在 Linux 應用程序與 Windows NT 內核之間起到了翻譯者的做用。很簡單的道理,既然 NT 內核沒法理解 Linux 應用程序的 POSIX 系統調用,那就弄個翻譯來將 POSIX 系統調用實時轉換爲 NT 內核能理解的 API 調用,突出一個見人說人話、見鬼說鬼話。
只要實現了足夠多的系統調用翻譯,那麼理論上 WSL 能夠徹底模擬成一個 Linux 內核。
相信各位都據說過鼎鼎大名的 Cygwin。一樣是能讓 Linux 應用程序運行在 Windows 上,WSL 和 Cygwin 有什麼不一樣呢?其實差異仍是挺大的。
雖然 Cygwin 提供了完整的 POSIX 系統調用 API(以運行庫 Cygwin*.dll
的形式提供),但其依然工做在 User Mode;而 WSL 中的 Linux 應用程序進程會被包裹在一個叫作 Pico Process 的東西里,這個東西里發出的全部系統調用請求都會被直接送往 Kernel Mode 中的 lxcore.sys
與 lxss.sys
處理。
一樣是將 POSIX 系統調用轉換爲 Windows 中的 API,Cygwin 是轉換成 Win32 API 的調用(由於它架設在 Win32 子系統上,不少內核操做受限於 Win32 的實現,好比 fork
),而 WSL 則是轉換爲更底層的 NT API 調用(WSL 是與 Win32 平行的子系統,直接架設在 NT 內核上,能夠經過 NT API 原生實現 fork
等系統調用)。
▲ WSL 架構示意圖。圖片來源:Windows for Linux Nerds
最重要的一點:若是使用 Cygwin,Linux 應用程序的源碼必須 link 至 Cygwin 運行庫(Cygwin*.dll
),修改源碼從新編譯後才能在 Windows 下運行。這些從新編譯後的 Linux 應用程序在調用 POSIX API 時不會直接去請求內核,而是會去調用 Cygwin 運行庫,由運行庫翻譯成 Win32 API、執行調用後返回結果。這也就意味着,從新編譯後的應用程序須要依賴 Cygwin 運行庫才能正常運行(有時候你會碰到的「缺乏 Cygwin1.dll
」報錯就是這個緣由),並且這樣編譯出來的可執行程序是純正的 Win32 PE 格式封裝,只能在 Windows 上運行。
而在 WSL 下,咱們能夠直接運行未經任何修改的 ELF 格式 Linux 可執行程序。
▲ Cygwin 目錄下,被編譯成 Win32 可執行程序的 Linux 應用程序們。
最後總結一波:
WSL 就像是一個翻譯官,就算那些未經修改的 Linux 應用程序們操着一口純正的 POSIX 系統調用語法,WSL 也能快速準確地將其翻譯爲 NT 內核能聽懂的 API 調用;
而那些使用了 Cygwin 從新編譯後的 Linux 應用程序,就像是改造人同樣變成了 Win32 應用程序的形狀,還被套了個翻譯機。程序本身(源碼中)說的是 POSIX,通過翻譯機(Cygwin 運行庫)以後就變成 Win32 API 調用了,這樣 NT 內核也能聽得懂。
可是每次添加新程序都要改造,多麻煩啊,仍是 WSL 原生態更健康(笑)。
以上只是我對 WSL 的粗淺解釋,其具體實現原理能夠參考官方博客上的 這一系列文章。
好了不說廢話,讓咱們開始安裝 WSL。注意,WSL 僅支持 64 位系統,且本文中所描述的安裝方法僅適用於 Windows 10 Fall Creators Update(秋季創意者更新,RS3,Version 1709,Build 16299)及以上版本。
第一步,打開「控制面板」中的「程序與功能」,點擊左側邊欄的「啓用或關閉 Windows 功能」選項,在彈出的窗口中勾選「適用於 Linux 的 Windows 子系統」,而後點擊肯定(可能須要重啓)。
若是你懶得用 GUI,也能夠直接在 PowerShell 中以管理員權限執行命令:
Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Windows-Subsystem-Linux
第二步,打開 Microsoft Store,搜索「WSL」。挑選一個你喜歡的 Linux 發行版,而後點擊安裝。(截至目前,商店中可用的發行版有 Ubuntu、openSUSE、SUSE Linux Enterprise Server、Debian 以及 Kali Linux。)
第三步,在開始菜單中找到你剛剛安裝的發行版,打開它。等待幾分鐘的初始化過程,設定好用戶名與密碼後(不須要與 Windows 的相同,用過 Linux 的選手應該都懂的)就會自動進入 Linux 環境。
至此,你已經完成了 WSL 的安裝。
你也能夠同時安裝多個發行版,它們的數據都是獨立的,互不影響。
我猜你如今正在對上面那個窗口發呆。
—— 這個新宋體他孃的是個什麼狀況?
若是你正在使用中文 Windows 系統,並且以前並無修改過 Win32 Console 的默認配置,那麼你的 WSL 終端默認就會是這樣的。新宋體,就是這麼 Hardcore。驚不驚喜,意不意外?
好吧不開玩笑,Windows 這個控制檯窗口就是不少人討厭它的緣由之一,難用又難看。醜這一點倒還有解決方法(通過一番設置後還算能看,我之前就寫過一篇關於 自定義 Windows 控制檯字體 的文章),難用倒是實打實的。儘管 Win10 上的控制檯已經改進了很多(能夠看看 Microsoft 的官方博客:Windows Command Line Tools For Developers),但其依然是最難用的終端模擬器之一,或許沒有之一。
所以,爲了實現咱們的目標,一個更強大的終端模擬器是必須的。
終端模擬器是什麼?爲了這個回答這個問題,我專門寫了一篇文章, 去看看吧。:P
我我的比較推薦的終端模擬器有:
專門爲 WSL 開發的終端模擬器,基於 mintty 與 wslbridge,穩定易用。
Windows 上的老牌終端模擬器,功能極爲強大,要啥有啥。
基於 Electron 的跨平臺終端模擬器,好看和可擴展性是賣點,BUG 很多。
還有其餘各類各樣的終端模擬器,選個本身喜歡的就好。反正無論選哪一個,都比默認的那玩意兒要好用。🌚
另外,設定終端模擬器的 Shell 入口時有個坑,須要注意一下(參見下文 6.4)。
▲ 我正在使用的終端,wsl-terminal 與 Hyper。好看是第一輩子產力。
如下是 WSL 的一些優化技巧。
因爲衆所周知的緣由,各大發行版默認的軟件源在中國大陸的訪問速度都很屎。
我目前使用的是 清華大學的 Ubuntu 鏡像源。
想要快樂地使用命令行,一個趁手的 Shell 是必不可少的。
我我的習慣使用 zsh,安裝步驟再也不贅述。個人自定義 oh-my-zsh 主題:
# ~/.oh-my-zsh/custom/themes/robbyrussell-ascii.zsh-theme # Modified from robbyrussell, the default theme of oh-my-zsh. # > blog git:(source) x $ local ret_status="%(?:%{$fg_bold[green]%}>:%{$fg_bold[red]%}>%s)" PROMPT='${ret_status} %{$fg[cyan]%}%c%{$reset_color%} $(git_prompt_info)$ ' ZSH_THEME_GIT_PROMPT_PREFIX="%{$fg_bold[blue]%}git:(%{$fg[red]%}" ZSH_THEME_GIT_PROMPT_SUFFIX="%{$reset_color%} " ZSH_THEME_GIT_PROMPT_DIRTY="%{$fg[blue]%}) %{$fg[yellow]%}x%{$reset_color%}" ZSH_THEME_GIT_PROMPT_CLEAN="%{$fg[blue]%})"
# ~/.zshrc ZSH_THEME="robbyrussell-ascii" plugins=(git zsh-completions zsh-autosuggestions zsh-syntax-highlighting)
Windows 10 Fall Creators Update 以後,WSL 支持同時安裝多個 Linux 發行版,直接在 Microsoft Store 中搜索想要的發行版並點擊安裝便可。這些發行版能夠同時運行,而且數據互相獨立。你可使用 wslconfig.exe
來查詢已安裝的發行版,或者更改默認的發行版。
刪除發行版也很簡單,直接卸載對應的商店應用便可(記得備份哦)。
新版支持同時安裝多個發行版,那天然不能像之前那樣只提供一個 bash.exe
入口了。
秋季創意者更新以後的 Windows 提供了 多種進入 WSL 環境的方式:
wsl.exe
打開默認發行版中的默認 Shell。
<distroname>.exe
打開指定發行版中的默認 Shell。
bash.exe
(DEPRECATED)打開默認發行版中的 bash Shell。
若是你更改了默認 Shell 卻老是打開 bash,就說明你使用了這個入口。
你也能夠經過這些入口直接在 WSL 中執行命令並返回結果:
<distroname> -c [command]
bash -c [command]
wsl [command]
(再也不須要指定 -c
)
WSL 與 Windows 之間的互操做性 (Interoperability) 很牛逼。怎麼個牛逼法呢?
Windows 下的全部盤符都掛載在 WSL 中的 /mnt
目錄下,能夠直接操做。WSL 中的全部數據則存放於 C:\Users\{你的用戶名}\AppData\Local\Packages\{Linux發行版包名}\LocalState\rootfs
目錄中(不要在 Windows 中修改這些文件,這會形成文件權限錯誤):
$ ls /mnt c d e $ mount -l rootfs on / type lxfs (rw,noatime) C: on /mnt/c type drvfs (rw,noatime,uid=1000,gid=1000) D: on /mnt/d type drvfs (rw,noatime,uid=1000,gid=1000) E: on /mnt/e type drvfs (rw,noatime,uid=1000,gid=1000)
你能夠在 Windows 命令行環境中直接調用 WSL 中的命令:
PS C:\temp> wsl ls -al total 0 drwxrwxrwx 1 printempw printempw 4096 Sep 7 19:04 . drwxrwxrwx 1 printempw printempw 4096 Sep 7 18:38 .. -rwxrwxrwx 1 printempw printempw 4 Sep 7 19:04 foo.txt
你也能夠在 WSL 中調用 Windows 中的命令行程序:
$ which ipconfig.exe /mnt/c/Windows/System32/ipconfig.exe $ ipconfig.exe Windows IP Configuration ...
你能夠在 WSL 中直接啓動 Windows 應用:
$ notepad.exe "C:\temp\foo.txt"
你還能夠經過 pipes 與 Windows 程序通訊:
# 複製內容至 Windows 剪貼板 $ cat foo.txt | clip.exe
你甚至能夠把 Windows 命令和 WSL 命令混着用:
PS> ipconfig | wsl grep IPv4 IPv4 Address. . . . . . . . . . . : 192.168.1.114 $ ipconfig.exe | grep IPv4 | cut -d: -f2 192.168.1.114 $ ls -al | findstr.exe foo.txt -rwxrwxrwx 1 printempw printempw 4 Sep 7 19:04 foo.txt $ cmd.exe /c dir Volume in drive C is Windows Volume Serial Number is B263-**** Directory of C:\temp 2018/09/07 19:04 <DIR> . 2018/09/07 19:04 <DIR> .. 2018/09/07 19:04 4 foo.txt 1 File(s) 4 bytes 2 Dir(s) 194,422,341,632 bytes free
同時,WSL 與 Windows 共享網絡棧,也就是說你能夠:
若是你對 WSL 與 Windows 之間互操做的原理有興趣,能夠參考一下這些文章:
雖然 WSL 中能夠直接訪問 Windows 磁盤的內容,但若是你曾經這麼作過,你應該對這樣綠油油一片的 ls
不會感到陌生。爲何 NTFS 文件系統中的文件到 WSL 下權限就所有成 0777
了呢?
這主要是 DrvFs 中 Linux 文件權限的實現致使的。
在 WSL 中,Microsoft 實現了兩種文件系統,用於支持不一樣的使用場景:
着力於在 Windows 文件系統上提供完整的 Linux 文件系統特性,經過各類手段實現了對 Inodes、Directory entries、File objects、File descriptors、Special file types 的支持。好比爲了支持 Windows 上沒有的 Inodes,VolFs 會把文件權限等信息保存在文件的 NTFS Extended Attributes 中。記得我上面警告過你不要在 Windows 中修改 WSL 裏的文件嗎?就是由於 Windows 中新建的文件缺乏這個擴展參數,VolFs 沒法正確獲取該文件的 metadata,並且有些 Windows 上的編輯器會在保存時抹掉這些附加參數。
WSL 中的 /
使用的就是 VolFs 文件系統。
着力於提供與 Windows 文件系統的互操做性。與 VolFs 不一樣,爲了提供最大的互操做性,DrvFs 不會在文件的 NTFS Extended Attributes 中儲存附加信息,而是從 Windows 的文件權限(Access Control Lists,就是你右鍵文件 > 屬性 > 安全選項卡中的那些權限配置)推斷出該文件對應的的 Linux 文件權限。
全部 Windows 盤符掛載至 WSL 下的 /mnt
時都是使用的 DrvFs 文件系統。
因爲 DrvFs 的文件權限繼承機制很微妙,最後致使的結果就是全部文件的權限都變成了 0777
。並且因爲早期的 DrvFs 不支持 metadata,因此你沒法給這些文件 chown/chmod,只能對着綠油油的 ls
乾瞪眼。不過好消息是,Windows Insider Build 17063 以後,DrvFs 也像 VolFs 同樣支持給文件寫入 metadata 了。
要啓用 DrvFs 的 metadata 支持,你須要添加參數從新掛載磁盤:
# 修改爲你本身的盤符 $ sudo umount /mnt/e $ sudo mount -t drvfs E: /mnt/e -o metadata
不過若是僅僅是執行了這個,雖然支持了文件權限的修改,但磁盤下的文件權限默認依然仍是 0777
,除非你給它們整個 chmod
一遍。若是你不想這麼作,也能夠指定其餘的 mount 參數:
$ sudo mount -t drvfs E: /mnt/e -o metadata,uid=1000,gid=1000,umask=22,fmask=111
這樣磁盤下的文件的默認權限就是 0644
,ls
也不會再是綠油油一片啦。
不過每次使用時都要從新掛載未免也太煩,咱們能夠經過另外一個新特性 Automatically Configuring WSL 實現自動掛載。在 WSL 中建立 /etc/wsl.conf
,在其中填寫以下內容:
[automount] enabled = true root = /mnt/ options = "metadata,umask=22,fmask=111" mountFsTab = true # 這個文件裏還能夠添加其餘配置項,有興趣的能夠看看上面的連接
重啓終端,全部的盤符就會使用上面的配置自動掛載啦(可使用 mount -l
查看)。
另外,若是你想要給不一樣的盤符設定不一樣的掛載參數(上面的方法對全部盤符都有效,若是你想在 WSL 中運行 Windows 下的應用程序,就得每次都 chmod +x
一下,因此我通常都會把 C:
排除掉),就須要手動修改 /etc/fstab
。首先確保 wsl.conf
中的 mountFsTab
爲 true
,而後編輯 /etc/fstab
,添加以下內容:
# 不在此列表中的盤符會使用 wsl.conf 中的參數掛載 # 格式能夠本身去查 fstab 的幫助文檔 E: /mnt/e drvfs rw,relatime,uid=1000,gid=1000,metadata,umask=22,fmask=111 0 0
雖然 Microsoft 開發 WSL 出來主要是着重於命令行環境的使用,但通過測試,WSL 是能夠經過 X Server 執行 GUI 應用程序的,甚至還能夠在 WSL 裏面用 Wine 執行 Windows 程序……(🤔?)
也有人試過在 WSL 中運行完整的 DE,體驗彷佛還不錯,有興趣的同窗能夠去試試。
另外,你也能夠經過某些神祕的方法用上 Microsoft Store 未提供的 Linux 發行版,好比 Arch Linux。
若是你對 WSL 的底層實現有興趣,也能夠去圍觀一下 WSL 的官方博客:
雖然 WSL 很不錯,可是其比起真正的 Linux 系統仍是有不少不足(Docker 等涉及未實現的內核特性的軟件沒法使用,Raw socket 相關的操做依然容易出錯,I/O 性能相比之下較爲孱弱等)。若是你平常開發中須要使用到那些 WSL 未提供的 Linux 特性,那麼仍是乖乖跑 VM 或者裝 Linux 吧。
對我來講,WSL 最大的意義就是,讓我可以用我熟悉的 Linux 那一套去操做 Windows。
若是你和個人需求同樣,那麼比起 Cygwin、VM 等解決方案,WSL 有着完整的 Linux 環境、強大的互操做性、更低的資源佔用。離不開 Windows,卻又羨慕 Linux 下強大命令行工具的各位,相信大家會喜歡 WSL 的。
並且最近幾年 Microsoft 在籠絡開發者方面的努力你們有目共睹,這裏就容我誇上一句:
Microsoft,幹得漂亮!