(1) Design an application with 3 threads, whose periods are 300ms, 500ms, and 800ms.
(2) The threads shall just "waste time", as we did in the exercise with threads.
(3) Design a simple driver with only open, close, write system calls.
(4) During its execution, every tasks
(i) opens the special file associated with the driver;
(ii) writes to the driver its own identifier plus open square brackets (i.e., [1, [2, or [3)
(iii) close the special files
(iv) performs operations (i.e., wasting time)
(v) performs (i)(ii) and (iii) again in order to write to the driver its own identifier, but with closed square brackets (i.e., 1], 2], or 3]).
(5) The write system call simply writes on the kernel log the string that was received from the thread. A typical output of the system, by reading the kernel log, may be the following [11][2[11]2][3[11]3] . This clearly shows that some threads can be preempted by other threads (if this does not happen, try to increase the computational time of longer tasks).
(6) Finally, modify the code of all tasks in order to use semaphores. Every thread now protects all its operations (i) to (v) with a semaphore, which basically prevents other tasks from preempting it. Specifically, use semaphores with priority ceiling.
-
you become superuser - root
sudo su
-
compile the driver with
make
which will produce a file simple.ko (have a look at the Makefile before doing that)
-
install the kernel module
insmod simple.ko
-
You can fine the major number of your driver. (for example : simple 509)
cat /proc/devices
-
make special file
mknod /dev/simple c <majornumber> 0
-
execute the main file
g++ task6.c (or task1_5.c) -pthread -o task6 (or task6.c) ./task6 (task1_5)
-
Design an application with 3 threads, whose periods are 300ms, 500ms, and 800ms.
-
Design a simple driver with only open, close, write system calls.
-
opens the special file associated with the driver
-
writes to the driver its own identifier plus open square brackets (i.e., [1, [2, or [3)
-
performs operations (i.e., wasting time)
-
performs (i)(ii) and (iii) again in order to write to the driver its own identifier, but with closed square brackets (i.e., 1], 2], or 3]).
-
You can check the kernel log or execute read.c.
But threads can by preempted by other threads like the following picture.
dmesg (or g++ read.c -o read.c & ./read)
![prempted](prempted.png) ![a](pr2.png)
-
-
Finally, modify the code "task1_5" in order to use semaphores. Every thread now protects all its operations (i) to (v) with a semaphore, which basically prevents other tasks from preempting it. Specifically, use semaphores with priority ceiling.
- It is a synchronization protocol for shared resources to prevent deadlocks due to priority reversal, which is the phenomena high-priority processes are kept waiting by low-priority processes.
- Each semaphore 𝑆_𝑘 is assigned a priority upper bound 𝐶(𝑆_𝑘) equal to the highest priority of the tasks that can lock it.
-
in order to use priority ceiling in POSIX, I use Priority Protect Protocol.
//initialize attribute pthread_mutexattr_init(&mutexattr_prioceiling); pthread_mutexattr_getprotocol(&mutexattr_prioceiling, &mutex_protocol); // set the protocol attribute of mutex created by the function pthread_mutexattr_init() //set the PTHREAD_PRIO_PROTECT pthread_mutexattr_setprotocol(&mutexattr_prioceiling, PTHREAD_PRIO_PROTECT); // set to a priority ceiling which is the highest priority among all the threads that may lock that mutex. pthread_mutexattr_setprioceiling(&mutexattr_prioceiling,priomax.sched_priority); //initialize mutex pthread_mutex_init(&task123_mutex, &mutexattr_prioceiling);