WSL

WSL

WSL is the “Linux aspect” of the Windows command line. With WSL enabled, you can execute Linux commands in cmd.exe. You can pipe the output of a Windows command to a Linux program, or pipe Linux output to Windows. For example, the output of a Windows command can be filtered with the Linux “grep” program:

C:\Users\gilbert>winget list|wsl grep Git Git Git.Git 2.48.1 winget GitHub Desktop GitHub.GitHubDesktop 3.4.17 winget

This is not a port of grep to create a Windows executable. The standard output of the Windows winget.exe program is piped to the Linux ELF executable program “grep” running in a VM under the Linux Kernel, and the output of grep is then piped back to the cmd.exe window.

WSL is a “feature” of the Windows OS, but it can be installed from Windows Store or by command. When you install WSL you will typically choose one of several vendor distributions such has Ubuntu or Fedora.

WSL is inactive until you use it in a command or from a program. On first use it starts a VM, boots the Linux kernel in the VM, mounts a virtual disk containing the file system of your chosen distribution, mounts the Windows local disks to Linux directories, runs init to set up the Linux system, starts bash with an environment borrowed in part from Windows, and then executes the command you provided. In the example above, the Linux program /usr/bin/grep runs with its standard input piped from the output of the Windows winget.exe program. It doesn’t matter in this example, but the current directory when grep runs is your Windows Home directory (C:\Users\gilbert or in Linux terms /mnt/c/Users/gilbert) not your Linux home directory /home/gilbert. The output of grep goes back to bash which in turn sends it back to cmd.exe where it is typed out for the user to see.

There is also an API for Windows programs to start WSL and run Linux programs. This API is used by VS Code Remote, Docker Desktop, Podman Desktop, and other tools that run Linux programs from Windows applications.

The WSL Linux Kernel is part of Windows, distributed by Microsoft, and maintained by monthly patching. Distributions such as Ubuntu 24.04 or FedoraLinux-42 are provided by Canonical or Red Hat and maintained separately. To separate Ubuntu from Fedora, and separate both of them from the Kernel, the distributions run in a type of “container” that is quite different from Docker. For example, the file systems are stored on a Hyper-V virtual disk that saves any changed you make from one run to the next, unlike Docker images that discard changed when the program ends.

The virtual disk files for your WSL Ubuntu and your WSL Fedora are stored in your Windows Home directory. They are private to you. Therefore, you can act as a Linux administrator and make any changes you want to your personal copies of Linux even if you don’t have admin privileges to your Windows system. While Windows can see the Linux files, and Linux can see the Windows files, Ubuntu and Fedora cannot see each other’s files. Nor can you directly share any WSL Linux files with another user or on the network. WSL is constrained to be private and personal to you, and it is running only when you are using it directly.

As shown in the examples above, when WSL installs a Linux distribution, it copies the name of the Windows user to a created Linux userid (which becomes uid 1000). This user is given sudo privileges and can run any Linux program and access any Linux file.

Meanwhile, the wsl.exe that you ran from the Windows command line to start WSL has established a simple “file server” that Linux can call back to when it wants to list, read, or write Windows files. Since wsl.exe was running under your userid, the Linux programs you run access Windows file through your Windows file permissions.

If you use ssh to run a program on a regular Linux computer, the standard output is sent back over the network and appears in your client terminal window. WSL, however, couples the Windows and Linux environments together as closely as possible. While ssh would start a Linux program in your Linux home directory, wsl.exe runs the Linux program in the Windows current directory.

The most surprising feature is that WSL reads the current Windows PATH environment variable and appends all the Windows directories in it to the end of the Linux PATH. As a result, bash running in Linux can run any program in your Windows PATH by name, except that since Linux doesn’t have special file types you have to type the entire program name including the ending “.exe” so that Linux will find the program.

Linux files can be accessed from Windows by a UNC prefix of “\\wsl$\” followed by the distribution name:

C:\Users\gilbert>type \\wsl$\ubuntu\etc\lsb-release DISTRIB_ID=Ubuntu DISTRIB_RELEASE=24.04 DISTRIB_CODENAME=noble DISTRIB_DESCRIPTION="Ubuntu 24.04.2 LTS"

Windows files are found in Linux with “/mnt/” followed by the Windows disk letter:

C:\Users\gilbert>wsl.exe pwd /mnt/c/Users/gilbert

From the point of view of an end user typing commands in the Terminal window, Windows programs seem to run Linux child programs, and vice versa. Technically Windows and Linux programs run side by side under Hyper-V but with the shared file system, inherited environment, and output piped back and forth it appears that they have a parent-child relationship.

There is no need for Linux to know about the complex rules that Windows NTFS has for ownership of files and directories and permissions granted to users and groups. Linux can only act through your Windows login, so when it looks at Windows files, it only sees the files you can see and only sees the access permissions you have. It pretends you own all the Windows files, and while it reports Linux file permissions for groups and others, since you are the only one who can use WSL Linux there are no real “other users”. It had to do something, so it marks all Windows files as executable regardless of file type:

gilbert@MWPF51R7L9:/mnt/d/ant/bin$ ls -ltr total ... -rwxrwxrwx 1 gilbert gilbert 11730 Aug 20 2024 ant -rwxrwxrwx 1 gilbert gilbert 862 Aug 20 2024 antRun -rwxrwxrwx 1 gilbert gilbert 2118 Aug 20 2024 antRun.pl -rwxrwxrwx 1 gilbert gilbert 8111 Aug 20 2024 ant.bat -rwxrwxrwx 1 gilbert gilbert 2856 Aug 20 2024 ant.cmd gilbert@MWPF51R7L9:/mnt/d/ant/bin$ cat ant #! /bin/sh # Licensed to the Apache Software Foundation (ASF) under one or more ...

