In the post "Creating an ioctl command in linux " we saw the basics of ioctl, and how we can create our own ioctl commands. The functions used to create ioctls have undergone a change from the kernel version 2.6.39,and the previous functions are no longer valid.
Before 2.6.39, the ioctl calls would lock the BKL (Big kernel lock) and then execute the required functions. This was unnecessary with the new fine grained locking available in the kernel. Thus to improve the implementation of ioctl, a new operation called unlocked_ioctl was introduced and all the existing ioctls were migrated to this new implementation.
Thus stating 2.6.39 the older implementation is no longer available.
The changes required in implementation of the ioctl are.
The function in the driver has changed from
The inode is no longer passed as an argument.
The fops uses the name unlocked_ioctl
The basic working of ioctl remains same.
Here is a code that implements the above mentioned changes if the kernel version is more than 2.6.39, else uses the older interface.
Here is a simple script we can use to find the mac address of the network card in our systems.
We will use the command ifconfig to find the mac address and in some distribution ifconfig is available only to the root hence we make use of "sudo" before ifconfig.
The following is a C program to find out the maximum length allowed for a host name in the system
We can make use of the function sysconf and pass the argument _SC_HOST_NAME_MAX
We need the header file unistd.h to use sysconf.
Save the program as get_max_hostname.c. Compile and execute the code.
The following is a C program to find out the number of processors configured on a system.
We can make use of the function sysconf and pass the argument _SC_NPROCESSORS_CONF.
We need the header file unistd.h to use sysconf.
Save the program as get_processors.c. Compile and execute the code.
Here are three ways in which we can get the path to the current working directory using a c program,
1. we can make use of the function "getcwd".
Header file required : unistd.h
The syntax of getcwd is
buf: Is the character pointer in which the path of current working directory is stored.
size: Is the maximum size of the path in bytes, if the size exceeds this then a NULL is returned
Here is a program that shows the usage of getcwd.
Save the program is get_cwd.c. Compile and execute it.
Header file required : unistd.h
The function returns the absolute path to the current working directory. The function itself mallocs for an array big enough to hold the current directory name, thus we don't need to do malloc in the program. This function returns the value of the environment variable PWD, thus if it is not set the function might fail to return the current value.
Here is a program which uses get_current_dir_name to get the current working directory.
Save the file as get_current_dir.c. Compile and execute it.
3. The third way of getting the current working directory is using the command "pwd" along with the function "system". The system function executes which ever command is passed to it and prints the result on the screen.
Thus we can execute the command system and pass the command "pwd" as the argument, the result of which will be the current working directory being printed on the screen.
The following program shows how the same can be implemented.
Save the file as get_system_pwd.c. Compile and execute the same.
Generating random numbers is common recruitment in number of scripts. bash by default gives a variable $RANDOM which when accessed gives a random number between 0 and 32767.
There might be situations where we might need random numbers between a range of smaller numbers or between zero and a smaller number than 32767. Here is a script which can be used to generate random numbers between any range of numbers.
It takes as input the lower limit and the upper limit of the random numbers required and then generates the required number of random numbers with in the range.
In the post we saw how we can use mutex to prevent simultaneous usage of the same resource by multiple threads.
But usage of mutexes have to be done very carefully to make sure that the threads don't end up in a dead lock.
A deadlock is a scenario when one thread is waiting for a resource which is held by the second thread, while the second thread is waiting for a resource held by the first thread. Thus causing both the threads to wait for each other infinitely.
To show how this situation might occur, in the following example we have used two mutexes,read_mutex and write_mutex. To gain access to the file both these mutexes have to be locked. In the code, the write thread locks write_mutex and then after a little delay tries to locks read_mutex.
The read thread on the other hand locks read_mutex first and then tries to lock write_mutex.
Thus by the time the write tries to access read_mutex, it has already been locked by read thread and by the time read thread tries to lock write_mutex, it has already been locked by the write thread. Thus both the threads end up waiting infinitely for each other to unlock the mutexes and process ends up in a deadlock.
Save the file as mutex_deadlock.c, and compile it by passing "-lpthread" flag.
There will be no output as both the threads endup in a deadlock state, to get out of the execution use "cntrl + c".
To avoid such simple deadlocks the easiest method is to make sure that all the threads access the locks in a specific order. In the above example if it is made compulsory that every thread should lock write_mutex first and then try to lock read_mutex, then the deadlock situation will not occur as only one thread will get access to write_mutex first and the same thread will be able to access read_mutex too.
The above code modified to avoid deadlock is shown below. Note that the order of acquiring lock by both the threads is the same, thus able to prevent deadlock.
Save the code as mutex_nodeadlock.c, compile it using "-lpthread" flag.
Thus we can successfully avoid a potential deadlock situation, by making use of the rules order of locks.
pthreads are used to split a process into multiple threads and execute them concurrently. The threads should
be independent of each other as far as possible, but at times the threads could be dependent on each other and they might be working on the same data.
Let us say we want to crate two threads,one to read from a file and the other to write into the same file. It is obvious that we can not read from a file unless we don't write into it, and at any given time the file should be allowed to be accessed by only one of the two threads. That is while it is being written it should not be allowed to be read and while it is being read, it should not be opened for write.
The following program implements the above mentioned two threads, but does not do any thing to synchronize the actions of the two threads.
Save the adove file as pthread_file.c. Compile it using "-lpthread" falg
We get a segmentation fault every time we execute the above program. This is because, the read thread tries to access the file which is being created by thread1 "write" and thus is still not available. When read thread accesses the file it still has not been created and thus we get a segmentation fault.
The part of the program in which multiple threads try to get access to the same resource, in this case the file, is termed as the critical section and ideally a critical section should be executed by only one thread at a any given time.
To prevent the access of the file by multiple threads at the same time we can make use of a mutex. Mutex stands for mutual exclution and as the name implies it keeps the threads mutually exclusive.
Access to the critical section is controlled by locking the mutex. That is whichever process locks the mutex first will be allowed to enter the critical section first and the other process can not enter the critical section unless the mutex is not unlocked by the first process.
Applying this to above example, both read and write threads will have to get a lock on a common mutex before getting access to the file. If we can make sure that the write thread always gets the access to the lock first then, the read thread will have to wait for the write thread to finish its operations on the file and unlock the mutex,before it can read it. Thus making sure that the segmentation fault never ocurrs.
To implement a mutex in pthreads we have to use a datatype of type pthread_mutex_t.
To lock the mutex we use the function pthread_mutex_lock(Address of mutex variable)
To unlock the mutex we use the functions pthread_mutex_unlock(address of mutex variable)
The implementation is shown in the code below.
save the code as pthread_mutex.c and compile by passing the "-lpthread" flag and execute it.
Every thread created using pthread_create gets assigned a uniue thread id, with which it is recognized.
Note: Refer to creation of threads using pthreads in the post " "
The parent gets to know the thread id after the pthread_create is executed sucessfully, but while executing the thread if we want to access the thread id we have to use the function pthread_self.
The function pthread_self does not take any arguments, and returns the thread id, which is of kind pthread_t.
In the following program we are creating a thread which executes the function "hello". In the function "hello" we have used pthread_self to find out the thread id and then printed the same. Note that pthread_t is an unsigned long data type and hence we have to use %u while printing using printf.
Save the file as self_pthread.c
Compile it using gcc by passing the "-lpthread" flag.
We can see that the thread_id printed by the main function, parent, and the thread are the same, indicating that
the value returned by pthread_self is the correct value.
The above error is a common error encountered by a number of people who begin writing programs using pthreads.
The POSIX libraries required for pthread functions are not linked by the compiler unless we don't ask or it to be linked. Thus we see the above error.
The simple workaround is to pass the option "-lp" to the compiler while compiling the code.
Here is a script that,when executed, will draw a christmas tree using the characters choosen by the user on the linux terminal.
The script will prompt the user to enter the character using which the tree has to be drawn. Then it will prompt the user to enter two characters that will be used to decorate the tree. The last input it will ask for is the character using which the base of the tree has to be drawn.
The following script will create a blank triangle of characters on the linux terminal, but unlike the script in "" the triangle will be empty, i.e. only the outline has the characters chosen by the user and inside is left blank.
In the post " " we saw how we can use the command factor to find whether a number is prime of not. We can extend the same script further to generate all the prime numbers in a given range of numbers.
To generate a range of numbers we can use the command "seq" . The usage of seq is shown in the post " ".
We can use seq in a for loop to iterate over one number at time.
Save the script as seq_for.sh, give it execute permissions and run the script.
Thus we can see that using seq along with for loop we can work on a range of numbers, one number at a time.
This script can be combined with the script in " " to generate all the prime numbers in a range of numbers.
We request the user to enter two numbers to indicate the lower and upper limits of the range of numbers between which the user wants to generate the prime numbers. The lower limit can not be 1 because the number 1 does not have any factors and the command "factor" does not return any thing for 1. Thus we need to make sure the user enters a number bigger than 1.
If the user enters 1, then we will prompt the user again to enter number greater than 1.
Then we pass these two numbers to seq in a for loop
In each iteration of the for loop we use the factor command to find out if a number is prime or not.
The full script looks as below.
Save the script as range_prime.sh,give it execute permissions and run it.
Thus we can see the script is able to generate a list of all the prime numbers between 2 and 20.
The command "factor" takes as input any integer number and returns the prime factors of the that number.
We can use this command to find wheter a given number is prime or not. If we pass a prime number as argument to the command factor, it returns the number itself as its prime factor. As shown above for the number 131.
We can pipe this output to grep,search for the number passed to factor in the output. If the output is comprised only of the input number then the given number is prime.
The following script does the same
Save the above script as prime.sh, and give it execute permissions
While doing the conditional execution in makefiles, using only if and ifneq allows us to check for one condition but to be able to check multiple conditions one after another, we can use either if or ifneq after the else to check for further conditions.
If the ifneq condition fails then the flow will go to the else and check the condition specified after the else. We can use as many else statements as we require.
Let us say we have three "c" programs, hello_1.c, hello_2.c and hello_3.c. We need to compile them depending on which user is running the make command. hello_1.c should be compiled if we are running make as root, hello_2.c if we are running make as "user1" and for any other user hello_3.c. This can be achieved by the following makefile.
In the above makefile, first variable "user" is compared with "root" then with "user1". If both the conditions turn out to be false then the statements after the final else gets executed, which sets the targets to hello_3.c as required.
If we run the above makefile as root then we will get the output
If we run it as "user1" we will get the output
If we run it as any other user we will get the output
Conditions can be added in makefiles using "if" conditions, to ensure the commands get executed only under certain conditions.
The syntax of if in a makefile is
Let us say we have two files hello1.c and hello2.c which we want to compile using makefile. But the condition for compiling these files is that if the user who is compiling is root then hello_1.c should be compiled,else for any other user hello_2.c should be compiled. We can use the if condition in makefile to achieve this.
This can be done using the following makefile
Above statement assigns the current user name to the variable "user" using the command "whoami".
Then we use the "if" condition and check the user name and assign the target based on the result of the condition check.
If we execute the make command as root we will get the following output
If we do the same as any other user other than root, we will get the following output
Prev: make is smart
We can use variables in makefile to prevent typing the same strings over and over and also make the file more easy to understand.
Let us say we have a makefile to compile three independent executables hello_1, hello_2 and hello_3
In the above makefile we use the string "cc -o" repeatedly. Instead of typing the whole string every time we can create a variable for it.
We can create a variable of any name by using the "=" sign. The left hand would be the name of the variable and the right hand side would be the value of the variable.
Any where in the file if we want to use the value of the variable we just have to use the name of the variable in the format $() and make will automatically replace the variable with its value while executing.
In the above file have created a variable CC and used it whenever we needed cc -o.
Note that variables are case sensitive thus "cc" "CC" and "Cc" are all different.
We can also use variables for prefixes of various strings. Like in the example above "hello" is like a prefix to a number of strings, thus we can use a variable for it.
Recursive expanded variables :
We can create recursive variables by using one variable while declaring another.
In the above example "targets" is a variable, which is a list of all the targets. The targets variable uses the variable "pf" , thus to find out what $(targets) refers to make will have to expand pf creating a recursion among variables any depth of such recursion is allowed.
The limitation of defining variables using "=" is that a variable can not refer to itself in recursion.
In the above example, the second definition of variable "targets" uses the "targets" itself in recursion. Such usage will result in an infinite loop, which make successfully detects and throws the error.
Simply expanded variables
This can be prevented by making use of simply expanded variables which are defined using ":=". The use of ":=" makes the expansion of all the variables in the assignment only once and stop, thus preventing the infinite loop.
Thus in the above example the second definition should be
Now make will not throw any error and will successfully compile the targets.
Using shell in declaration :
We can also execute shell commands while defining variables.
In the above example we executed the command "pwd" and assigned the result to the variable "opd" which we used while compiling to indicate the path where the compiled executable needs to be stored.
Till now to compile a c program we have passed the command to the makefile to compile a given c code. But make has built in intelligence and can work even without the command.
Let us say we have ta file
To compile this file using make we just need to create the makefile with the contents
make looks at the targets and if no dependencies and commands are mentioned in the makefile,it automatically looks for a file with the name .c i.e. in the above example hello.c and compiles it to create the executable.
If we want to make sure that the correct files are being compiled before actually generating the executable we can just print the commands that make is going to run by passing the option "--just-print".
In the above case the executable will not get generated, but it only prints the commands that will be executed.
We can also print the folders in which make is running the commands from using the option -w
The make command when executed with out any arguments always tries to create the first target.
But in one makefile we can specify as many independent targets as we want and then choose to create the specific targets .
Let us say we have two programs
The above two are independent programs which can be compiled on the command line using
The same can be done by creating a makefile with two targets "hello_1" and "hello_2".
Now if we run the command make
We can see that if use the command make with no arguments the first target i.e. hello_1 gets crated. Now if we want to create the second target we will have to pass the second target to the make command as an argument.
Thus we can specify any specific target we want to create as the argument to the command make.
If we want to option of creating all the independent targets with out specifying every target separately we add a target to the makefile and mention all the targets as the dependencies.
Now we can create hello_1 and hello_2 by running make with all as the target.
Thus we can use makefile with multiple targets and selectively create the required targets or create all of them together.
Makefiles are the files used by the utility make. make helps in compiling a source code into an executable or automate execution of commands listed in the makefile.
Let us say we have a file "hello.c"
The usual compilation of this in linux using gcc is
We can use a makefile to do the same compilation
The syntax of writing a makefile is
A makefile is comprised of
Target : The executable which has to be generated. In the above case the target is the executable hello.
Prerequisite: The source files required to generated the target. In the above example the source is hello.c
Command: If the prerequisite is available which command will convert it into the target ?
The command in the above example is "cc -o hello.c -o hello". The command always has to be preceded by a single tab space
Create a file by the name "Makefile" and enter the contents
Now open a terminal and cd to the folder where the makefile is stored.
Run the command
We can see that make has run the command and generated the executable just as we did from the command line.
You might get the following error if you have omitted the tab space before the command.
If you name the makefile wrongly You might get the error
The makefile can also be named with any other name we want but in that case we need to use the option "-f" with the command make.
If we want to name the above makefile as my_makefile then we need to use the command make as
After the makefile has generated the executable if we run the command make again we will get the message
This is a special property of the make utility. It will generated a new executable of the mentioned target only if the target does not exist or the source file has been modified after the target has been generated.
Next : Makefiles: Dependent targets
To find the amount of space occupied by any file or folder in the system we can use the command du.
Let us say we have a folder test which contains the files file1,file2,file3,temp
To find the size of the folder test
The first column Indicates the size of the folder and the second column gives the name of the folder. By default du also lists the sizes of only the sub folders and not of files.
In case we want to have a look at the size of the files too we can use the option "-a"
The size column if displayed with no suffix can be difficult to read for bigger sized files, to make the reading easier we can use the option "-h" which sill convert it into more human readable form
The suffix K indicating the size is in Kilo bytes. For Mega bytes it will be M for giga G etc.
If we just want the size of the folder and not its sub folders we can use the option "-s"
We can also pass file names to find the size of a file instead of a folder.
Thus we can easily find the space occupied by any file or folder on the system using the du command
To find the current runlevel of the system we can use the following commands
The output indicates that the current runlevel is "2" , followed by the date and the time. If there was any other runlevel used before it would be listed after "last=".
The second command that can be used is
The second number is the current runlevel. The first number is the previous runlevel.
readlink is a command that can be used on a softlink to find out the file to which it is actualy pointing to.
For example if we have a file "temp" which is a softlink, we can run readlink of temp as follows
This indicates that readlink is a softlink of the file temp. If we want the full path to the file "test" we can pass the option "-f"
Thus we can find the full path to the file to which the softlink is pointing to .
symbolic links are used in linux to create a link to a folder or a file which is located in a different location, so that the same file or folder can be accessed from both the locations.
Softlinks are created using the command
If we have a file "temp1" in the path
And if we want to create a link to this file in the path
We can do it as follows.
Now if we do a long listing of the new link that was created we can see that it is a soflink by the way it gets listed
The first letter, l, in the long listing indicates that the file is a softlink and the last column indicates the file to which it is a softlink
The new link file is just a pointer to the original file and not a new copy of it and hence it occupies just enough space required for a link, which is very very small, rather than for the whole file.
The file can be opened and modified from either of the locations, and the changes will be reflected in files at both the locations.
In grub2, when the menu is displayed it by default does not make any sound to alert the user about the display.
This behavior can be changed so that the grub creates a beep sound as soon as the grub menu gets displayed, so that the user, if he or she is not looking at the monitor, gets notified about the display.
Open the file
and go to the line
Uncomment the the line by removing the "#" in the beginning.
Save and quit the file.
Now run the command
Now reboot the system and we should be able to hear a beep when the grub menu gets displayed.
In c programming, we use structures to store variables of different data types in one kind of variable. Each variable in the structure is termed as a member of the structure. The memory allocation for these members is always sequential.
In the above structure the addresses allocated for members "i" and "ch" will be sequential in the memory.
Using this property of the structure, if we know the pointer to a member of the structure we can retrieve the pointer to the structure with the help offsetof function.
In the post "offset of" we saw how we can retrieve the offset of a member with in the structure.
As the size occupied by each data type is fixed once we know the offset of the member and its pointer, we can subtract the offset from the pointer to get a pointer to the structure.
For example in the above structure if the pointer to the "char ch" is 1000. The offset of function will return 4 for "ch" as it is after an integer in the structure.
Thus the pointer to the structure would be "1000-4 = 996".
The same is shown in the example code below.
We allocate memory for one structure,test1, of type "temp" and assign values to "i" and "ch".
Then we assign the pointer of ch to a char pointer.
Then we subtract the offset of "ch" from the pointer and assign the return value to a new pointer to a structure,test2, of type temp.
If the pointer assigned to test2 is the correct , we should be able to access the value of "i" using test2.
Thus we print the value of "i" using test2
Here is the full code
Compile and execute it.
Thus we can see that we were able to retrieve the pointer to the parent structure from the pointer to the member.
The same steps are used in the container_of function which is used in the linux kernel code.
By passing pointer to the member of the structure in ptr and the structure type as argument type and the name of the member of which ptr is the pointer to we can retrieve the pointer to the parent structure just as we did in the example above.
Example usage in kernel code :
By passing a pointer to the cdev structure,the structure type as "versatile_led" and the member of the structure as cdev we are able to get a pointer to the structure that contains the cdev and hence get access to other members of the structure.
The "offsetof" function in c is useful in determining the offset of members with in a structure or union.
It is defined in the header file "stddef.h".
The syntax is
Let say we have a structure foo
The structure "foo" has a integer member which generally would of 4 bytes on a 32 bit system and a chat member which would be one byte.
Thus the offset of the member "ch" in the structure "foo" would be the number of bytes occupied by "int" which is "4".
Here is a example code which uses "offsetof" to find the offset of ch in the structre foo.
Compile the code and execute it
Try changing adding new members to the structure foo and see how the result changes depending on the members added.
Try with the structure :
The offset is the size of the array which is 4 X 10 = 40.
The command "tr" can be used to translate/change or delete characters from the input.
By default tr takes two arguments, the first one being the set of characters to be searched for and the second argument being the set of characters to be replaced with . The default input is the standard input.
The command waits for user to enter a string and then hit the return key. In the above example on entering "xyx" the character x has been replaced by character y which was what was passed as arguments to the command.
We can also pass multiple characters as arguments and each characters will be considered separately and not as a string and each of the characters in argument1 will be replaced by the corresponding character in the argument2
If the string length of argument1 is greater than that of argument2 then the last character of argument2 is applied to all the characters of argument1 that are after the string length of argument2.
As we can see both y and z are replaced by b.
We can change this behavior by passing the option "-t" which will truncate the characters of first argument to the same as the length of argument2.
Thus we can see, "y" and "z" both are not replaced by character.
The command can be applied on files by using the input redirection.
Let say we have a file,temp, with following contents
We can see that all o have been replaced with O.
Instead of translating, we can delete characters too by passing the option "-d" .
Thus we can see that all the digits in the file have been deleted.
We can user "tr" to convert all the characters in a file to upper case too.