summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristian Göttsche <cgzones@googlemail.com>2023-01-10 19:40:04 +0100
committerBenBE <BenBE@geshi.org>2023-02-05 00:17:33 +0100
commitda494896914a327476ab7e0298619d742a6205d4 (patch)
tree700db7b14d148c492b1fb0295462fee2214239ed
parentf1da8cfa28cce46cc7a4ab1661be35e2173155ab (diff)
Add support for scheduling policies
Add a process column for scheduling policy to show the current scheduling policy of the process. Add a the ability to change the scheduling policy of a process via the key 'Y'. Currently implemented on Linux and FreeBSD only but should be portable, since sched_getscheduler(2) is part of POSIX.1-2001. Closes: #1161
-rw-r--r--Action.c52
-rw-r--r--Makefile.am2
-rw-r--r--Process.c12
-rw-r--r--Process.h5
-rw-r--r--Scheduling.c154
-rw-r--r--Scheduling.h49
-rw-r--r--configure.ac2
-rw-r--r--freebsd/FreeBSDProcess.c4
-rw-r--r--freebsd/FreeBSDProcessList.c6
-rw-r--r--linux/LinuxProcess.c4
-rw-r--r--linux/LinuxProcessList.c7
11 files changed, 297 insertions, 0 deletions
diff --git a/Action.c b/Action.c
index 81432f5d..5d33f662 100644
--- a/Action.c
+++ b/Action.c
@@ -29,6 +29,7 @@ in the source distribution for its full text.
#include "Process.h"
#include "ProcessLocksScreen.h"
#include "ProvideCurses.h"
+#include "Scheduling.h"
#include "ScreenManager.h"
#include "SignalsPanel.h"
#include "TraceScreen.h"
@@ -407,6 +408,51 @@ static Htop_Reaction actionSetAffinity(State* st) {
}
+#ifdef SCHEDULER_SUPPORT
+static Htop_Reaction actionSetSchedPolicy(State* st) {
+ if (Settings_isReadonly())
+ return HTOP_KEEP_FOLLOWING;
+
+ static int preSelectedPolicy = SCHEDULINGPANEL_INITSELECTEDPOLICY;
+ static int preSelectedPriority = SCHEDULINGPANEL_INITSELECTEDPRIORITY;
+
+ Panel* schedPanel = Scheduling_newPolicyPanel(preSelectedPolicy);
+
+ const ListItem* policy;
+ for(;;) {
+ policy = (const ListItem*) Action_pickFromVector(st, schedPanel, 18, true);
+
+ if (!policy || policy->key != -1)
+ break;
+
+ Scheduling_togglePolicyPanelResetOnFork(schedPanel);
+ }
+
+ if (policy) {
+ preSelectedPolicy = policy->key;
+
+ Panel* prioPanel = Scheduling_newPriorityPanel(policy->key, preSelectedPriority);
+ if (prioPanel) {
+ const ListItem* prio = (const ListItem*) Action_pickFromVector(st, prioPanel, 14, true);
+ if (prio)
+ preSelectedPriority = prio->key;
+
+ Panel_delete((Object*) prioPanel);
+ }
+
+ SchedulingArg v = { .policy = preSelectedPolicy, .priority = preSelectedPriority };
+
+ bool ok = MainPanel_foreachProcess(st->mainPanel, Scheduling_setPolicy, (Arg) { .v = &v }, NULL);
+ if (!ok)
+ beep();
+ }
+
+ Panel_delete((Object*)schedPanel);
+
+ return HTOP_REFRESH | HTOP_REDRAW_BAR | HTOP_KEEP_FOLLOWING;
+}
+#endif /* SCHEDULER_SUPPORT */
+
static Htop_Reaction actionKill(State* st) {
if (Settings_isReadonly())
return HTOP_OK;
@@ -571,6 +617,9 @@ static const struct {
{ .key = " x: ", .roInactive = false, .info = "list file locks of process" },
{ .key = " s: ", .roInactive = true, .info = "trace syscalls with strace" },
{ .key = " w: ", .roInactive = false, .info = "wrap process command in multiple lines" },
+#ifdef SCHEDULER_SUPPORT
+ { .key = " Y: ", .roInactive = true, .info = "set scheduling policy" },
+#endif
{ .key = " F2 C S: ", .roInactive = false, .info = "setup" },
{ .key = " F1 h ?: ", .roInactive = false, .info = "show this help screen" },
{ .key = " F10 q: ", .roInactive = false, .info = "quit" },
@@ -779,6 +828,9 @@ void Action_setBindings(Htop_Action* keys) {
keys['S'] = actionSetup;
keys['T'] = actionSortByTime;
keys['U'] = actionUntagAll;
+#ifdef SCHEDULER_SUPPORT
+ keys['Y'] = actionSetSchedPolicy;
+#endif
keys['Z'] = actionTogglePauseProcessUpdate;
keys['['] = actionLowerPriority;
keys['\014'] = actionRedraw; // Ctrl+L
diff --git a/Makefile.am b/Makefile.am
index 8af1864a..1c685e4d 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -73,6 +73,7 @@ myhtopsources = \
ProcessList.c \
ProcessLocksScreen.c \
RichString.c \
+ Scheduling.c \
ScreenManager.c \
ScreensPanel.c \
Settings.c \
@@ -135,6 +136,7 @@ myhtopheaders = \
ProcessLocksScreen.h \
ProvideCurses.h \
RichString.h \
+ Scheduling.h \
ScreenManager.h \
ScreensPanel.h \
Settings.h \
diff --git a/Process.c b/Process.c
index fcaa3d54..614369b4 100644
--- a/Process.c
+++ b/Process.c
@@ -28,6 +28,7 @@ in the source distribution for its full text.
#include "ProcessList.h"
#include "DynamicColumn.h"
#include "RichString.h"
+#include "Scheduling.h"
#include "Settings.h"
#include "XUtils.h"
@@ -960,6 +961,15 @@ void Process_writeField(const Process* this, RichString* str, ProcessField field
xSnprintf(buffer, n, "%3ld ", this->priority);
break;
case PROCESSOR: xSnprintf(buffer, n, "%3d ", Settings_cpuId(this->settings, this->processor)); break;
+ case SCHEDULERPOLICY: {
+ const char* schedPolStr = "N/A";
+#ifdef SCHEDULER_SUPPORT
+ if (this->scheduling_policy >= 0)
+ schedPolStr = Scheduling_formatPolicy(this->scheduling_policy);
+#endif
+ xSnprintf(buffer, n, "%-5s ", schedPolStr);
+ break;
+ }
case SESSION: xSnprintf(buffer, n, "%*d ", Process_pidDigits, this->session); break;
case STARTTIME: xSnprintf(buffer, n, "%s", this->starttime_show); break;
case STATE:
@@ -1203,6 +1213,8 @@ int Process_compareByKey_Base(const Process* p1, const Process* p2, ProcessField
return SPACESHIP_NUMBER(p1->priority, p2->priority);
case PROCESSOR:
return SPACESHIP_NUMBER(p1->processor, p2->processor);
+ case SCHEDULERPOLICY:
+ return SPACESHIP_NUMBER(p1->scheduling_policy, p2->scheduling_policy);
case SESSION:
return SPACESHIP_NUMBER(p1->session, p2->session);
case STARTTIME:
diff --git a/Process.h b/Process.h
index eb79470d..0fdc392b 100644
--- a/Process.h
+++ b/Process.h
@@ -19,6 +19,7 @@ in the source distribution for its full text.
#define PROCESS_FLAG_IO 0x00000001
#define PROCESS_FLAG_CWD 0x00000002
+#define PROCESS_FLAG_SCHEDPOL 0x00000004
#define DEFAULT_HIGHLIGHT_SECS 5
@@ -49,6 +50,7 @@ typedef enum ProcessField_ {
TGID = 52,
PERCENT_NORM_CPU = 53,
ELAPSED = 54,
+ SCHEDULERPOLICY = 55,
PROC_COMM = 124,
PROC_EXE = 125,
CWD = 126,
@@ -221,6 +223,9 @@ typedef struct Process_ {
/* Process state enum field (platform dependent) */
ProcessState state;
+ /* Current scheduling policy */
+ int scheduling_policy;
+
/* Whether the process was updated during the current scan */
bool updated;
diff --git a/Scheduling.c b/Scheduling.c
new file mode 100644
index 00000000..5ca49ae2
--- /dev/null
+++ b/Scheduling.c
@@ -0,0 +1,154 @@
+/*
+htop - Scheduling.c
+(C) 2023 htop dev team
+Released under the GNU GPLv2+, see the COPYING file
+in the source distribution for its full text.
+*/
+
+#include "Scheduling.h"
+#include "EnvScreen.h"
+
+#ifdef SCHEDULER_SUPPORT
+
+#include <errno.h>
+
+#include "FunctionBar.h"
+#include "ListItem.h"
+#include "Macros.h"
+#include "Object.h"
+#include "Panel.h"
+#include "XUtils.h"
+
+
+static const SchedulingPolicy policies[] = {
+ [SCHED_OTHER] = { "Other", SCHED_OTHER, false },
+#ifdef SCHED_BATCH
+ [SCHED_BATCH] = { "Batch", SCHED_BATCH, false },
+#endif
+#ifdef SCHED_IDLE
+ [SCHED_IDLE] = { "Idle", SCHED_IDLE, false },
+#endif
+ [SCHED_FIFO] = { "FiFo", SCHED_FIFO, true },
+ [SCHED_RR] = { "RoundRobin", SCHED_RR, true },
+};
+
+#ifdef SCHED_RESET_ON_FORK
+static bool reset_on_fork = false;
+#endif
+
+
+Panel* Scheduling_newPolicyPanel(int preSelectedPolicy) {
+ Panel* this = Panel_new(0, 0, 0, 0, Class(ListItem), true, FunctionBar_newEnterEsc("Select ", "Cancel "));
+ Panel_setHeader(this, "New policy:");
+
+#ifdef SCHED_RESET_ON_FORK
+ Panel_add(this, (Object*) ListItem_new(reset_on_fork ? "Reset on fork: on" : "Reset on fork: off", -1));
+#endif
+
+ for (unsigned i = 0; i < ARRAYSIZE(policies); i++) {
+ if (!policies[i].name)
+ continue;
+
+ Panel_add(this, (Object*) ListItem_new(policies[i].name, policies[i].id));
+ if (policies[i].id == preSelectedPolicy)
+ Panel_setSelected(this, i);
+ }
+
+ return this;
+}
+
+void Scheduling_togglePolicyPanelResetOnFork(Panel* schedPanel) {
+#ifdef SCHED_RESET_ON_FORK
+ reset_on_fork = !reset_on_fork;
+
+ ListItem* item = (ListItem*) Panel_get(schedPanel, 0);
+
+ free_and_xStrdup(&item->value, reset_on_fork ? "Reset on fork: on" : "Reset on fork: off");
+#else
+ (void)schedPanel;
+#endif
+}
+
+Panel* Scheduling_newPriorityPanel(int policy, int preSelectedPriority) {
+ if (policy < 0 || (unsigned)policy >= ARRAYSIZE(policies) || policies[policy].name == NULL)
+ return NULL;
+
+ if (!policies[policy].prioritySupport)
+ return NULL;
+
+ int min = sched_get_priority_min(policy);
+ if (min < 0)
+ return NULL;
+ int max = sched_get_priority_max(policy);
+ if (max < 0 )
+ return NULL;
+
+ Panel* this = Panel_new(0, 0, 0, 0, Class(ListItem), true, FunctionBar_newEnterEsc("Select ", "Cancel "));
+ Panel_setHeader(this, "Priority:");
+
+ for (int i = min; i <= max; i++) {
+ char buf[16];
+ xSnprintf(buf, sizeof(buf), "%d", i);
+ Panel_add(this, (Object*) ListItem_new(buf, i));
+ if (i == preSelectedPriority)
+ Panel_setSelected(this, i);
+ }
+
+ return this;
+}
+
+bool Scheduling_setPolicy(Process* proc, Arg arg) {
+ const SchedulingArg* sarg = arg.v;
+ int policy = sarg->policy;
+
+ assert(policy >= 0);
+ assert((unsigned)policy < ARRAYSIZE(policies));
+ assert(policies[policy].name);
+
+ const struct sched_param param = { .sched_priority = policies[policy].prioritySupport ? sarg->priority : 0 };
+
+ #ifdef SCHED_RESET_ON_FORK
+ if (reset_on_fork)
+ policy &= SCHED_RESET_ON_FORK;
+ #endif
+
+ int r = sched_setscheduler(proc->pid, policy, &param);
+
+ /* POSIX says on success the previous scheduling policy should be returned,
+ * but Linux always returns 0. */
+ return r != -1;
+}
+
+const char* Scheduling_formatPolicy(int policy) {
+#ifdef SCHED_RESET_ON_FORK
+ policy = policy & ~SCHED_RESET_ON_FORK;
+#endif
+
+ switch (policy) {
+ case SCHED_OTHER:
+ return "OTHER";
+ case SCHED_FIFO:
+ return "FIFO";
+ case SCHED_RR:
+ return "RR";
+#ifdef SCHED_BATCH
+ case SCHED_BATCH:
+ return "BATCH";
+#endif
+#ifdef SCHED_IDLE
+ case SCHED_IDLE:
+ return "IDLE";
+#endif
+#ifdef SCHED_DEADLINE
+ case SCHED_DEADLINE:
+ return "EDF";
+#endif
+ default:
+ return "???";
+ }
+}
+
+void Scheduling_readProcessPolicy(Process* proc) {
+ proc->scheduling_policy = sched_getscheduler(proc->pid);
+}
+#endif /* SCHEDULER_SUPPORT */
diff --git a/Scheduling.h b/Scheduling.h
new file mode 100644
index 00000000..d91855ae
--- /dev/null
+++ b/Scheduling.h
@@ -0,0 +1,49 @@
+#ifndef HEADER_Scheduling
+#define HEADER_Scheduling
+/*
+htop - Scheduling.h
+(C) 2023 htop dev team
+Released under the GNU GPLv2+, see the COPYING file
+in the source distribution for its full text.
+*/
+
+#include "config.h" // IWYU pragma: keep
+
+#include <sched.h>
+#include <stdbool.h>
+
+#include "Panel.h"
+
+
+#if defined(HAVE_SCHED_SETSCHEDULER) && defined(HAVE_SCHED_GETSCHEDULER)
+#define SCHEDULER_SUPPORT
+
+typedef struct {
+ const char* name;
+ int id;
+ bool prioritySupport;
+} SchedulingPolicy;
+
+#define SCHEDULINGPANEL_INITSELECTEDPOLICY SCHED_OTHER
+#define SCHEDULINGPANEL_INITSELECTEDPRIORITY 50
+
+Panel* Scheduling_newPolicyPanel(int preSelectedPolicy);
+void Scheduling_togglePolicyPanelResetOnFork(Panel* schedPanel);
+
+Panel* Scheduling_newPriorityPanel(int policy, int preSelectedPriority);
+
+
+typedef struct {
+ int policy;
+ int priority;
+} SchedulingArg;
+
+bool Scheduling_setPolicy(Process* proc, Arg arg);
+
+const char* Scheduling_formatPolicy(int policy);
+
+void Scheduling_readProcessPolicy(Process* proc);
+
+#endif
+
+#endif /* HEADER_Scheduling */
diff --git a/configure.ac b/configure.ac
index de0718e1..73a8e91c 100644
--- a/configure.ac
+++ b/configure.ac
@@ -260,6 +260,8 @@ AC_CHECK_FUNCS([ \
memfd_create\
openat \
readlinkat \
+ sched_getscheduler \
+ sched_setscheduler \
])
if test "$my_htop_platform" = darwin; then
diff --git a/freebsd/FreeBSDProcess.c b/freebsd/FreeBSDProcess.c
index 4eccfe7f..90bc1f2c 100644
--- a/freebsd/FreeBSDProcess.c
+++ b/freebsd/FreeBSDProcess.c
@@ -13,6 +13,7 @@ in the source distribution for its full text.
#include "Macros.h"
#include "Process.h"
#include "RichString.h"
+#include "Scheduling.h"
#include "XUtils.h"
@@ -47,6 +48,9 @@ const ProcessFieldData Process_fields[LAST_PROCESSFIELD] = {
[PROC_COMM] = { .name = "COMM", .title = "COMM ", .description = "comm string of the process", .flags = 0, },
[PROC_EXE] = { .name = "EXE", .title = "EXE ", .description = "Basename of exe of the process", .flags = 0, },
[CWD] = { .name = "CWD", .title = "CWD ", .description = "The current working directory of the process", .flags = PROCESS_FLAG_CWD, },
+#ifdef SCHEDULER_SUPPORT
+ [SCHEDULERPOLICY] = { .name = "SCHEDULERPOLICY", .title = "SCHED ", .description = "Current scheduling policy of the process", .flags = PROCESS_FLAG_SCHEDPOL, },
+#endif
[JID] = { .name = "JID", .title = "JID", .description = "Jail prison ID", .flags = 0, .pidColumn = true, },
[JAIL] = { .name = "JAIL", .title = "JAIL ", .description = "Jail prison name", .flags = 0, },
[EMULATION] = { .name = "EMULATION", .title = "EMULATION ", .description = "System call emulation environment (ABI)", .flags = 0, },
diff --git a/freebsd/FreeBSDProcessList.c b/freebsd/FreeBSDProcessList.c
index 9ee5ea40..98447df9 100644
--- a/freebsd/FreeBSDProcessList.c
+++ b/freebsd/FreeBSDProcessList.c
@@ -34,6 +34,7 @@ in the source distribution for its full text.
#include "Object.h"
#include "Process.h"
#include "ProcessList.h"
+#include "Scheduling.h"
#include "Settings.h"
#include "XUtils.h"
#include "generic/openzfs_sysctl.h"
@@ -606,6 +607,11 @@ void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) {
if (Process_isKernelThread(proc))
super->kernelThreads++;
+#ifdef SCHEDULER_SUPPORT
+ if (settings->ss->flags & PROCESS_FLAG_SCHEDPOL)
+ Scheduling_readProcessPolicy(proc);
+#endif
+
proc->show = ! ((hideKernelThreads && Process_isKernelThread(proc)) || (hideUserlandThreads && Process_isUserlandThread(proc)));
super->totalTasks++;
diff --git a/linux/LinuxProcess.c b/linux/LinuxProcess.c
index 381b7cf5..8f9c3462 100644
--- a/linux/LinuxProcess.c
+++ b/linux/LinuxProcess.c
@@ -19,6 +19,7 @@ in the source distribution for its full text.
#include "Process.h"
#include "ProvideCurses.h"
#include "RichString.h"
+#include "Scheduling.h"
#include "XUtils.h"
#include "linux/IOPriority.h"
@@ -100,6 +101,9 @@ const ProcessFieldData Process_fields[LAST_PROCESSFIELD] = {
[CWD] = { .name = "CWD", .title = "CWD ", .description = "The current working directory of the process", .flags = PROCESS_FLAG_CWD, },
[AUTOGROUP_ID] = { .name = "AUTOGROUP_ID", .title = "AGRP", .description = "The autogroup identifier of the process", .flags = PROCESS_FLAG_LINUX_AUTOGROUP, },
[AUTOGROUP_NICE] = { .name = "AUTOGROUP_NICE", .title = " ANI", .description = "Nice value (the higher the value, the more other processes take priority) associated with the process autogroup", .flags = PROCESS_FLAG_LINUX_AUTOGROUP, },
+#ifdef SCHEDULER_SUPPORT
+ [SCHEDULERPOLICY] = { .name = "SCHEDULERPOLICY", .title = "SCHED ", .description = "Current scheduling policy of the process", .flags = PROCESS_FLAG_SCHEDPOL, },
+#endif
};
Process* LinuxProcess_new(const Settings* settings) {
diff --git a/linux/LinuxProcessList.c b/linux/LinuxProcessList.c
index eca9459b..8490d82c 100644
--- a/linux/LinuxProcessList.c
+++ b/linux/LinuxProcessList.c
@@ -44,6 +44,7 @@ in the source distribution for its full text.
#include "Macros.h"
#include "Object.h"
#include "Process.h"
+#include "Scheduling.h"
#include "Settings.h"
#include "XUtils.h"
#include "linux/CGroupUtils.h"
@@ -1721,6 +1722,12 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, openat_arg_
LinuxProcessList_readAutogroup(lp, procFd);
}
+ #ifdef SCHEDULER_SUPPORT
+ if (ss->flags & PROCESS_FLAG_SCHEDPOL) {
+ Scheduling_readProcessPolicy(proc);
+ }
+ #endif
+
if (!proc->cmdline && statCommand[0] &&
(proc->state == ZOMBIE || Process_isKernelThread(proc) || settings->showThreadNames)) {
Process_updateCmdline(proc, statCommand, 0, strlen(statCommand));

© 2014-2024 Faster IT GmbH | imprint | privacy policy