Note that WSL magically fixes line end characters between the different systems. Yes, you could run a Linux script from a Windows text file in a Windows directory that happens to contain a valid Linux shell script.

If more than one Windows program connects to more than one Linux program in the same distribution, then Linux runs concurrent “logins” and runs the various programs and sessions in parallel.

There are two reasons to use a real Linux VM instead of WSL:

  • If you want the full Gnome desktop experience, or you need a Linux system with background processes that are running all the time, then you need a regular Hyper-V Linux VM.

  • If you want to run a Linux network server in the background all the time and expose it for use or testing by other VMs or computers, then you need a real Linux VM.

Microsoft has put all the source to WSL on GitHub as an Open Source project. More importantly, more of the internal implementation details are not published as https://wsl.dev.

Ubuntu, Debian, AlmaLinux (RHEL), Arch, Fedora, SUSE …

The Linux Kernel Organization manages official versions of the Linux Kernel. Various groups and companies then add their own choice of utilities, services, desktop managers, and libraries of installable packages. These are called Linux Distributions. Canonical creates Ubuntu, Red Hat creates the RHEL server and Fedora desktop, and so on.

Microsoft supports any Linux Distribution that any vendor will adapt to install in WSL. Normally vendors ship their own version of the Linux Kernel as part of each version of their distribution, but in WSL Microsoft supplies the Kernel and applies patches, so the vendor cannot include code that depends on specific new Kernel features. WSL can run individual programs that interact with the user in a Linux window on the Windows desktop, but a WSL distribute does not supply its own desktop system like Gnome.

Microsoft makes the various vendor Linux distributions available in Store, or they can be downloaded and installed from the command line. List them by typing the command:

wsl.exe --list --online

If you have a particular favorite, then go ahead and choose it.

If you have no Linux preference, then Ubuntu is the version that has always supported WSL, has the most features, and has the largest set of installable packages.

Some companies and projects distribute tools to run and develop Linux programs on Windows. They will ship Windows programs, libraries, and plug ins that talk through WSL to some Linux system running their backend programs. For example, Docker and Podman provide Windows commands to build, list, and run Containers, but the images have to be stored and the containers have to run in Linux. They will often create their own “private” WSL distribution just to run their own programs. This distribution will be created when you install their Windows application, or it will be created later on with a command or button on their Windows configuration utility.

A Short History

All versions of Windows after NT 3.1 sometime around 1994, have had some ability to compile and run C programs written for Unix. These were Unix C source files complied with the Microsoft C compiler to produce a Windows exe that did the same thing as the Unix program but ran on Windows. Nobody really wanted this.

The first version of the Windows Subsystem for Linux created a subdirectory inside the Windows directories with the structure of a Linux filesystem (subdirectories named bin, etc, home, lib, and so on). It populated these directories with real Linux binary (ELF) programs and text files with Linux line ending characters. WSL then ran these programs in Windows using support in the Windows Kernel and NTFS to emulate Linux behavior.

This was better, but not all Linux behavior could rationally be emulated, and emulation is always inefficient.

Microsoft Already Needed a Linux Kernel for Azure

The new strategic Microsoft project is Cloud based computing through Azure. Companies run a mixture of Windows and Linux systems in their datacenters and expect the same in the Cloud. So, Microsoft needed a Linux Kernel that ran well under Hyper-V in Azure.

When Windows or Linux are running in a real computer, they will discover that the computer has an Intel I219-LM wired network chip and install an Intel device driver specific to that chip. Run the same operating system in a VM under Hyper-V, VMware, or VirtualBox and it is more efficient to install the generic Hyper-V/VMware/VirtualBox Generic Virtual Network driver that makes software calls from the VM to Hyper-V to transmit or receive network packets. Then Hyper-V can pass the request on to the Windows host Kernel where the real hardware will be known and, if it happens to be that Intel chip, then the Windows Intel I219-LM device driver will be used.

While the Microsoft Linux Kernel is mostly the same as the official Linux Kernel, to squeeze the most performance out of it there will be a few extra optimizations that Microsoft can add because it distributes the Kernel as a Windows component and maintains it thought the monthly Windows maintenance. This particular Kernel may only run in a Hyper-V VM.

WSL (Version 2)

While WSL is a Windows feature, it is currently also on Open Source project with a GitHub source repository. You can download and built it yourself. You can modify it if you want.

This means that while the Windows Kernel and Hyper-V are Microsoft components that are carefully written, extensively tested, sophisticated, optimized, and digitally signed for security, all the WSL components are high level user mode code.

Every Hyper-V VM needs virtual disks to hold its system and user data. While you can assign a real physical disk to a VM, it is more common to create a Windows VHDX file on a Windows file system and use that file to hold the data on a Hyper-V emulated disk assigned to a VM. This entire path involves only Hyper-V and the Windows Kernel and can be highly optimized even though it involves a low-level connection between a Windows device driver and the Linux Kernel running in the Hyper-V VM.

On top of that, however, WSL wants the Linux programs to have access to all the Windows files on local disks, and it wants all the Windows programs to have access to all the files in any WSL Linux distribution. This also involves communication between Windows and Linux about data on files, but this is a user level interaction between what turns out to be a pair of client-server relationships, one where Windows is the client and Linux is the server, and entirely different software where Linux is the client and Windows is the server. These components are all part of the WSL GitHub source that you can download and modify.

