调度程序有时会在启动时忽略分叉进程

Scheduler sometimes ignores forked processes at start

本文关键字:分叉 进程 调度程序 启动      更新时间:2023-10-16

我正在帮助我的一个朋友用C++实现一个软件,该软件使用四个进程,这些进程应该在单核上准并行运行。

四个进程中的三个进程是通过使用fork()-函数创建的。这些进程由三个信号量(semaOnesemaTwosemaThree)同步,并在无尽循环中一个接一个地运行"接力赛"(proc1->proc2->proc3->proc1->…)。第四个进程(显然是父进程)是看门狗进程,它执行自己的无尽循环并观察三个子进程。考虑孩子们按预期运作,信号量和同步。

看门狗的实现非常原始,但已经足够了。孩子们用他们的PID向看门狗注册,现在有义务每隔一段时间tick看门狗,并通过这样做来增加计数器。当调度程序允许看门狗运行时,看门狗本身会checks子项,并递减每个注册子项的计数器。如果任何计数器达到0状态,则看门狗应该采取行动。

问题是,有时当我们启动软件时,看门狗进程(在一个无休止的循环中调用它的check-函数)会占据主动,无休止地运行,似乎被调度器忽略了,所以孩子们永远不会运行。经过几次启动软件的尝试,我们很幸运,孩子们成功地开始了他们的"接力赛",然后一切都很好。

正如我所认识到的,调度器在循环模式下工作,应该在所有进程之间分配CPU资源,但这似乎无法正常工作。

如果需要,请询问更多信息!

附言:环境是运行在虚拟盒子中的Ubuntu Linux 16.04

正如你所看到的,我已经尝试降低父亲进程的优先级,希望这会影响调度器,并敦促它为孩子们留出一个时间段,但这从未发生过。

// 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;
}
}

我们想要实现的是:即使看门狗/父亲进程在启动时很早就进入了循环,调度器也会给孩子们提供运行的时间段。

感谢您,2785528,提出检查调度程序类型的想法!

我已经找到了解决方案,现在一切都按预期运行。

以下说明适用于我的开发系统Ubuntu 16.04,也适用于其他发行版。

在Linux Shell中,使用chrt工具可以显示任何进程的调度策略。-p参数传递要显示其调度策略的进程的PID。

以下输出适用于当前在我的系统上运行的Firefox:

$ chrt -p 19580
pid 19580's current scheduling policy: SCHED_OTHER
pid 19580's current scheduling priority: 0

"SCHED_NORMAL / SCHED_OTHER这是默认策略,适用于具有一些交互的普通程序。对其他进程进行抢占。"(来自http://manpages.ubuntu.com/manpages/xenial/man8/schedtool.8.html)

默认的调度策略不是Rount-Robin!因此,要使用Round Robin调度程序运行任何软件,都必须显式地选择它。在我的Ubuntu 16.04上有一个名为schedtool的工具,可以让你选择系统上可用的任何调度程序。

schedtool,如果还没有安装,可以很容易地安装:

$ sudo apt-get install schedtool

现在,通过在没有任何参数的情况下执行schedtool,可以显示该工具的帮助。以下是该工具可以处理的调度策略列表的摘录:

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

由于我想要从Eclipse IDE启动的软件的Round Robin,所以我使用schedtool使用Round Robins调度器启动Eclipse,并将进程的优先级设置为20:

$ sudo schedtool -R -p 20 -e eclipse

现在,所有将从IDE启动的进程也将使用Round Robin调度器启动。