I'm helping a friend of mine to implement a software in C++, which utilizes four processes that are supposed to run quasi-parallel on the single core.
Three of the four processes are created by utilizing the fork()
-function. These processes are synchronized by the three semaphores (semaOne
, semaTwo
and semaThree
) and run "a relay race" one after another in the endless loop (proc1 -> proc2 -> proc3 -> proc1 -> ...). The fourth process (is the father process, obviously) is the watchdog-process that executes it's own endless loop and observes the three child processes.
Consider children functioning as expected, semaphores and synchronization too.
The watchdog implementation is very primitive, but sufficient. Children register themselves with the watchdog with their PID and are now obliged to tick
the watchdog once in a while and, by doing that, to increase the counter. The watchdog itself, when it is allowed by the scheduler to run, checks
the children's entries and decrement the counter of each registered child. If any counter reaches the state of 0, the watchdog is supposed to take action.
The problem is, that sometimes when we start the software, the watchdog process, where in an endless loop it's check
-function is called, grabs the initiative and runs endlessly and seems to be ignored by the scheduler, so the children never run. After several attempts to start the software, we're lucky the children manage to start their "relay race" and then everything works fine.
As I recognize, the scheduler is working in the round-robin mode and is supposed to divide the CPU-resources among all processes, but this seems not to work properly.
Please, ask for additional information, if needed!
P.S. The environment is Ubuntu Linux 16.04 running in the Virtual Box
As you can see, I've already tried to decrease the priority of the father process in hope this could affect the scheduler and urge it to give a time slot for the children, but this never happen.
// main.cpp
// Shared memory allocation and instantiation of semaphores happen here
// This part of the code relies on a simple framework that was given
// by the lecturer. It works as expected and is of no concern in the scope
// of the actual problem.
CWatchdog myWD;
pthread_t proc1 = 0;
pthread_t proc2 = 0;
pthread_t proc3 = 0;
int main(void) {
pthread_t PID;
myWD.init();
PID = fork();
if (PID == 0) {
// proc1
proc1 = getpid();
myWD.register(proc1);
while (true) {
semaOne->take();
std::cout << "[PROC 1] here" << std::endl;
// per1form proc1 task here and TICK the watchdog
myWD.tick(proc1);
usleep(2000000);
semaTwo->give();
}
} else if (fork() == 0) {
// proc2
proc2 = getpid();
myWD.register(proc2);
while (true) {
semaTwo->take();
std::cout << "[PROC 2] here" << std::endl;
// perform proc2 task here and TICK the watchdog
myWD.tick(proc2);
usleep(2000000);
semaThree->give();
}
} else if (fork() == 0) {
// proc3
proc3 = getpid();
myWD.register(proc3);
while (true) {
semaThree->take();
std::cout << "[PROC 3] here" << std::endl;
// perform proc3 task here and TICK the watchdog
myWD.tick(proc3);
usleep(2000000);
semaOne->give();
}
} else {
pthread_t wdProcID = getpid();
int myPrio = 0;
myPrio = getpriority(PRIO_PROCESS, 0);
// 0 for current process!
std::cout << "[WD] PID, priority (old) " << wdProcID << ", " << myPrio << std::endl;
setpriority(PRIO_PROCESS, 0, -20);
while (true) {
std::cout << "[WD] here" << std::endl;
// perform watchdog task here: CHECK the children
myWD.check();
usleep(50000);
}
return 0;
}
}
What we want to achieve, is the following: the scheduler gives the children their time slots to run, even if the watchdog/father-process enters it's loop early at startup.
Thank you, 2785528, for the idea to check the scheduler type!
I have found the solution and now everything runs as expected.
The following instructions are applicable for my development system, Ubuntu 16.04, but also for other distributions.
In the Linux Shell, the scheduling policy of any process can be displayed by using the chrt
tool. The -p
parameter passes the PID of the process the scheduling policy of which you want to display.
The following output is for Firefox currently runnung on my system:
$ chrt -p 19580
pid 19580's current scheduling policy: SCHED_OTHER
pid 19580's current scheduling priority: 0
"SCHED_NORMAL / SCHED_OTHER
This is the default policy and for the average program with some interaction. Does preemption of other processes."
(from http://manpages.ubuntu.com/manpages/xenial/man8/schedtool.8.html)
The default scheduling policy is not Rount-Robin! So to run any software with Round-Robin scheduler, it has to be explicitly selcted. There is a tool called schedtool
on my Ubuntu 16.04 that lets you to select any scheduler available on your system.
The schedtool
, if isn't installed yet, can easily be installed:
$ sudo apt-get install schedtool
Now, by executing schedtool
without any parameters, the tool's help can be displayed. Here is an excerpt with the list of scheduling policies the tool can handle:
set scheduling policies:
-N for SCHED_NORMAL
-F -p PRIO for SCHED_FIFO only as root
-R -p PRIO for SCHED_RR only as root
-B for SCHED_BATCH
-I -p PRIO for SCHED_ISO
-D for SCHED_IDLEPRIO
Since I wanted Round-Robin for our software which I start from the Eclipse IDE, I've used the schedtool
to start Eclipse with the Round-Robin scheduler and set the processe's priority to 20:
$ sudo schedtool -R -p 20 -e eclipse
Now, all the processes that will be started from the IDE, will be started with the Round-Robin scheduler, too.