WSL Linux networking happens entirely as high-level, client-server, open source code where Windows is the server and Linux is the client.

Example: VM Optimized Disk I/O

The history of this topic takes us back before Windows or Linux.

The first system to create virtual machines was the IBM CP-67 OS in 1968. It was initially designed so that more than one software developer could be testing operating system code on the same computer. However, sometimes they wanted a small friendly system (named CMS) to edit and compile code. To support that simple system, they created dummy instructions (based on the DIAG instruction) that were not part of the real CPU but which CP-67 could pretend were firmware in the virtual machine to do the minimal set of things that anyone needed to build a small personal computer. One such operation was to read or write data between a location in memory and a numbered block of data stored on an assigned area of disk.

About 15 years later IBM developed its first hardware Personal Computer. They famously chose a small company named Microsoft to write PC DOS. DOS borrowed ideas from a lot of sources, but one idea that IBM supplied was to write firmware for the computer that provided a version of the same type of dummy operations (based on the INT instruction) that almost one for one duplicated the things that CMS did on CP-67. In the PC, these operations were called the BIOS. One such operation was to read or write data between a location in memory and a numbered block of data stored on a floppy disk.

Now fast forward to today. It doesn’t matter what computer you are using, or what operating system you choose, or what type of disk you are using. There is a common set of steps between a program request to read or write data and the operations transmitted through the SATA cable, USB cable, or the PCIe bus.

  1. A program provides a file path name.

  2. The operating system finds the file path name in tables built in memory from the directory of the file system.

  3. The directory entry for that file name points to an object that contains a list of numbered blocks of data (relative sectors) in the part of the disk that is owned by that file system.

  4. Each file system is in a partition that has a starting sector number.

Somewhere around this point we arrive at the point where we need to read or write data between a location in memory and a numbered block of data stored on an assigned area of disk.

Below this point there is a lot of detail specific to SATA, SAS, or SSD devices and how they power up and how to efficiently manage them. There is also a lot of work when the disks are set up in RAID or other error recovery arrangements. Once you have to deal with specific real hardware there is a lot of work and different device drivers for each type of disk.

So, we stop at that point in the middle between the file system and the real hardware, where we have the same simple operation that IBM emulated in 1968 and the first IBM PC added to BIOS in 1981 and is somewhere in the middle of every operating system on every computer today.

Reading or writing a numbered sector on a disk was always a strategic choice to present to a virtual machine as an emulated operation. So Hyper-V, and VMware, and VirtualBox, and Proxmux all provide some way for Virtual Machines to get to that point in their processing and then simply pass a request for that operation to the native VM supervisor under which they are running.

We now look at the general case of how Hyper-V handles VM disk I/O, and the specific case of how a WSL Linux distribution like Ubuntu does I/O to its filesystem virtual disk.

The virtual disk file of a named WSL distribution is installed in your home directory as:

C:\Users\{username}\AppData\Local\Packages\{distribution}\LocalState\ext4.vhdx.

When WSL wants to start that distribution, it commands Hyper-V to mount this file as a virtual disk on the VM running the Linux Kernel.

