Abstract: drivers of most hardware devices are used as modules. These modules are automatically loaded during Linux Startup. This article briefly describes the process through the kernel source code.
1. The driver module contains detailed information such as the device manufacturer and device ID number.
What should I do if I want to automatically load a module during kernel startup? The easiest way to think of it is to go to/etc/init. d/, and then add a STARTUP script in/etc/rcN. d/create a symbolic link in the directory. The Link name starts with "S". When the kernel is started, the script will automatically run, in this way, you can use modprobe in the script for automatic loading. However, we found that many hardware device drivers are loaded in the kernel, while searching for the/etc directory did not find any scripts responsible for loading the modules of these hardware device drivers. How are these modules loaded? Each device has Verdon ID, device ID, and subvendor.
Id. Each device driver must specify the Verdon ID, devieceid, and subvendorid devices that can provide services. Taking a PCI device as an example, it implements this function through a data structure of pci_device_id. For example, the pci_device_id of rtl8139 is defined:
static struct pci_device_id rtl8139_pci_tbl[] = {{0x10ec, 0x8139, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },{0x10ec, 0x8138, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },......}MODULE_DEVICE_TABLE (pci, rtl8139_pci_tbl);
The above information indicates that any PCI device whose Verdon ID is 0x10ec, device ID is 0x8139, and 0x8138 (subvendor ID and subdeviceid are pci_any_id, which indicates no restriction .), You can use this driver (8139too ).
Module 2 the installation process extracts information about the device vendor and device ID, and writes the information to the modules. Alias file.
During module installation, depmod generates the following information based on the rtl8139_pci_tbl information in the module and saves it to/lib/modules/uname-r/modules. the alias file contains the following content:
alias pci:v000010ECd00008138sv*sd*bc*sc*i* 8139tooalias pci:v000010ECd00008139sv*sd*bc*sc*i* 8139too......
Then, the latest 10ec indicates that the vendor ID is 10ec. The 00008138 after D indicates that the device ID is 8139, And the SV and SD indicate that the subvendor ID and the subdevice ID, and the asterisks behind it indicate any matching. In addition, the dependency between modules is saved in the/lib/modules/uname-r/modules. Dep file. The content of the dependency between modules is as follows:
(Path information is saved here .) 8139too. Ko: MII. Ko
3. During kernel startup, the device ID and other information read during bus enumeration are sent to udevd. udevd finds and loads the matching Driver Module Based on the modules. Alias file.
During kernel startup, the bus driver will enumerate the bus protocol and create a device object for each device. Each bus object has a kset object, each device object is embedded with a kobject object, and kobject is connected to the kset object, so that between the bus and the bus, A tree structure is formed between the bus and the device. When the bus driver creates a device object for the scanned device, it initializes the kobject object and connects it to the Device Tree. It also calls kobject_uevent () this event and related information (including the vendorid and DeviceID of the device .) It is sent to the user State through Netlink. After detecting this event in user-mode udevd, you can open the/lib/modules/uname-r/modules. Alias file based on this information.
PCI: v201710ecd2138138sv * SD * BC * SC * I * 8139too learned that the newly scanned Device Driver Module is 8139too. So modprobe will know to load the 8139too module, while modprobe according to modules. dep file found that 8139too depends on MII. ko, if the MII. if Ko is not loaded, modprobe first loads MII. ko, and then load 8139too. ko.
4. Experiment
In your shell, run:
# Ps aux | grep udevdroot 25063 ...... /sbin/udevd -- daemon we get the udevd process ID as 25063. Now we end this process: # Kill-9 25063 and then track udevd and run it in shell: # strace-F/sbin/udevd -- daemon, the output of udevd is as follows :...... close (8) = 0 munmap (0xb7f8c000, 4096) = 0 select (7, [3 4 5 6], null, null, null we find that udevd is blocked in the select () function. The select function is prototype: int select (INT NFDs, fd_set * readfds, fd_set * writefds, fd_set * limit TFDs, struct timeval * timeout); the first parameter: NFDs indicates the largest file description symbol. Here it is 7 (obviously 6 ?). The second parameter: readfds is the set of read file descriptors, which are 3, 4, 5, and 6. The third parameter: writefds is the set of write file descriptors, Which is null. The fourth parameter: Invalid TFDs is a collection of abnormal file descriptors. Here it is null. The fifth parameter: timeout specifies the time-out time, which is null. The select function is used to return data if any file in readfds is readable, any file in witefds can be written, or any file in limit TFDs has an exception. Otherwise, the current process is blocked until the appeal conditions are met, or the current process is awakened because the blocking time exceeds the time specified by timeout, And the SELECT statement is returned. Therefore, here udevd waits for the 3, 4, 5, 6 files to be wakened only when the data is readable. Run the following command in shell: # ps aux | grep udevdroot 27615 ...... strace-O/tmp/udevd. debug-F/sbin/udevd -- daemonroot 27617 ...... /sbin/udevd -- The daemonudevd process ID is 27617. Now let's take a look at several files waiting for the SELECT statement: # cd/proc/27615/FD # ls-ludevd standard input, standard output. All standard errors are/dev/null.0->/dev/null1->/dev/null2->/dev/nulludevd. Wait for the following files. 3->/inotify4-> socket: [331468] 5-> socket: [331469] 6-> pipe: [331470] 7-> pipe: [331470] because it is not convenient to insert a 8139 Nic In the running process, now we use a USB flash drive for testing. When you insert a USB flash drive, you will see the strace output, from its output, we can see that after udevd returns the SELECT statement, it calls modprobe to load the driver module, calls sys_mknod, and creates the corresponding node in the dev directory. Execve ("/sbin/modprobe", ["/sbin/modprobe", "-Q", "USB: v05acp1301d0100dc00dsc00dp00" ......] ...... mknod ("/dev/SDB", s_ifblk | 0660, makedev (8, 16) = 0 ...... here the modprobe parameter "USB: v05ac... "modules. A module in alias. You can use udevmonitor to view the message sent by the kernel to udevd through Netlink. Run: # udevmonitor -- env in the shell and insert it into the USB flash disk. Then you will see the message sent to udevd. = Kernel processing process =: here we take the PCI bus as an example to see how the kernel handles this process. When the PCI bus driver scans a new device, it creates a device object and calls the pci_bus_add_device () function. This function will eventually call kobject_uevent () use Netlink to send messages to user-mode udevd. Int pci_bus_add_device (struct pci_dev * Dev) {int retval; retval = device_add (& Dev-> Dev );...... return 0;} The device_add () code is as follows: int device_add (struct device * Dev) {struct device * parent = NULL; Dev = get_device (Dev );...... error = bus_add_device (Dev); If (error) goto buserror; kobject_uevent (& Dev-> kobj, kobj_add );......} after the relevant data structure is prepared, device_add () calls kobject_uevent () to send the message to the udevd of the user space. Int kobject_uevent (struct kobject * kobj, Enum kobject_action action) {return kobject_uevent_env (kobj, action, null);} int partition (struct kobject * kobj, Enum kobject_action action, char * envp_ext []) {struct kobj_uevent_env * env; const char * action_string = kobject_actions [action]; const char * devpath = NULL; const char * subsystem; struct kobject * top_kobj; struct kset * kset; struct kset_uevent_ops * Uevent_ops; u64 seq; int I = 0; int retval = 0 ;...... /* default keys */retval = add_uevent_var (ENV, "Action = % s", action_string); If (retval) goto exit; retval = add_uevent_var (ENV, "devpath = % s", devpath); If (retval) goto exit; retval = add_uevent_var (ENV, "subsystem = % s", subsystem); If (retval) goto exit; /* keys passed in from the caller */If (envp_ext) {for (I = 0; envp_ext [I]; I ++) {retval = add_uevent _ VaR (ENV, envp_ext [I]); If (retval) goto exit ;}}...... /* send a message through Netlink, so that the udevd process in the user State will return the message from the select () function and perform corresponding processing. */# If defined (config_net)/* Send Netlink message */If (uevent_sock) {struct sk_buff * SKB; size_t Len; /* allocate message with the maximum possible size */Len = strlen (action_string) + strlen (devpath) + 2; SKB = alloc_skb (LEN + env-> buflen, gfp_kernel ); if (SKB) {char * Scratch;/* Add header */scratch = skb_put (SKB, Len); sprintf (Scratch, "% s @ % s", action_string, devpath);/* Copy keys to our continuous event payload buffer */for (I = 0; I <env-> envp_idx; I ++) {Len = strlen (env-> envp [I]) + 1; Scratch = skb_put (SKB, Len); strcpy (Scratch, env-> envp [I]);} netlink_cb (SKB ). dst_group = 1; netlink_broadcast (uevent_sock, SKB, 0, 1, gfp_kernel) ;}# endif ...... return retval ;}5. Thinking
Now we know that the device files under the/dev directory are created by udevd, but during kernel startup, We need to mount a root directory. Generally, our root directory is on the hard disk, for example:/dev/sda1, but before the driver corresponding to the hard disk is loaded,/dev/sda1 does not exist. If no/dev/sda1 exists, you cannot mount the root directory through Mount/dev/sda1. On the other hand, udevd is an executable file. If the hard drive program is not loaded and the root directory does not exist, udevd cannot run. If udevd cannot run, the disk driver will not be automatically loaded, And/dev/sda1 cannot be created automatically. Isn't it a deadlock? So how did you start Linux?