Search

Kernel Thread Creation -2

 In the post kernel thread cration -1  the basics of thread creation in the kernel was introduced. In continuation to the same, let us look at another function that will help us create threads in fewer lines than given that post.

To create and run a thread we had used the following two functions

kthread_create   to create the thread
wake_up_process to run the thread.


Instead of the two functions we can achieve the same using just one function

kthread_run( "function name" , "data for the function" , "name of the thread" )

So instead of calling the two functions we can just call the kthread_run to create as well as run the thread. this useful when you want the thread to start executing immediately.

The modified init function will look as follows, the rest of the code remains the same as given in the post  kthread creation -1


########################### init function ##########
int thread_init (void) {
   
    char name[8]="thread1";
    printk(KERN_INFO "in init");
    thread1 = kthread_run(thread_fn,NULL,name);

    return 0;
}

###########################################

The above init function is obviously much shorter than the previous one thus helping in reducing the code size.



A look into the inodes

Every file that gets created  in the system also has with it various other data associated with it other than the file contents itself. For ex
The modification time
The owner of the file
Permissions : Who is allowed to read/write from the file etc

These information about a file is called the matadata of the file, i.e. it is data about the data in the file.

All the meta data of a file has to be stored somewhere so that the users can access it as and when required . In Linux the inodes are used for this.

Hence Inode is nothing but the data structure that holds the meta data of a file. Every file that gets created in the system gets an inode number assigned to it which is nothing but the address of the first block of the file. The inode in Linux stores the following information of a file

 The type of file type (executable/text etc)
 Read/Write Permissions
 Owner of the file (uid)
 Group to which owner belongs to (gid)
 The size of the file
 Time:  Last time when the contents of the file were modified (written to,mtime)
        Last time when the file was used (Read or executed, atime)
        Last time when the inode itself was modified i.e. changing permissions etc. (ctime)
   
 The number of soft/hard links to the file
 Extended attributes like only root is allowed to delete the file  etc



Note that the inode does not hold the name of the file. The interesting point is the system does not look at the files by their names but by their inode numbers.
Every directory maintains a table of file names and their corresponding inode numbers, so whenever a file is accessed, the inode number is looked up  from this table and the requested operation is done on the file.

To look at the inode numbers of the files you can use the command "ls" with the "-i" option.
For ex:

$ ls -i /etc/fstab

360456 /etc/fstab


The difference between a soft link and a hard link can be easily brought about using the inodes.

A softlink is just a pointer to the file and not the actual file itself, but a hardlink is the same file as the original file that it links to.

Let us create a soft link and hard link for the same file and then have a look at the inode numbers of all three

$ ln -s temp temp_soft  (Creating a softlink for temp)
$ ln temp temp_hard     (Creating hardlink for temp)
$ ls -i temp*
  4195423 temp
  2065399 temp_soft
  4195423 temp_hard


If you observe the output, the inode numbers of the original file "temp" and the hard link "temp_hard" are the same. Indicating that both the file names are actually pointing to the same location in the memory. On the other hand the file "temp_soft" has a different inode number hence indicating that it is a different file which points to the original file "temp".

The number of inodes allowed in a system can be fixed a few file systems, the maximum number is arrived at  by assuming a minimum size for each file that gets created , for e.g every file might be assumed to have a minimum size of 2 KB and a system that has 1 MB of memory will be allowed a maximum of 500 inodes. The modern systems though allow the inode numbers to increase dynamically.






Kernel Thread Creation : 1

Kernel Thread Creation : 1

The Linux way of handling threads is unique when compared to the traditional approach. Generally a thread and a process are treated in different ways, but in case of linux thread is also just another process and is handled in the same fashion as any other process. Let us take write a small module to create a kernel thread and try to understand the various functions involved in the same.

To work with threads you would need the header file linux/kthread.h A thread is created by the call to the function



The function takes the following arguments

function: The function that the thread has to execute

data : The "data" to be passed to the function

name: The name by which the process will be recognised in the kernel.

A thread created using the above call does not run but only gets created. To run the thread we need to call the function "wake_up_process" passing the thread id that is returned by "kthread_create".

When the wake_up_process is called the function passed to kthread_create gets executed.