The Windows Kernel opens this file and goes through the four universal steps listed above (look that path up in the directory, find the location of the file on disk, in this case take exclusive ownership of the file so no other program can change it, and build in the Kernel the tables that will translate relative sector numbers inside the file to physical hardware sectors on the real disk. The Kernel also attaches the lower-level device drivers appropriate for the physical disk type (SATA, SAS, NVMe, Thunderbolt, …).

Inside this file is data formatted as if it were a Linux disk with an ext4 file system. Windows does not have an ext4 file system driver, but the Linux Kernel does. Hyper-V now establishes a logical communication connection between the bottom of the Linux Kernel ext4 file system driver for a virtual disk device, though Hyper-V, to the open ext4.vhdx file in the Windows Kernel.

Any Linux I/O goes down through the Linux Kernel until it gets to the point where it has become an operation to read or write data between a location in memory and a numbered block of data stored on an assigned area of virtual disk. That operation is then transferred over the connection to the Windows Kernel drivers where what Linux regarded as “a numbered block of data on a disk” is now treated as an “offset from the beginning of the ext4.vhdx file”. We now go through a second round of translating a file location into an absolute disk location, then pass the request to the hardware device drivers that support whatever type of disk the C:\ volume resides on.

Note that at no point here is there any protocol that in any way looks like the exchange between a client and a network file server. The Windows Kernel device drivers behave as an extension of the Linux Kernel device drivers. Since everything is running on the same computer, it behaves as a direct call from one operating system kernel to the other kernel. This is why Linux running in a VM is almost as efficient as Linux running natively.

VM Optimized Network I/O

In any computer the networking software creates in memory an Ethernet packet. The original rule was that the packet could only be 1500 bytes long, and it begins with the 6-byte ID assigned to the destination network adapter. The OS transfers the packet to its network adapter, hardware magic happens, and the packet appears in the memory of the destination network adapter.

Hyper-V supports virtual network adapters, and it provides special Linux device drivers. Essentially, when Linux running in a Hyper-V VM has created an Ethernet packet, it calls Hyper-V to send the packet to its destination, and when it wants to receive data it calls Hyper-V to deliver any packets addressed to its 6-byte ID.

However, the normal Hyper-V networking treats each VM as if it were a real computer with its own name and addresses.

That is not the way WSL wants to operate. WSL wants Linux to appear to be part of the Windows computer. WSL does not want its own network addresses, and above all its doesn’t want to require any network configuration.

Sometimes WSL uses Hyper-V virtual network adapters internally between its own services running on Windows and the Linux VM. When it does this, it tries to hide them. If you happen to see them, you are supposed to ignore them and pretend they are not there.

This is not really much different from the way that Docker handles network I/O to a Docker container. The container appears to have a network adapter, but it is not real. The program i nthe container does network I/O, and Docker converts it to real I/O somewhere else. The real network I/O uses entirely different addresses, on a completely different network, all of which is invisible to the programs in the container.

While Hyper-V optimizes disk I/O to be as efficient as possible, WSL is designed so that Linux gets all its network access through the already existing Windows network. You only configure and manage the Windows network. When you move your laptop from place to place, you use Windows to connect to the local WiFi or to set up a VPN. Windows chooses which network connection is the best choice to send data to its destination.

Therefore, WSL networking components run outside the Kernel. It acts as a proxy, and ships each request on to Windows. For example, if a WSL program looks up a name, WSL ships the request to Windows, the name is resolved, and the answer is sent back to WSL. If a program binds to a port number, WSL ships the request to Windows where the proxy program binds to that port number on Windows, and ships back the result of the operation.

WSL has no real networking of its own. All the network operations are forwarded to Windows. As a result, a WSL Linux process is, indirectly at least, a client of Windows network services.

One System - Two Interconnected Kernels

Although there is a VM, WSL installs your files in your C:\Users home directory. There is no need for admin mode or system file access privileges. You can just type the command “wsl --install”, wait for it to complete, and then reboot to start the Hyper-V service.

In either Windows or Linux, a programmer knows that a “parent” program can run a “child” program in its own memory as a separate process. The parent can set up streams to send and receive standard input and output.

With WSL, a Windows program can create a Linux “child process”, and a Linux program can run a Windows “child process”. Standard input and output can stream between the two systems.

However, while WSL Linux programs know that Windows is running, WSL is not necessarily running until you start to use it. Therefore, from Windows you run a Linux program through wsl.exe and you access a Linux file through the UNC name “\\wsl$\xxxxx”. Both provide an intermediary to start WSL if it isn’t already running.

You can configure WSL to bring up a Linux Kernel with no configured network adapter and no TCP/IP and yet the wsl.exe communication and the file sharing work just fine because they use Hyper-V system services instead of network protocols. In a manner of speaking, Windows is a Linux Device Driver and Linux is a Windows Device Driver.

Switching back and forth

When a real Linux system boots up on a physical computer or VM, it runs a program named “init” that starts services in the background and then creates one or more programs waiting for a user to connect from the keyboard, a serial port, or over the network with telnet or ssh. When a user connects, there is a login which authenticates a userid and starts a shell under that uid number. Any programs that run under this type of login are said to be in the “foreground”, while programs started by init system commands run as services in the “background”.

When the WSL VM starts and the WSL Linux Kernel boots, and the version of Linux you have chosen runs “init” to start the background services, the keyboard and display belong to Windows, there are no serial ports, and telnet and ssh servers are not allowed. The only connection comes from Windows wsl.exe which connects through Hyper-V. It bypasses the login and authentication, because the Linux you connect to is the one you installed in your private C:\Users\* directory so it already knows you are its owner. Each wsl.exe that connects to this Linux instance is a separate “foreground” user session like a normal Linux ssh connection.

Since this is your private copy of Linux, you have full admin rights in it, even if you are not an administrator in the Windows system. Any access to Windows files and directories from Linux uses the Windows user credentials from the wsl.exe program that started WSL.

When a Windows program calls wsl.exe, its existing PATH is transformed into Linux file syntax and is appended on the end of the standard Linux PATH. Linux programs typically don’t have extensions. The Microsoft Linux Kernel service to run a program looks at the file and if it is a Windows .exe instead of a Linux ELF or script file, then it calls back to Windows to run the program under its parent wsl.exe process.

We run a test case

  1. Windows cmd.exe calls wsl.exe with no arguments.

  2. WSL maps the environment to Linux and runs a bash shell connecting standard input and output between cmd.exe and bash.

  3. In bash the current directory is changed just to test, then “cmd.exe” is entered as a bash command.

  4. In the original cmd.exe window we see a new cmd.exe running but in a current directory set by bash.

In the Windows process tree, we see cmd.exe call wsl.exe. The callback spawns a new cmd.exe under wsl.exe.

image-20250316-184945.png

The actual output to the end user is less exciting, but it verifies that the Linux cd propagates back to Windows:

C:\Users\gilbert>wsl gilbert@MWPF51R7L9:/mnt/c/Users/gilbert$ cd /mnt/c/temp gilbert@MWPF51R7L9:/mnt/c/temp$ cmd.exe Microsoft Windows [Version 10.0.22631.5039] (c) Microsoft Corporation. All rights reserved. C:\temp>

On the Linux side, the process tree is:

root 558 2 0 14:44 ? 00:00:00 /init root 559 558 0 14:44 ? 00:00:00 /init gilbert 560 559 0 14:44 pts/0 00:00:00 -bash root 561 2 0 14:44 pts/1 00:00:00 /bin/login -f

Linux boot init (pid 2) spawns a new init (558) which in turn spawns another init (559), which runs bash (560). Although bash tried to create a child process from “cmd.exe”, no permanent child process is visible, but there is something called “/bin/login -f” which because it has the next process id (561) is probably the Linux end of the callback to Windows.

Windows Proxy of Linux Program

If you want to be able to run Linux programs by typing in their name instead of adding a “wsl.exe” prefix on the Linux command, you need to create proxy files in the Windows Path.

For any Linux command you can use a bat file with the same generic contents;

wsl.exe %0 %*

The %0 is replaced with the name typed to call the bat file, and %* generates all the arguments.

That will work from cmd.exe, but sometimes you need to configure an actual program instead of a bat file. There should be a Microsoft supplied program that does it, but you can do it yourself as an exercise running Copilot to generate a C program. Ask it for a program that runs “wsl.exe” and passes it the current program name followed by all the arguments. The core of the program should look like:

char* command = (char*)malloc(4096); strcpy(command, "wsl.exe "); strcat(command, argv[0]); for (int i = 1; i < argc; i++) { strcat(command, " "); strcat(command, argv[i]); } int result = system(command); free(command); return result;

Unfortunately, you need a C compiler from Visual Studio Community Edition. Feel free to try doing the same thing in another language that can compile to produce a *.exe.

I would suggest that a single copy of the bat file and exe be put in a special directory somewhere in your Windows path. Then in the same directory you can build alias to these files using the Windows command to generate Hardlinks (file aliases) to one of these files.

I created wslcmd.bat and inwsl.exe. The hardlink commands would be:

mklink /h docker.bat wslcmd.bat
mklink /h docker.exe inwsl.exe

First, we verify that there are no Windows commands named ip or df.

C:\Users\gilbert>ip 'ip' is not recognized as an internal or external command, operable program or batch file. C:\Users\gilbert>df 'df' is not recognized as an internal or external command, operable program or batch file.

Now in the prepared directory create the hardlinks:

C:\alternatives>mklink /h ip.exe inwsl.exe Hardlink created for ip.exe <<===>> inwsl.exe C:\alternatives>mklink /h df.exe inwsl.exe Hardlink created for df.exe <<===>> inwsl.exe

Now try again in cmd.exe:

C:\Users\gilbert>ip addr 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever inet 10.255.255.254/32 brd 10.255.255.254 scope global lo valid_lft forever preferred_lft forever inet6 ::1/128 scope host valid_lft forever preferred_lft forever 2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000 link/ether 00:15:5d:fb:88:b6 brd ff:ff:ff:ff:ff:ff inet 192.168.106.209/20 brd 192.168.111.255 scope global eth0 valid_lft forever preferred_lft forever inet6 fe80::215:5dff:fefb:88b6/64 scope link valid_lft forever preferred_lft forever 3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default link/ether 02:42:15:c4:59:01 brd ff:ff:ff:ff:ff:ff inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0 valid_lft forever preferred_lft forever C:\Users\gilbert>df Filesystem 1K-blocks Used Available Use% Mounted on none 16290776 0 16290776 0% /usr/lib/modules/5.15.167.4-microsoft-standard-WSL2 none 16290776 4 16290772 1% /mnt/wsl drivers 997872636 276662560 721210076 28% /usr/lib/wsl/drivers /dev/sdc 1055762868 4410368 997649028 1% / none 16290776 76 16290700 1% /mnt/wslg none 16290776 0 16290776 0% /usr/lib/wsl/lib rootfs 16287284 2372 16284912 1% /init none 16290776 896 16289880 1% /run ...

You only need one .exe and can create any number of hardlink aliases for any number of Linux programs. The proxy has to be in the Windows PATH, while the real Linux program has to be in the Linux PATH after bash starts up. You can create additional bat and exe file to run programs in any non-default WSL instance.

Authentication, Security, Access

Microsoft wants you to be able to login to Windows and automatically have access and security for both Windows and WSL Linux files. You also need the ability to add programs to your Linux environment even if you are not an administrator in Windows.

Therefore, the entire WSL Linux file system is stored in a Hyper-V VHDX virtual hard disk file that is itself stored in your C:\Users\username Windows profile home directory. This is your personal file. When you run programs in WSL Linux, it gets mounted on the WSL Linux VM in a way so that only your WSL Linux instance has access to its contents. WSL Linux behaves normally, which means that you have access to everything you normally expect and get root access by using the “sudo” command.

The Windows disks are mounted under the /mnt directory of Linux, but WSL Linux only has access to Windows files through your Windows login credentials. Linux programs you run in WSL have the same access as Windows programs running under your Windows userid.

The first time you run WSL Linux you are prompted to create a username and give it a password. The sensible choice is to use the same userid you have in Windows, but whatever name you give doesn’t matter. When Windows starts a program in Linux using wsl.exe, then that program runs under the userid you just named and uid 1000. The only use of the password is when you run sudo.

The WSL Linux Kernel is a Microsoft Windows Component

The WSL Linux Kernel is installed and maintained by Microsoft with the rest of the Windows system. It can be changed by Windows Update on any Patch Tuesday. Microsoft and Linus Torvalds guarantee that new versions of the Kernel are compatible with old versions and all your programs will continue to run correctly after the patch or upgrade.

Linux Distributions create a WSL <distro>

Companies and interest groups (Canonical, Oracle, Red Hat, Debian, …) combine a Linux Kernel with a selection of programs and libraries to create a Linux distribution. Normally a distribution is shipped as a ISO (DVD) or a USB disk image, both of which you can boot.

Microsoft provides the Kernel and Windows does the install. The installation package, which is basically a tar file of the Linux file system) is downloaded over the network and WSL loads it into a VHDX virtual disk file and adds a “distro” name to the list of WSL instances. You can ask for a list of available alternatives:

