我發現如果我從 WSL2 執行 Windows 本機程式 (PE),訪問 POSIX 路徑會神奇地起作用。
例如,/dev/random如果我從 WSL bash 執行我的程式,我可以訪問,但如果我從 CMD(命令提示符)執行相同的程式,我不能。
我必須了解允許這樣做的機制!:)
測驗程式相當簡單:
#include <stdio.h>
int main(int argc, char *argv[], char *envp[]) {
printf("%p\n", fopen("/dev/urandom", "r"));
return 0;
}
如果我從 WSL 實體內部執行此操作,它將成功打開設備。
但是,如果我通過 CMD 執行此操作,它將失敗。
當我查看 API mon 時,我可以看到open("/dev/urandom", "r")轉換為CreateFileA("\\wsl.localhost\Ubuntu\dev\urandom", ...).
第一個問題:什么組件在做這個轉換?
如果我用它替換fopen它CreateFile失敗......所以它必須是stdio函式中的東西。
第二個問題:它如何知道哪個 WSL 實體是父級?
我沒有看到任何 API 查詢,也沒有給我提示的環境。我能看到的唯一例外是\\wsl.localhost\Ubuntu\tmp行程啟動程序中的打開。
第三個問題:這是否可以嵌套在流程樹中?
當我cmd.exe從 WSL 內部執行,然后執行我的測驗程式時,它失敗了。
但是,我撰寫了自己的本機 Windows 程式來執行我的測驗程式并且測驗程式成功,所以這種行為確實存在于行程樹中。
任何人都可以解釋允許這種魔法發揮作用的機制嗎?什么API?什么組件在進行轉換?背景關系存盤在哪里?它是如何查詢的?它如何知道要查找的發行版?
我試圖在 Microsoft 討論 [1] 上提出這個問題,但沒有得到回應,所以我希望這里有人可以提供提示。
[1] https://github.com/microsoft/WSL/discussions/8212
uj5u.com熱心網友回復:
簡短的摘要。我相信:
/init處理傳遞給 Windows 可執行檔案的作業目錄的轉換。- 當路徑以目錄分隔符(例如
/或\)開頭時,fopen認為它是相對于作業目錄卷的根目錄的。
例如:
- 如果你執行你的代碼
/home/<username> - ...然后作業目錄將是
\\wsl.localhost\Ubuntu\home\<username>. - ...“卷”(在這種情況下為共享名稱)將是
\\wsl.localhost\Ubuntu\ - ...所以
/dev/random打開為\\wsl.localhost\Ubuntu\dev\random.
但是,試試這個:
cd /mnt/c(或該坐騎內的任何位置)- 通過呼叫你的程式
/full/path/to/the.exe。 - 我的
fopen測驗失敗(我假設你也會失敗),因為...... - ...傳入的作業目錄是
C:\(或其子目錄)。 - ...因此卷名也是
C:\. - ...并
fopen嘗試打開C:\dev\random不存在的。
更多詳情:
什么組件在做這個轉換?
這部分(我相信)相當容易回答,盡管不是明確的。正如這個答案中提到的,當您在 WSL 中啟動 Windows 可執行檔案時,它使用注冊的處理程式binfmt_misc(請參閱cat /proc/sys/fs/binfmt_misc/WSLInterop)來呼叫 WSL /init。
不幸的是,WSL/init是封閉源代碼,因此很難全面了解啟動程序中發生的情況。但我認為我們可以有把握地說,處理程式 ( /init) 將成為在 Windows 行程接收路徑之前轉換路徑的組件。
需要注意的一件有趣的事情是該wslpath命令通過符號鏈接映射到同一個二進制檔案。當使用 name 呼叫時wslpath,/init二進制檔案將進行 OS 路徑轉換。例如:
wslpath -w /dev/random
# \\wsl.localhost\Ubuntu\dev\random
但這是真正的問題......
所以我們知道它/init知道如何轉換路徑,但是在啟動 Windows 二進制檔案時它究竟轉換了什么?這有點棘手,但我認為我們可以推測得到轉換的是當前作業目錄的路徑。
試試這些簡單的實驗:
$ cd /home
$ wslpath -w .
\\wsl.localhost\Ubuntu\home
$ powershell.exe -c "Get-Location"
Path
----
Microsoft.PowerShell.Core\FileSystem::\\wsl.localhost\Ubuntu\home
$ cd /dev
$ wslpath -w .
\\wsl.localhost\Ubuntu\dev
$ powershell.exe -c "Get-Location"
Path
----
Microsoft.PowerShell.Core\FileSystem::\\wsl.localhost\Ubuntu\dev
$ cd /mnt/c
$ wslpath -w .
C:\
$ powershell.exe -c "Get-Location"
Path
----
C:\
還有一個問題
所以這是我的問題——Windows API 什么時候開始聰明地連接以目錄分隔符開頭的 UNC 作業目錄和路徑?我找不到關于該行為的檔案,但它顯然有效。它并不特定于 WSL。在將 UNC 作業目錄用于常規網路共享時,我觀察到了相同的串聯行為。
更奇怪的是,.NET 的路徑處理對于UNC 連接并不是那么聰明。從doc中,我們觀察到的行為fopen是 DOS 路徑的預期行為,但對于 UNC:
UNC 路徑必須始終是完全限定的。它們可以包括相對目錄段(
.和..),但它們必須是完全限定路徑的一部分。您只能通過將 UNC 路徑映射到驅動器號來使用相對路徑。
我能夠在 PowerShell 中使用簡單的Get-Content.
回到我們定期安排的...
但除此之外,您甚至不需要示例代碼來演示這一點。notepad.exe您可以通過從 WSL中呼叫來查看相同的行為:
$ cd /etc
$ notepad.exe /home/<username>/testfile.txt
# Creates or opens the proper file using \\wsl.localhost\Ubuntu\home\<username>\testfile.txt
$ cd /mnt/c/Users
$ notepad.exe /home/<username>/testfile.txt
# Results in "The system cannot find the path specified", because it is really attempting to open C:\home\<username>/testfile.txt, and the `home` directory (likely) doesn't exist at that path.
以及您的其他相關問題:
它如何知道哪個 WSL 實體是父實體?
如果現在還不清楚,我認為可以肯定地說 WSL/init知道您所在的 WSL 實體,因為它無論如何都在“協調”整個事情。
這是否可以嵌套在行程樹中?
只要一個行程不更改樹中下一個行程的作業目錄,就可以。但是,CMD 不理解 UNC 路徑,因此,如果它在行程鏈中,您的程式將失敗。
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/463729.html
