How Linux kernel interacts with your hardware
Discover and control hardware
Let’s talk about discovering and controlling hardware. Now, an application, like a program or a command, we’ll call Library functions, and some of those Library functions, the system calls them the Kernel, and the Kernel is responsible for really interacting with the hardware.
Some commands for hardware info include lshw, list hardware, or for getting information about PCI devices, lspci, or USB devices, lsusb, or block devices, lsbk. Or about your CPU, lscpu, or about other devices, lsdev. Now of course, your Linux distro, your particular installation of Linux might now have all these commands, you might need to install ’em. And there’s even a chance that the distro you’re using doesn’t even have all these commands, but, generally, these are available and they’re pretty handy. I use lspci, for example, pretty frequently.
There’s also commands for controlling or configuring hardware, not just finding out information about hardware. The hdparm command is for hard disks. If you wanna find out the speed of your SSD for example, you can use hdparm, and you can set certain configuration options on your disk with hdparm. You can set certain parameters on your hard disk with hdparm as well. The proc and sys files systems and device files also provide a mechanism for us to write information through the Kernel to devices, or to tell the Kernel that we want to change some configuration or control hardware.
Some devices, like the keyboard on your laptop, are normally controlled via the special I/O Bus, and the commands inb and outb can transfer bytes to control, like say, to lock your keyboard. There would be a writing a certain byte to a certain address, with the outb command. For PCI devices you can change registers and resources with the setpci command. Now if we wanted even more information about our PCI devices, we could use the Verbose option -v, now I have a wealth of information.
Sometimes this kinda information is really valuable to find out what driver you need. Say your network isn’t working and you don’t seem to have the device even showing up. Well, PCI devices identify themselves to the Kernel in a standard way. Even if there’s no driver, the Kernel still knows of your PCI devices. So you can get this information with the lspci command. And then you can go on the web and you can search for a driver for that device. Maybe it turns out that you have to go to the vendor’s website to get a driver for a particular device. Now besides PCI devices, maybe we want to look at UBS devices. Maybe we want to find out something about our CPU.
Now the BogoMIPS line is a Linux thing. BogusMIPS, but gives you an idea of a factor, a speed that the Kernel assigned to this, and the bigger the number, the better, but that number doesn’t really correspond to MIPS. It’s used for calibrating certain things.
Understand system call mechanics
Let’s talk about system calls. Well, system calls are functions implemented by the kernel and meant to be called from user space. They are the application interface to the kernel. For Linux, there’s about 300 of ’em.
You can see the prototypes for the functions in the “include” file, include/uapi/asm-generic/unistd.h. Of course, the path name may differ for different kernel versions or different implementations, distros of Linux, but uni standard dot h is a key header file.
Also, on Linux, in the man pages, those functions that are system calls are put in section two. For example, there’s a number of things that are also commands, and system calls, and maybe files, so there might be entries in different sections with the same name.
So, if you want to find out about system call, you need to look in section two. Now, “application” doesn’t call a system call directly, as it turns out. Applications call library functions, and the system calls from C are in the standard C library. And the standard C library is what’s going to do the real system call mechanism to invoke the kernel.
So system call mechanics involve the library, using some sort of special instructions. So these are Assembly language instructions. That means this part is architecture dependent. From you application, you just call “open,” or “read,” or “write,” and that’s architecture independent, that’s portable, but the standard C library has to be implemented for each architecture, because that transition in the kernel is special. And how it’s implemented is certainly up to how the library works, but commonly, it’s gonna make use of registers.
So, when you call the corresponding function in the library, for, say, “read,” that function library will put your parameters in registers, and then invoke some special instruction to get the kernel executed. And then the kernel can read those parameters out of the registers. The kernel has to figure out which system call it was, and then call it.
When the system call returns, it’s gonna return back to the library. And the protocol is for the system call to return a negative value, with the absolute value of that indicating what error there was. If there’s not an error, then it returns zero or a positive number. The read function, for example, returns zero on end of file, or the positive number is the number of characters read.
Now, if the kernel returns a negative value to the library, then the library sets what’s effectively a global variable in your process, called “errno,” to represent what error there was. And then the library returns “minus one” to the application. So applications calling system calls get back “minus one” from the library, and errno is set to represent what error there was. Now, the library normally does not set errno if there’s no error.
So you don’t want to just check errno, you want to check the return value from the function to see if there was an error. Let’s look at some man pages for system calls. So, to make sure we’re looking in section two, we gotta put a “two.” Let’s look at “read.” Everybody knows how to read. We see here reports, up on the top, “read two,” and we see the name with a little one-liner description, and then we see the synopsis, this is handy, says what we need to include.
Now this is user-space stuff, that’s a user-space header, and then we see the read function. So this is what we call “from user space.” In the kernel will be a function that corresponds to this. And if we scroll down a bit, we see, on success, the number of bytes read is returned, with zero indicating end of file, and if we read down a little bit farther, we see “on error minus one” is returned. And errno is set appropriately. So there are man pages for all the system calls. So you can look ’em up with the man command to get important information.
In the next article, I’ll talk about read messages from the kernel and how to use the /proc and /sys filesystems.
Follow me if you want to know more about Linux kernel and other Open Source technologies.