wsl -l -o The following is a list of valid distributions that can be installed. Install using 'wsl.exe --install <Distro>'. NAME FRIENDLY NAME AlmaLinux-8 AlmaLinux OS 8 AlmaLinux-9 AlmaLinux OS 9 AlmaLinux-Kitten-10 AlmaLinux OS Kitten 10 Debian Debian GNU/Linux FedoraLinux-42 Fedora Linux 42 SUSE-Linux-Enterprise-15-SP5 SUSE Linux Enterprise 15 SP5 SUSE-Linux-Enterprise-15-SP6 SUSE Linux Enterprise 15 SP6 Ubuntu Ubuntu Ubuntu-24.04 Ubuntu 24.04 LTS archlinux Arch Linux kali-linux Kali Linux Rolling openSUSE-Tumbleweed openSUSE Tumbleweed openSUSE-Leap-15.6 openSUSE Leap 15.6 Ubuntu-18.04 Ubuntu 18.04 LTS Ubuntu-20.04 Ubuntu 20.04 LTS Ubuntu-22.04 Ubuntu 22.04 LTS OracleLinux_7_9 Oracle Linux 7.9 OracleLinux_8_7 Oracle Linux 8.7 OracleLinux_9_1 Oracle Linux 9.1

The first column (Name) is the short, no embedded spaces nickname for WSL instance created from the installation file, and in WSL documentation (as seen in the comment above) it is called a “distro”.

