Showing posts with label File operations. Show all posts
Showing posts with label File operations. Show all posts

Saturday, 3 December 2016

Introduction to kernel modules


•       Objectives
•        Understanding Kernel modules
•       Writing a simple kernel module
•       Compiling the kernel module
•       Loading and unloading of modules
•       Kernel log
•        Module dependencies
•       Modules vs Programs

Kernel modules
•       Linux kernel has the ability to extend at runtime the set of features offered by the kernel. This means that you can add functionality to the kernel while the system is up and running.
•       Each piece of code that can be loaded and unloaded into the kernel at runtime is called a module.
•       Module extends the functionality of the kernel without the need to reboot the system.
•       The Linux kernel offers support for quite a few different types (or classes) of modules, including, but not limited to, device drivers.
•       Each module is made up of object code (not linked into a complete executable) that can be dynamically linked to the running kernel.

Advantages of modules
•       Modules  make it easy to develop drivers without rebooting: load, test, unload, rebuild & again load and so on.
•       Useful to keep the kernel size to the minimum (essential in embedded systems). Without modules , would need to build monolithic kernel and add new functionality directly into the kernel image.
•       Also useful to reduce boot time, you don’t need to spend time initializing device that may not be needed at boot time.
•       Once loaded, modules have full control and privileges in the system. That’s why only the root user can load and unload the modules.

