This is a basic kernel module that implements the following features:
- entry point for loading and unloading the kernel module
- debug statements that can be observed with
dmesg
- optionally transmitting a parameter,
magic
, when the module is loaded (otherwise a default value is used) - creation of a character device,
/dev/oracle
(mknod must be invoked manually for now) - implements the
read
API, which takes the current value ofmagic
and copies it from the kernel to the user-space buffer - reacts to any
ioctl
sent to the module by incrementingmagic
This was tested on Debian 7 x64.
This only needs to be done once, to prepare the compilation environment.
sudo apt-get install build-essentials
sudo apt-get install linux-headers-$(uname -r)
It is expected that you already have Python, it will be needed for the user-mode program to test ioctl
handling.
Get into the directory where you cloned the git repo and run make
. The output will be stored in hellok.ko
.
git clone https://github.com/ralienpp/hello-kernel.git
cd hello-kernel
make
You can make sure this loads and unloads correctly as follows:
sudo insmod ./hellok.ko
run
lsmod
and it should be in the list of loaded modules, e.g.Module Size Used by hellok 12452 0
unload it with
sudo rmmod hellok
run
dmesg
to see what has just happened, the debug lines should be there:[73964.820487] hellok: module license 'unspecified' taints kernel. [73964.820492] Disabling lock debugging due to kernel taint [74342.727012] Loaded hellok [74469.278069] Unloaded hellok
- You can pass a parameter at load time, e.g.
sudo insmod ./hellok.ko magic=12
. If not specified, a default value will be used. - The value can be examined after the module is loaded:
cat /sys/module/hellok/parameters/magic
- You can write to it (if permissions allow so), e.g. (as root)
echo 45 > /sys/module/hellok/parameters/magic
To check if the devices was created successfully, run cat /proc/devices
and expect something like this:
Character devices: 100 Oracle
The number is the MAJOR
defined in the code, and the name is the string we used to name the device.
Before you can interact with this device, run sudo mknod -m 666 /dev/oracle c 100 0
(create a character device, allowing everyone to do everything with it, 100 is MAJOR
as defined in the code, and 0 is the minor [taken "as is" from a guide]).
To demonstrate communication between kernel-space and user-space, you can run cat /dev/oracle
and the value of magic
will be shown on the screen as a string.
This happens by copying this value from a buffer in the kernel's address space, into a buffer in user-space, using copy_to_user
.
Alternatively, run python testusermode.py
, which will send an ioctl
to /dev/oracle
(the driver doesn't care what command is sent, they're all handled equally). You can observe the effect by reading from /dev/oracle
, the read value should be incremeted after each ioctl call.
- http://www.tldp.org/LDP/lkmpg/2.4/html/x281.htm
- https://www.apriorit.com/dev-blog/195-simple-driver-for-linux-os
- https://www.youtube.com/watch?v=Zn8mEZXr6nE
- http://www.virtsync.com/c-error-codes-include-errno
- https://lwn.net/Articles/115651/
- why
static
? Other tutorials don't have that. - what is the point of having major and minor versions?
- can
mknod
be invoked automatically? cat
never stops, should I add anEOF
at the end? (does stop when run as root, strange)#define ENOTTY 25 /* Not a typewriter */
why can't my character device handle this?- what is a safe way to avoid
MAJOR
collisions?
- use
%zu
forsize_t
in debug statements
ioctl
is used because I was aware of this concept earlier and expected it to be easier. After reading more about it while implementing it, I found it convenient that I can send avoid
payload, and use the presence of the message itself as a signal, which is sufficient for the purposes of this prototype. Otherwise, the benefit of theioctl
is that it provides the possibility to send arbitrary payloads, which may be handy in the future.- clearly, the communication done via
/sys/module/hellok/parameters/magic
is another form of interaction between kernel-space and user-space, which is not anioctl
and which appears to be a much simpler/cheaper method, because I got it "for free" (no need to write a handler for it). - when it comes to user-space, the "cheapest" way to prove it works is to read from
/dev/oracle
with any existing tool, thus there's no need to write any code at all. - however, I also wrote a Python program in order to check how the
ioctl
handler works. I used this language because it was much easier to write in, and I was thinking that if there were implementation issues it would be easier to debug them using the REPL. However, due to the simplicity of the current driver, there's not much room for something not to work.