In this paper, a “distribution” is a collection of files (in a tar) that the vendor has decided to put in the initial Linux system. You ask wsl.exe --install to untar the files to a VHDX formatted as an ext4 file system, creating a “distro”, which is a runnable instance of WSL Linux.

The default distribution is “Ubuntu” which is probably the best all-around choice because it offers the widest range of installable packages.

It is a Container, but not the type you are used to

Long before Docker, Unix had all the services needed to create what we now call “containers”. Basically, any running Linux program can start another “child” program in a restricted environment where the child process sees only a subset of the file system that the parent had. A more sophisticated program can create dummy devices, particularly dummy network adapters, that that the child program then uses and the parent program emulates.

Docker created one very specific, highly restricted version of the container idea which was optimized for running one application program with only the Linux files and programs that program needed. When you need more than one program, or an application and background services, you start a group of containers that communicate with each other over a simulated private network.

Docker allows the developer to run a container interactively (typing in a sequence of bash commands) for debugging, but in production the container is started and runs on its own. Docker containers can write to a separate mounted volume, but changes to the container root files are discarded when the container restarts.

WSL is always interactive. In fact, WSL will normally only run while the user is actively connected to it and entering commands. A WSL distro contain all the programs of a basic Linux system, and you should be able to install any command line program that you need. Changes you make are saved in the VHDX and persist across restarts like a regular system disk.

WSL uses its own type of container to solve three problems:

  1. It cleanly separates the Microsoft Linux Kernel from the vendor supported programs, libraries, and data. Microsoft can change the Kernel at any time without affecting the Ubuntu/Debian/Oracle/Red Hat pieces.

  2. If you install two distros, each has its own isolated container. If more than one Windows user installs the same distro, the distro for each user goes in its own isolated container.

  3. It was easier for Microsoft to design WSL if it could ensure that there was only one WSL VM and Microsoft Linux Kernel. That one Kernel is connected to the one Windows Kernel. You can then run as many distros as you want in containers on top of the one Kernel.

What about Linux GUI Apps?

Unix was first written for large computers shared by multiple users connected over phone wire from teletype terminals (it was, after all, written by AT&T - the phone company - that made teletypes). Over time the teletype was replaced by terminals with screens, the most famous of which was the DEC VT100.

To support graphical output, a more sophisticated terminal was built that allowed programs on the computer to send commands to draw pictures and add text. The Unix protocol was called “X” and the final version of it was X11.

Linux was a reimplementation of Unix on a PC. However, every PC has its own keyboard and screen and did not require a separate external terminal. To preserve the original Unix behavior, Linux includes components that turn the keyboard, mouse, and video display into a simulated terminal. During boot it simulates a VT100, and then if you load a desktop like Gnome it behaves as an X11 terminal.

Today the network replaces the phone line. Linux supports login of remote users over the network through either telnet/ssh. However, while the keyboard and mouse have not changed, a modern video display adapter has massive computing power and transfers data from the CPU about 640 times as fast as a typical network adapter.

There are X11 terminal emulators in Windows and you have been able to use them with earlier versions of WSL and Hyper-V Linux VMs. However, the Linux developers realized long ago that using a modern video card to emulate X11 was a silly idea. There has been a long running project to create a new system called Wayland that continues to support X11 applications but exposes the greater power available with a GPU.

Today WSL has support for Wayland built in. It is no longer necessary to install an X11 terminal emulator in Windows and start it running before you run a graphics program in WSL. When you run the program, a window opens up on the Windows desktop presenting the same user interface that the program would have on a Linux desktop like Gnome.

image-20250307-152820.png

Integrated File Systems

The Windows File Manager has a “Linux” section that displays your WSL Linux distros:

image-20250311-175204.png

And if you select one you see the Linux directories in Windows File Explorer:

image-20250311-175336.png

The Linux file system also has a full Windows path under the \\wsl$\ prefix, then the distro name, and then the full Linux path.

C:\Users\gilbert>dir \\wsl$\Ubuntu\home\gilbert Volume in drive \\wsl$\Ubuntu has no label. Volume Serial Number is 0000-0000 Directory of \\wsl$\Ubuntu\home\gilbert 11/23/2024 09:12 PM 220 .bash_logout 11/23/2024 09:13 PM 0 .sudo_as_admin_successful 03/13/2025 11:20 AM 1,567 .bash_history 11/26/2024 09:04 PM <DIR> .landscape 03/07/2025 11:49 AM <DIR> snap 11/26/2024 09:08 PM <DIR> .local 11/26/2024 09:35 PM 10 test.txt ... 11/23/2024 09:12 PM 3,771 .bashrc 7 File(s) 6,375 bytes 7 Dir(s) 1,021,575,172,096 bytes free