To stop a thread that is running we need to call the kthread_stop(struct task_struct *threadid) where threadid is the same that is returned by the kthread_create call.

To understand the working of these system calls better let us create a module and create a kernel thread in it.

We need the following header files



Let us spawn a kernel thread as soon as the module gets inserted into the kernel, so we will have to add the kernel creation part in the init function.



In the call to kthread_create we have passed the following arguments

thread_fn : Which is the function that will be run as the thread.

NULL: As we are not passing any data to the function we have kept this NULL.

name: The process will be named "thread1" in the list of processes .

Now we need to implement the function "thread_fn" . The following functions prints a statement and waits for one minute before exiting. (If the syntax of waiting and jiffies is not clear right now, we will cover that soon ) .

The wait is just to give us enough time to look at the thread running.



To stop thread we can use the system call kthread_stop(struct task_struct *threadid). Note that in case the thread does not exist this call would end up in segmentation fault. The function kthread_stop does not terminate the thread but waits for the thread to terminate itself.

In our clean up function we will call kthread_stop and let the thread exit itself.



Putting all the above code together the complete code is as follows



The makefile need to compile the code is, assuming the above code is saved as threads.c



Now run the following commands



If this happens with out any errors, run the following command to see the thread running.

The timer in previous to last column shows from how long the thread has been running.

To remove the module run the command. But make sure you wait for a whole minute before running the command, because te



If you try to remove thread before a minute it will keep waiting until the thread exits which happens only at the end of a minute.

Creating a Read/Write proc entry

In the post "creating proc read entry" we saw how to create a proc entry that can only be read and not written to.

The proc entries can also be created such that we can write data into the proc entry, the data in turn can be used for configuring modules in the kernel. For eg the default level of logging of the printk function call is defined by the value of CONSOLE_LOG_LEVEL. All the messages that have the priority more than this will be displayed on the console (While working in the text mode, not in the graphics mode ). The value of this can be found out by "cat /proc/sys/kernel/printk".
For eg the output may be



The first number in the output is the default CONSOLE_LOGL_EVEL, in this case all the printk messages which have loglevel higher than 4 (3,2,1) are displayed on the screen.
To change this loglevel we can just write the required log level into the proc entry are follows



This will change the CONSOLE_LOG_LEVEL from 6 to 7.

Thus we can see that creating write_proc entries can be very useful in configuring the functioning of the kernel as well while debugging a module.

In this post we will see how we can create a proc entry which can be read from as well as written to.
To create a write proc entry we will have to make use of the func tion "create_proc_entry"




Where the arguments are

name: The name of the entry that we want to create.
mode : The permissions for the entry created.
parent: In case the entry needs to be created in a sub directory under /proc, we will have to give the full path. 

In our example we will use



Thus "hello" is the name of the entry, by passing 0666 we are giving read and write permission to every one,
And we are creating the entry straight under /proc hence the third argument is NULL.

This returns a pointer to a structure of the kind proc_dir_entry. You can look into the contents of the structure in the file linux/proc_fs.h

The structure has various fields and we need to initialize only the ones we feel is relevant for our module.

In our example we want to implement read and write functions of proc hence we will have to initialize the read_proc and write_proc in the structure to point to the respective read and write functions.

Thus the initializations will be



Thus we have to implement a function by the name read_proc that will be called when the proc entry is read and a function by the name write_proc that will be called when the proc entry is written to.

We will use an array by the name proc_data to store the data that will written into the proc entry. The read function thus will output the contents of the proc_data array whenever the proc entry is read as shown below.



The write function will have to written to accept data from the user and put it into the array proc_data as shown below.



Before writing into the proc entry we make sure that amount of write does not exceed the size of the array.
In case the amount of write from user space is more than the size of the array then we truncate the data to the maximum size array

The full code of the module is as below.
proc_write_read.c :



Makefile required to compile the above module is as below. Malefile:



To compile the module run the following commands as super user




If there are no errors run the following command

After inserting successfully, write some data into the proc entry as shown below.



Now read the proc entry and see if the same data written above is shown or not.




What ever you write into the proc etnry, you should be able to read it back using the cat command.

Reference Books