Wine ทำงานอย่างไร 101
(werat.dev)- Wine เป็น compatibility layer ที่ทำให้สามารถรันโปรแกรม Windows บนระบบปฏิบัติการที่เข้ากันได้กับ POSIX (Linux, macOS, BSD) ได้
- Steam Deck ของ Valve ก็ใช้โซลูชันที่อิงกับ Wine เช่นกัน
WINE = Wine Is Not an Emulator
- วิธีแบบ emulator นั้นช้า และจริง ๆ แล้ว Linux/macOS สามารถรันไบนารีของ Windows แบบเนทีฟได้อยู่แล้ว (ถ้ามีตัวช่วยเล็กน้อย)
- อธิบายอย่างละเอียดผ่านดีบักเกอร์ว่าไบนารีของ Linux/Windows ทำงานอย่างไร
Hello, Wine!
- โดยพื้นฐานแล้ว Wine เป็น "Dynamic Loader" สำหรับไฟล์รันของ Windows
- มันเป็นไบนารีลินุกซ์แบบเนทีฟ และรู้ว่าต้องจัดการ EXE หรือ DLL อย่างไร
- Wine จะอ่านไฟล์รันของ Windows เข้าสู่หน่วยความจำ จากนั้นพาร์สเพื่อดู dependency แล้วกระโดดไปยังโค้ดที่ต้องรัน
- แค่นี้ก็ทำให้รันไบนารี Windows ได้ในระดับหนึ่งแล้ว แต่ยังมีข้อยกเว้น
System Calls
- system call หรือ syscall คือสิ่งที่ทำให้ Wine ซับซ้อน
- syscall เป็นสิ่งที่ implement อยู่ใน OS ไม่ได้อยู่ในไฟล์รันหรือไลบรารี
- syscall ที่ OS จัดให้ก็คือ OS API
- Linux : read, write, open, brk, getpid,..
- Windows : NtReadFile, NtCreateProcess, NtCreateMutant,..
- system call ต่างจากการเรียกฟังก์ชันทั่วไปในโค้ด ตัวอย่างเช่น การเปิดไฟล์ต้องมีการติดตาม File Descriptor จึงต้องให้เคอร์เนลจัดการ
- ดังนั้นโค้ดของแอปพลิเคชันจึงต้องมีวิธี "interrupt" ตัวเองและส่งการควบคุมให้เคอร์เนล ("Context Switch")
- ชุดฟังก์ชันและวิธีการเรียกที่ OS ให้มานั้นแตกต่างกันไปในแต่ละระบบ
- บน Linux ถ้าเรียกฟังก์ชัน read() ต้องใส่ file descriptor ไว้ในรีจิสเตอร์
%rdi, pointer ของบัฟเฟอร์ไว้ใน%rsi, และจำนวนไบต์ที่จะอ่านไว้ใน%rdx - แต่บน Windows ไม่มีฟังก์ชัน read() อยู่ในเคอร์เนล
- บน Linux ถ้าเรียกฟังก์ชัน read() ต้องใส่ file descriptor ไว้ในรีจิสเตอร์
- หากลองรันโค้ดที่พิมพ์ "Hello World!" แบบเดียวกันบน Linux/Windows
- Linux จะเรียก puts ของ
libc.soส่วน Windows จะเรียก printf ของucrtbase.dll - ปัจจุบันบน Linux มักลิงก์แบบ static กันไปเลย ทำให้ใส่ implementation ของ puts ไว้ในไบนารี และไม่ต้องใช้
libc.soตอนรันจริง
- Linux จะเรียก puts ของ
- บน Windows อย่างน้อยก็จนถึงช่วงหลัง ๆ นี้ "มีแต่ malware เท่านั้นที่เรียก system call โดยตรง"
- แอปพลิเคชันทั่วไปจะพึ่งพา
kernel32.dll/kernelbase.dll/ntdll.dllเสมอ เพื่อไม่ให้สื่อสารกับเคอร์เนลโดยตรง
- แอปพลิเคชันทั่วไปจะพึ่งพา
Runtime translation of Syscalls
- ถ้าดักจับ syscall ได้จะเป็นอย่างไร?
ถ้าแทรกตัวตอนแอปพลิเคชันเรียกNtWriteFile()แล้วไปเรียกwrite()จากนั้นคืนค่ากลับในรูปแบบผลลัพธ์ที่ไบนารีต้องการล่ะ? - ถ้าจัดเตรียม
ucrtbase.dllเวอร์ชันคัสตอมก็พอจะทำได้ แต่จะมีปัญหาซับซ้อนตามมา - ดังนั้นจึงไปแก้ที่
ntdll.dllซึ่งคั่นอยู่ระหว่างไบนารีกับเคอร์เนลแทน - Wine เวอร์ชันใหม่ประกอบด้วย
ntdll.dll(ไบนารี PE) และntdll.so(ไบนารี ELF)- ฝั่ง dll เป็นเลเยอร์บาง ๆ ที่คอย redirect การเรียกไปยังฝั่ง ELF
- ฝั่ง ELF มีฟังก์ชันพิเศษชื่อ
__wine_syscall_dispatcherที่ทำเวทมนตร์แปลงสแตกปัจจุบันจากฝั่ง Windows ไป Linux หรือกลับกัน
- syscall dispatcher นี้คือสะพานที่เชื่อมโลกของ Windows กับโลกของ Linux
- มันจัดการ calling convention, จัดสรรพื้นที่สแตก, ย้ายรีจิสเตอร์ และงานอื่น ๆ
- เมื่อการทำงานเข้ามาถึง
ntdll.soและข้ามมาฝั่งไบนารีลินุกซ์แล้ว เราก็สามารถใช้ Linux API ได้ทั้งหมด
แค่นี้เองเหรอ?
- ฟังดูง่ายมาก แต่...
- Windows API มีจำนวนมหาศาล เอกสารก็ไม่ดีนัก มีทั้งบั๊กที่รู้กันและไม่รู้กัน และต้องคงพฤติกรรมเหล่านั้นไว้ตามเดิม โค้ดส่วนใหญ่ของ Wine คือ implementation ของ DLL ต่าง ๆ ของ Windows
- มีหลายวิธีในการเรียก syscall และในทางเทคนิคก็ไม่มีทางห้ามไม่ให้แอปพลิเคชันเรียก syscall โดยตรงได้
(จำไว้ว่าพวกเกมบน Windows ทำทุกอย่างที่บ้าคลั่งได้)
เคอร์เนลลินุกซ์มีกลไกพิเศษสำหรับรับมือเรื่องนี้ และแน่นอนว่ามันยิ่งเพิ่มความซับซ้อน - ปัญหา 32bit vs 64bit ก็ชวนปวดหัวไม่แพ้กัน ยังมีเกม 32 บิตจำนวนมาก และมันคงไม่ได้ถูกออกใหม่เป็น 64 บิตอีกแล้ว Wine รองรับทั้งสองแบบ จึงยิ่งเพิ่มความซับซ้อน
- ยังไม่ได้พูดถึง
wine-serverเลย นี่คือโปรเซสแยกที่ Wine สร้างขึ้นมาเพื่อเก็บ "สถานะ" ของเคอร์เนล (file descriptor, mutex ฯลฯ) - ถ้าอยากรันเกมล่ะ? ก็ต้องจัดการ DirectX, PulseAudio, อุปกรณ์อินพุต ฯลฯ ทำให้มีงานอีกมหาศาล
- Wine ถูกพัฒนามาอย่างยาวนานและเดินทางมาไกลมาก ทุกวันนี้สามารถรันเกมใหม่อย่าง Cyberpunk 2077 หรือ Elden Ring ได้แบบไม่มีปัญหา
บางครั้ง Wine ยังทำงานได้เร็วกว่าบน Windows เสียอีก
11 ความคิดเห็น
เนื้อหาก็ดีอยู่แล้ว แต่คุณภาพของบทสรุปยอดเยี่ยมมากจริง ๆ ขอบคุณครับ
ฉันกำลังใช้ yes24 และ kyobobook ซึ่งให้บริการอ่านหนังสือแบบสมัครสมาชิก
หลังจากเปลี่ยนสภาพแวดล้อม PC ที่บ้านเป็น Ubuntu ก็ลองรัน YES24 และ kyobobook ผ่าน Wine ดู
ทั้งคู่ใช้ DRM แยกกัน เลยสงสัยว่าจะรันได้ไหม แต่ YES24 ที่เท่าที่ทราบว่าสร้างด้วย QT กลับรันได้ดี ส่วน kyobobook EBOOK รันไม่ได้ครับ (UI รันได้, DRM รันไม่ได้)
รู้ว่าทั้งคู่มีการใช้ DRM เหมือนกัน เลยคิดอยู่ว่าทำไมอันหนึ่งรันได้แต่อีกอันรันไม่ได้ แต่พอได้อ่านบทความข้างบนก็น่าจะพอเข้าใจคร่าว ๆ แล้วครับ (มีมแนว ๆ ว่า "เข้าใจอย่างสมบูรณ์แล้วแบบคร่าว ๆ")
ตั้งแต่
wine5.0เป็นต้นมา KakaoTalk ก็รันได้โดยไม่ต้องตั้งค่าอะไรเลย เลยมีความสุขมาก (แม้จะเป็นอีกเรื่องหนึ่งที่จริง ๆ แล้วไม่ได้อยากใช้ KakaoTalk ก็ตาม..)แม้จะมีปัญหาเรื่องการแสดงผลหน้าจอบางอย่างอยู่บ้าง แต่ฟีเจอร์ต่าง ๆ อย่างการส่งรูปภาพผ่านคลิปบอร์ดก็ทำงานได้อย่างลื่นไหล
โดยรวมแล้วดูเหมือนว่า Wine จะโฟกัสกับการรันเกมเป็นหลัก แต่ก็ดีที่มันช่วยให้แอปหลากหลายตัวรันได้ดีด้วย
ถึงจะมีการพูดถึงการนำ Linux ไปใช้ในหน่วยงานภาครัฐ แต่กลับไม่คิดจะทำ KakaoTalk เวอร์ชัน Linux เลย นี่มันก็... แย่มากจริง ๆ..
แต่เวอร์ชัน Mac กลับทำออกมาได้อย่างรวดเร็ว..
พอรู้คร่าว ๆ อยู่แล้ว แต่พอมาอธิบาย Wine แบบละเอียดขนาดนี้ก็น่าทึ่งดีนะ .. ฮ่าๆ
โดยทั่วไปแล้ว Wine สามารถรันโปรแกรม Windows ได้ค่อนข้างดี ถ้าอย่างนั้นจะเป็นไปได้ไหมที่จะใช้ Wine เพื่อสร้างแอปข้ามแพลตฟอร์ม! (จำกัดเฉพาะเดสก์ท็อป)
ผมเข้าใจว่าเมื่อนานมาแล้ว HWP ของ Hancom ก็เคยถูกพอร์ตและวางจำหน่ายสำหรับลินุกซ์โดยอาศัย Wine เป็นพื้นฐานด้วยเช่นกัน (R4 มีเลเยอร์ไลบรารีความเข้ากันได้กับ win32 แยกต่างหาก ส่วนที่ใช้ Wine นั้นเป็น R5 หรือ 2002 กันแน่ก็เริ่มจำไม่ค่อยได้แล้ว)
ดังนั้นช่วงหนึ่งจึงเคยมีมุกว่า เพราะ Wine ทำให้ win32 เป็น API ข้ามแพลตฟอร์มที่แพร่หลายและประสบความสำเร็จที่สุด
แต่ตอนนี้กลายเป็นยุคของ electron/wasm ไปแล้ว;;
เป็นอีกประเด็นหนึ่งเล็กน้อย แต่ถ้าคุณจะทำแบบนั้น — เนื่องจากไลเซนส์ของ Wine เป็น LGPL คุณอาจต้องเปิดเผยซอร์สโค้ดบางส่วนหรือทั้งหมด ทั้งนี้ขึ้นอยู่กับวิธีที่คุณเขียนโค้ด
ตามที่อธิบายไว้ในต้นฉบับด้วย เหตุผลที่ Wine ไม่ใช่อีมูเลเตอร์ก็เพราะมันใช้ชุดคำสั่งของ CPU โดยตรง นั่นหมายความว่าซอฟต์แวร์ที่สามารถรันด้วย Wine ได้ โดยพื้นฐานแล้วก็คือซอฟต์แวร์สำหรับ Windows ที่ทำงานบน CPU x86 หรือ x86-64
ในเวลานี้ที่ Apple ได้ย้าย Mac ทั้งหมดไปสู่สถาปัตยกรรม ARM แล้ว แถมทาง MS เองก็ออก ชุดพัฒนาสำหรับ ARM มาแล้ว การจะเรียกซอฟต์แวร์ที่ทำงานได้เฉพาะบน CPU ที่ใช้ x86(-64) ว่า “รองรับข้ามแพลตฟอร์ม” ก็คงจะฝืนไปสักหน่อยหรือไม่
ใช่ครับ อย่างที่คุณว่าไว้... ดูเหมือนว่าผมจะเผลอจำกัดความไว้แค่เครื่องตระกูล x86 โดยไม่รู้ตัวครับ
เพราะมีทั้ง electron และ tauri อยู่แล้ว ถ้าต้องทำแบบข้ามแพลตฟอร์มตั้งแต่ต้นก็ดูเหมือนจะไม่ใช่ตัวเลือกที่ดีนัก
ถ้ามีข้อจำกัดพิเศษที่ทำให้ใช้เทคโนโลยีบนฐานเว็บเบราว์เซอร์ไม่ได้
การใช้ไลบรารีอย่าง Qt ที่รองรับการคอมไพล์ข้ามแพลตฟอร์มได้ดี อาจจะเป็นทางเลือกที่ดีกว่าก็ได้..
222