In Linux, your Windows disk letters appear as subdirectories of /mnt

ls -ltr /mnt total 0 drwxrwxrwx 1 gilbert gilbert 4096 Mar 7 11:16 d drwxrwxrwx 1 gilbert gilbert 4096 Mar 7 11:16 c drwxrwxrwx 1 gilbert gilbert 4096 Mar 7 11:16 e drwxrwxrwt 2 root root 60 Mar 7 11:48 wsl drwxrwxrwt 7 root root 300 Mar 7 11:48 wslg

Here you can see the C, D, and E disk letters.

When you use wsl.exe, the Windows current directory becomes the Linux current directory, but it is now expressed as a subdirectory of /mnt. See how C:\users\gilbert becomes /mnt/c/Users/gilbert.

C:\Users\gilbert>wsl gilbert@MWPFxxxxx:/mnt/c/Users/gilbert$

Here the command prompt shows that the current directory was C:\Users\gilbert, and when you get to bash in Linux you are in the same directory, but the path is now expressed as starting in /mnt.

Installation

You can install WSL and the distribution from the Windows Store in Windows 11. However, there is no Store in Windows Server 2025, so you might as well get used to the command line.

If WSL is not installed, run an elevated (Run as administrator) command prompt and enter

wsl --install

This installs the minimal Hyper-V feature, then downloads and installs the Microsoft Linux Kernel, and finally downloads and installs the default Ubuntu distribution. This requires a reboot to complete the installation and bring up Hyper-V. When you run wsl for the first time, the Ubuntu system will go through a first-time initialization asking you to enter the userid and password that will administer the system.

If you want to update it to a new release, type “wsl --update”.

WSL Implementation

9P Protocol

Bell Labs created Unix when the smallest computer was the size of a large refrigerator and was shared by multiple users sitting at terminals.

As smaller, less expensive machines became common and were connected to each other over high-speed networks, Bell Labs started work on a project to create a new OS specifically designed for that type of hardware. They named the project “Plan 9” after what is widely regarded as the Worst, Cheesiest Movie Ever Made, a film titled “Plan 9 From Outer Space”. The project never completed, and the system was never released.

However, their first problem was to imagine a set of services and a network protocol that would turn a network of small machines into what appeared to be a single multiuser operating system. You could run programs anywhere and access files anywhere and have people connect anywhere. They created a protocol named “9P” that would more closely couple multiple computers. When Microsoft was looking for an architecture to tightly couple the Windows and Linux Kernels together, they chose “9P” because it already had the idea of cross mounting directories and disks and starting a child process on a different machine with the same current directory and environment variables.

Windows Home Edition

Windows Home Edition is not licensed to run Hyper-V. It does run WSL. When WSL is installed, it configures a limited version of Hyper-V with no user configuration, one virtual machine, and simple default networking.

On Pro or Enterprise, you can install real Hyper-V and configure your own VMs, but the WSL VM and networking remain invisible to the Hyper-V manager. There are a few commands that will display them, but mostly you don’t need to know there are there.

Distro Backup and Restore

While you could probably backup the VHDX file that contains the file system, you could not restore it anywhere else. The WSL export operation creates a tar file copy of your system that you can save or share with others.

wsl --export <distro> <tar-file-pathname>

You can then import it back to your system with a second distro name (and a selected second location for the files). Another developer can import it on their system and inherit your programs and packages. You could export and import alternate versions of the same version of Linux to use when you are working on different applications or need to run different groups of test cases.

wsl --import <newdistroname> <location> <tar-file-pathname>

where <location> is the Windows disk file directory under which a new directory is createdto hold all the new distro files.

WSL Networking

This is a subject that is being changed by Microsoft, but there are a few basic ideas that remain constant.

Concept: One System

Using Hyper-V, you can create regular Virtual Machines that run a fully separate Linux system with a GUI desktop. Typically, you connect the VMs to each other and to the host Windows system using a virtual network. Each VM has its own hostname, its own IP address, and its own configuration for networking and all other functions.

Microsoft wants to regard WSL as part of the host Windows machine and not a separate system. You add WSL as a feature to your already configured Windows system. There is no duplicate configuration in WSL. There is only one hostname and only one shared internet connection. Add WSL to your laptop. Now as you connect to a wired network, or switch to a wireless network, or connect to a VPN, or change your DNS server in Windows and the WSL programs inherit the Windows networking configuration changes.

Concept: There is one “localhost” shared by Windows and WSL

Localhost (address 127.0.0.1) provides the appearance of networking when the client and server are two programs on the same system. In both Windows and Linux it is implemented as a Kernel device driver without any real device. The programs use all the protocols of real Internet communication, but the data goes from one program to the Kernel to the other program.

The Localhost device drivers in Windows and Microsoft Linux have been modified to communicate with each other, synchronize their assignment of port numbers, and exchange data as needed. They appear to be two halves of a single object. Each port number is assigned to one program on either system (assigned on a first come basis) and data is delivered based on the port destination and moved through the VMBus as needed.

Normal “NAT” WSL Networking

When you buy an IOT device for your home, you only need to give it the password for your Wi-Fi. The rest of its network configuration is done by your Wi-Fi “router”, typically the box provided by your ISP. We call these devices “routers”, but technically they are a Gateway device that connects your home network to the ISP network and through it to the Internet.

