In the posts "
Creating a linked list in linux kernel" and "
Using list_del to delete a node" saw how to use the in built functions of a kernel to build a linked list. One of the main applications of linked list could
be the implementation of stack. In this post, let us make use of the list functions available in the kernel to implement a stack.
A stack, as we know, is a data structure that stores elements in the order of first in last out. So when ever we write or push data into the stack, the data needs to be added as a node at the end of the list and when we read data from the stack we need to read data from the last node of the list and then delete the corresponding node. To implement a stack using a list, we need to keep track of the last element that gets inserted into the list, because when ever we need to read from the stack we need to return the data stored in the last node.
To implement the stack operation using list functions, we will make use of the function
Where
new: The new node to be added to the list
head: Is the node after which the new node has to be added.
If we have to implement a linked list from scratch we would have had to take care of all the pointer manipulations when inserting data into the list or removing data from the list. But as we have the required help, as built in functions, all we need to do is call the right function with the right arguments.
To show the operation of a stack we will make use of a proc entry. We will create proc entry named as "stack" and make it behave as a stack.
We will use the following structure as the node
test_list: Will be used to create the linked list using the built in functions of kernel
data: To hold the data of the list.
The first thing we will have to do, to implement a stack using proc entry is create a linked list and create a proc entry in the init function.
create_new_proc_entry : Function to create a new proc entry.
INIT_LIST_HEAD: Initialize the linked list with the head node as test_head.
tail=&test_head; : With no data in the stack the tail and the head point to the same node.
emp_len=strlen(empty): "empty" is the message to the printed when the stack is empty and strlen returns the length of the string.
Once the initialization is done we need to associate the functions for push, writing data to stack and pop, reading data from stack.
Now we need to implement the functions pop_stack and push and stack.
push_stack:
msg=kmalloc(10*sizeof(char),GFP_KERNEL): allocate memory for the data to be received from the user space.
temp=copy_from_user(msg,buf,count): copy the data from user space into the memory allocated
node=kmalloc(sizeof(struct k_list *),GFP_KERNEL): Allocate a new node.
node->data=msg: Assign the data received to the node.
list_add(&node->test_list,tail): Add the new node to the list. Note that we are passing the node after which the new node has to be added as tail.
This makes sure that the list behaves as a stack.
cnt++: Increase the count of nodes that are present in the list.
tail=&(node->test_list): Change the node to point to the new node of the list. Note that we are assigning the address of test_list to tail and not
the address of the node.
pop_stack:
In the pop operation we pop the last element inserted into the stack and then delete the node. According to the push_stack function, the variable tail points to the last element inserted into the stack.
According the standard practice followed in the the kernel, the read operation has to return the number of bytes of data transfered to the user space and user space functions continue to read as long as they do not get a 0 as return value indicating end of the read operation.
To make the proc entry work as a stack, we need to make sure that every read operation return only one node. To ensure this, in the above code we have used a variables new_node and flag, which after returning the data of one node or empty stack, respectively, makes the return value to 0, which ensures the read is terminated.
While popping data out of stack, we need to first check if the stack has data in it. This is done by comparing tail with test_head. If test is pointing to test_head it means the stack is empty and we return the string "Stack Empty". This is implemented by following piece of code
On the other hand if stack has data in it.
node=container_of(tail,struct k_list ,test_list): Get the pointer to the node which has the tail, last node of the list, as its member.
msg=node->data : Assign the data in the node to msg
ret=strlen(msg): Get the length of data to be transfered.
new_node=0: set new_node to 0, to indicate one node has been read.
Set count value to sting length, if count is larger than string length and reduce the variable ret by count value.
ret become 0 when ever the count valur is greater than ret.
temp=copy_to_user(buf,msg, count): Copy msg to user space.
Once all the data from the node has been transferred count becomes 0, not we need to change the tail to point to the node previous to the current node.
tail=((node->test_list).prev): Assign to tail the address of test_list of previous node.
list_del(&node->test_list): delete the node from the list.
new_node=1: Set new_node to 1 for the read of next node.
The full code for the kernel module looks as below.
stack_list.c
To compile the module use the make file.
Compile and insert the module into the kernel
To see the output
Thus we have written the 1,2,3,4 as data into 4 nodes with 4 being inserted last. Now to pop data out of the stack
Thus we can see that the proc entry is operating as a stack.
Related posts
Implementing stack using the list functions in kernel-2
Creating a read write proc entry in kernel versions above 3.10
Pointer to structure from its member pointer: container_of