Hello module
/* hello.c */
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
static int __init hello_init(void)
{
                printk(“Hello :This is my first kernle module\n");
                return 0;
}
static void __exit hello_exit(void)
{
                printk(“Bye, unloading the module\n");
}
module_init(hello_init);
module_exit(hello_exit);
MODULE_DESCRIPTION("Greeting module");
MODULE_AUTHOR(VSalve");
MODULE_LICENSE("GPL");

Module explanation
•       Headers specific to the linux  kernel <linux/xxx.h>
•       No access to the usual C library
•       An initialization function
•       Called when the module is loaded, returns an error code (0- success, negative value on failure)
•       Declared by the module_init() macro:
•       A cleanup function
•       Called when the module is unloaded.
•       Declared by the module_exit() macro.
•       Metadata information declared used MODULE_DESCRIPTION and MODULE_AUTHOR

Compiling a module
•       Out of tree
–      When the code is outside of the kernel source tree, in a different directory.
–      Advantage:  Easier to handle than modifications to the kernel itself.
–      Disadv: Not integrated to the kernel configuration/compilation process, needs to be build separately, driver cannot be built statistically if needed.
•       Inside the kernel tree
–      Well integrated into the kernel configuration/compilation process.
–      Driver can be build statistically if needed

Compiling an out-of-tree module
•       Makefile to compile module
•       KDIR := /path/to/kernel/sources
obj-m := hello.o
all:
                make -C $(KDIR) M=$(PWD)  modules
clean:
                make –C $(KDIR) M=$(PWD) clean


Module utilities
•       modinfo <module_name>
•       Gets information about the module: parameters, license, descriptions and dependencies
•        insmod <module_name>.ko
•       Load the given module. Full path of module is needed
•        rmmod <module_name>
•       Unloads the given module
•        lsmod <module_name>
•       Displays the list of modules loaded.
•       Check cat /proc/modules

Kernel log
•       When a new module is loaded, related information is available in the kernel log.
–      The kernel keeps its messages in a circular buffer.
–      Kernel log messages are available through the ‘dmesg’ command
–      Kernel log messages can be seen in /var/log/messages file

Module dependencies
•       Some kernel module can depend on other modules, which need to be loaded first.
•       Dependencies are described in
                /lib/modules/<kernel-version>/modules.dep
•       This file is generated when you run make modules_install
•       sudo modprobe <module_name>
–      Loads all the modules the given module depends on. Modprobe looks into /lib/modules/<kernel-version> for the object file corresponding to the given module
•       Sudo modprobe –r <module_name>
–      Remove the module and all dependent modules, which are no longer needed.

Applications Vs. Kernel modules
Application
Kernel module
Performs single task from beginning to end
Module registers itself to serve the future request and its ‘main’ function terminates on loading.
Application can call functions, which it doesn’t define. The linking stage resolves the external references loading the appropriate libraries. E.g libc for ‘printf’ function.
The module is linked only to the kernel and it can only the functions that are exported by the kernel.
No C library is linked with the kernel.


Passing command line arguments
•       Modules can take command line arguments, but not with the argc/argv you might be used to.
•       To allow arguments to be passed to your module, declare the variables that will take the values of the command line arguments as global and then use the module_param() macro, to set the mechanism up.
•       At runtime, insmod will fill the variables with any command line arguments that are given, like ./insmod mymodule.ko myvariable=5. The variable declarations and macros should be placed at the beginning of the module for clarity.
•       The module_param() macro takes 3 arguments: the name of the variable, its type and permissions for the corresponding file in sysfs. Integer types can be signed as usual or unsigned.
•       int myint = 3; module_param(myint, int, 0);
•       If you'd like to use arrays of integers or strings see module_param_array() and module_param_string().
•       module_param(foo, int, 0000)
•       The first param is the parameters name.
•       The second param is it's data type
•       The final argument is the permissions bits, for exposing parameters in sysfs (if non-zero) at a later stage.
•        Example
•       static short int myshort = 1;
•       static int myint = 420;
•       module_param(myshort, short, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
•       module_param(myint, int, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
•       module_param_array(name, type, num, perm);
•       The first param is the parameter's (in this case the array's) name
•       The second param is the data type of the elements of the array
•       The third argument is a pointer to the variable that will store the number of elements of the array initialized by the user at module loading time
•       The fourth argument is the permission bits
•       static int myintArray[2] = { -1, -1 };
•       static int arr_argc = 0;
•       module_param_array(myintArray, int, &arr_argc, 0000);

Modules spanning multiple files
/*hello_start.c*/
#include <linux/module.h>
#include <linux/kernel.h>
int init_module(void)
{
        printk("Hello :This is my first kernle module\n");
        return 0;
}
/*hello_stop.c*/
#include <linux/module.h>
#include <linux/kernel.h>
void module_cleanup(void)
{
        printk("Bye, unloading the module\n");
}
MODULE_DESCRIPTION("Greeting module");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("VSalve");

Makefile
KDIR:=/lib/modules/2.6.35-31-generic/build/
obj-m += startstop.o
startstop-objs := hello_start.o hello_stop.o
all:
        make -C $(KDIR) M=$(PWD) modules
clean:
        make -C $(KDIR) M=$(PWD) clean

Functions available to modules
•       In the hello world example, you might have noticed that we used a function, printk() but didn't include a standard I/O library.
•       That's because modules are object files whose symbols get resolved upon insmod'ing.
•       The definition for the symbols comes from the kernel itself; the only external functions you can use are the ones provided by the kernel.
•       If you're curious about what symbols have been exported by your kernel, take a look at /proc/kallsyms.


Exercises of System Calles

Now it’s your turn to do some exercise and write a comment if you face any issue.

•       Implement cp src_file.txt dst_file.txt

•       Implement cat src_file.txt, read and display file contents

•       Implement cp src1.txt src2.txt src3.txt dst.txt

•       Implement a program to display the contents of file from the starting and from given offset.

•       Implement program for creating dir, change dir, create a file, write data in to a file, copy file to another file and display contents of both the files.

•       Implement a program to create a file read permission, Perform write on it then change file permissions to make writable, display file contents


•       Implement a program to read the file stats and statistics.

File management

•       We will start our study with the functions available for the file I/O operations such as open a file, read & write a file, close a file and so on.

•       Most file IO operations are performed with the open, read, write, lseek and close system calls.

•       File descriptors
–      File descriptor is a non-negative integer representing the file opened in the kernel.
–      When is opened or newly created, the kernel returns a file descriptor to the process.

–      During read/write, the file is identified by the file descriptor that was returned by open()
System call
File Operation
open
open a file or device fs/open.c
creat
create a file or device
close
close a file descriptor
dup2
duplicate a file descriptor
dup
duplicate an open file descriptor
mmap
map files into memory
(Only the PROT_READ protection flag is supported.)
munmap
unmap files from memory
pread
read from a file descriptor at a given offset
pwrite
write to a file descriptor at a given offset
read
read from a file descriptor
readv
read data from multiple buffers
write
write to a file descriptor
writev
write data from multiple buffers
lseek
reposition read/write file offset
llseek
move extended read/write file pointer
lstat
get file status
truncate
set a file to a specified length
ftruncate
set a file to a specified length
unlink
delete a name and possibly the file it refers to

System call categories

•       System calls can be roughly grouped into five major categories:

  1. Process Control.
    1. load
    2. execute
    3. create process
    4. Terminate process
    5. get/set process attributes
    6. wait for time, wait event, signal event
    7. allocate, free memory
  2. File management.
    1. create file, delete file
    2. open, close
    3. read, write, reposition
    4. get/set file attributes
  3. Device Management.
    1. request device, release device
    2. read, write, reposition
    3. get/set device attributes
    4. logically attach or detach devices
  4. Information Maintenance.
    1. get/set time or date
    2. get/set system data
    3. get/set process, file, or device attributes
  5. Communication.
    1. create, delete communication connection
    2. send, receive messages
    3. transfer status information
    4. attach or detach remote devices
Processor mode and context switching
•       A syscall is processed in kernel mode, which is accomplished by changing the processor execution mode to a more privileged one, but no process context switch is necessary.
•       The hardware sees the world in terms of the execution mode according to the processor status register, and processes are an abstraction provided by the operating system.
•       A syscall does not require a context switch to another process, it is processed in the context of whichever process invoked it. 

System calls

•       All operating systems provides service points through which programs request services from the kernel. This service request points directly into the kernel called “system calls”.

•       The system provides a library APIs that sits between normal programs and the OS, usually an implementation of C library (libc) such as glibc, it provides wrapper functions for the system calls.

•       System call implementation requires a control transfer which involves some sort of architecture specific feature.

•       A typical way to implement this is to use a software interrupt or trap. Interrupts transfer control to the operating system kernel so software simply needs to set up some register with the system call number needed, and execute the software interrupt.


•       The system call functions may put one or more of the C arguments into the general registers and then execute some machine instruction that generates a software interrupt in the kernel.

Library functions
•       Library functions are the general purpose functions defined in Section of “Unix programmers Manual”

•       These functions are not entry points into the kernel although they may invoke one or more of the kernel functions.

•       For example, ‘printf’ may invoke the ‘write’ system call to perform the output but ‘strcpy’ and ‘atoi’ doesn’t invoke the kernel service at all.

Difference between system call and library function
•       System call invokes kernel service, library functions may or may be invoke kernel services.

•       For eg. Some OS provides separate system call to return the ‘time’ another for ‘data’. Linux uses a single system call that returns the number of seconds since the Epoch: January 1, 1970, coordinated universal time. Converting this value into human readable time and date using local time zone is left to the user process. Routines are provided in the standard C library to handle most cases.

•       System calls usually provides minimal interface while library functions often provide more elaborate functionality.

•       For e.g. fork()/exec() are the system call to create and execute process where as system/popen are the libray functions with similar simplified functionality.

Interfacing functions between user space and kernel space

•       The kernel offers several subroutines or functions in user space, which allow the end-user application programmer to interact with the hardware. Usually, in UNIX or Linux systems, this dialogue is performed through functions or subroutines in order to read and write files. The reason for this is that in Unix/Linux devices are seen, from the point of view of the user, as files.

•       On the other hand, in kernel space Linux also offers several functions or subroutines to perform the low level interactions directly with the hardware, and allow the transfer of information from kernel to user space.

•       Usually, for each function in user space (allowing the use of devices or files), there exists an equivalent in kernel space (allowing the transfer of information from the kernel to the user and vice-versa).

User space and kernel space

•       Kernel space. Linux kernel manages the machine's hardware in a simple and efficient manner, offering the user a simple and uniform programming interface. In the same way, the kernel, and in particular its device drivers, form a bridge or interface between the end-user/programmer and the hardware. Any subroutines or functions forming part of the kernel (modules and device drivers, for example) are considered to be part of kernel space.

•       User space. End-user programs/applications, like the UNIX shell or other GUI based applications (open office for example), are part of the user space. Obviously, these applications need to interact with the system's hardware. However, they don’t do so directly, but through the kernel supported functions called system calls.


•       When you write device drivers, it’s important to make the distinction between “user space” and “kernel space”.