Back in the early 1990’s (before the Web) when a Yale home user would connect to the Internet over a phone line connected to a Yale phone number, a startup company named Linksys began to sell its first home Gateway router device and a New Zealand company created a Windows program named “WinGate” that could turn any Windows computer into an Internet Gateway. Both the Linksys box and the WinGate program provided two services:

  • DHCP assigns a temporary internal network address (something like 192.168.1.101) to each new device that connects to the home network. It also provides all the other network configuration parameters (like name server addresses) that the devices need.

  • Network Address Translation (NAT) connects home client programs to Internet servers by converting the home device IP addresses to the one address assigned to the Gateway device by the ISP (or before ISP’s, by the device on the other end of the Yale phone number). To Yale and the Internet, all the client programs running on all the devices in your home appear to be running on the one Gateway device.

Windows contains code to perform the Gateway function, but it is not fully exposed on Windows 10/11. The NAT service is available, and you can configure it on Windows for any physical or virtual network using the PowerShell command “New-NetNAT”. DHCP and DNS, on the other hand, are only exposed on Windows Server.

However, the full Gateway services are available on every Windows system for specific Hyper-V virtual network functions. The full Hyper-V service that allows you to create your own VMs automatically creates one virtual network named “Default” and provides in Windows a Gateway function. Each Hyper-V VM you create should typically be connected to the Default network. The VM is then dynamically assigned all the necessary network configuration and is able to access your local (home or work) network and the Internet through the network configuration and devices you already configured to Windows.

Although WSL is implemented by Hyper-V, the WSL VM is hidden and is not visible in the Hyper-V Manager and to the Hyper-V VMs you create. So, a separate WSL virtual network is created, and a separate instance of the Gateway service is started.

This is simultaneously an example and a violation of the “One System - Two Kernels” model. It implements the “single system configuration” vision because you do all your network configuration on Windows and the WSL Linux system automatically inherits the Windows network. If you have a laptop running Windows, then wherever you are at home, work, or travelling, once you connect Windows to a wireless, wired, 5G, or VPN network then WSL Linux piggybacks on that connection. Windows decides which adapter is the best choice to send data to any other device, which DNS server to use to resolve names, how to roam from one access point to another. The Windows Firewall and anti-virus protection also protects WSL.

However, a Gateway is defined as a device on a network accessed by another device on the same network. In order to use the Windows Gateway service to provide NAT and DHCP to the Linux Kernel, it is necessary to have a virtual ethernet adapter on Windows, a virtual ethernet adapter on the Linux Kernel, and a Hyper-V network connecting them. In this one case, the WSL Linux VM has to behave as a separate computer.

Since WSL is not supposed to look like a separate machine, the the Hyper-V WSL virtual network is hidden from Windows programs (including all the Setting panels) and from other Hyper-V VMs. It is entirely self-configuring and does not need to be exposed to the user. The only Windows program that uses it is the Gateway service. However, on the Linux VM it has to be exposed as the eth0 virtual network adapter. Linux does see a virtual IP address, but you are supposed to pretend that this adapter and address are invisible.

Any code you write that connects a client program to a server program, one running on Windows and the other running on WSL Linux, should use the Localhost device to communicate. You agree to pretend that the two programs are both running on the same system. Writing code that uses WSL eth0 is naughty.

Mirrored Networking

There is limited Microsoft documentation.

For advanced users, Microsoft provides an WSL startup option to run in Mirrored networking mode. This applies to all WSL Linux instances, and to use it you must give up all the convenience of the NAT Gateway.

In a sense, Mirrored networks is a more authentic implementation of the “One System - Two Kernels” model of behavior. The Linux Kernel gets one virtual network adapter for each Windows network adapter. The device drives in the Windows Kernel extend the Localhost behavior to real network adapters.

On any adapter, a given TCP port number can only be owned by one program. That program may be in Windows or in any of the WSL distros, but whichever program binds to the port first gets it and any subsequent attempts to bind to the same port number are rejected on the other systems because the “port is in use”.

It is probably insane to use Mirrored networks on a laptop that you move daily from home to work and from room to room. Windows knows how to adapt automatically when you move from place to place, but each WSL Linux instance would have to know somehow when to communicate through the wired network, Wi-Fi, or the VPN. Reconfiguring all your Linux distros every time you go to a meeting room is irrational.

However, if you have a desktop computer in a single location with networks that never change, then enabling Mirrored networking and configuring the network for Windows and then separately for each WSL instance may be feasible. It is not fun.

More importantly, the things that you might want to use Mirrored networks to do are also the things that WSL doesn’t do particularly well given its design to shut down within a minute after you stop using it. If you are seriously thinking about Mirrored WSL networking, you should first stop and consider using a real Hyper-V Linux VM instead.

Hyper-V Default: NAT + Selective Mirroring

While WSL requires you to choose between using a NAT Gateway and extending the network adapters to Linux, Hyper-V allows you to do both at the same time.

Every Hyper-V VM probably should have the Default network as its first adapter. Hyper-V then allows you to create additional virtual network adapters that are associated with real adapters and talk directly to external physical networks.

Each VM gets access to your home network, work network, and the Internet through the Default network. Any extra adapter you configure would only be used to talk with specific machines, commonly so that a server on your VM can be accessed by a client on your external network.

If the host is a laptop and you take it to work, the extra network may be disconnected and while you are moving around, the VMs use Default and its Hyper-V Gateway. When you return to your home or office desk and plug the wired network back in to the extra adapter, then it becomes available for whatever special use you have configured.

However, you cannot configure WSL this way.