Change Linux Kernel Timer

Lowering linux kernel timer frequency

Don't worry! Your confusion is nothing but expected. Linux timer interrupts are very confusing and have had a long and quite exciting history.

CLK_TCK

Linux has no sysconf system call and glibc is just returning the constant value 100. Sorry.

HZ <-- what you probably want

When configuring your kernel you can choose a timer frequency of either 100Hz, 250Hz, 300Hz or 1000Hz. All of these are supported, and although 1000Hz is the default it's not always the best.

People will generally choose a high value when they value latency (a desktop or a webserver) and a low value when they value throughput (HPC).

CONFIG_HIGH_RES_TIMERS

This has nothing to do with timer interrupts, it's just a mechanism that allows you to have higher resolution timers. This basically means that timeouts on calls like select can be more accurate than 1/HZ seconds.

divider

This command line option is a patch provided by Red Hat. You can probably use this (if you're using Red Hat or CentOS), but I'd be careful. It's caused lots of bugs and you should probably just recompile with a different Hz value.

CONFIG_NO_HZ

This really doesn't do much, it's for power saving and it means that the ticks will stop (or at least become less frequent) when nothing is executing. This is probably already enabled on your kernel. It doesn't make any difference when at least one task is runnable.

Frederic Weisbecker actually has a patch pending which generalizes this to cases where only a single task is running, but it's a little way off yet.

Change linux kernel timer

Don't edit .config directly, unless you're a Kbuild expert (and if you're asking this, you're not a Kbuild expert). Instead run make menuconfig or make xconfig to load the menu-based configuration system. Alternately, make config will do a line-based configuration process (where it asks you several hundred questions about what to configure - not recommended). The relevant option is under "Processor type and features" as "Timer frequency".

That said, this may not be necessary. Modern Linux can use high-resolution event timers (CONFIG_HIGH_RES_TIMERS) to acheive low-latency timers even without increasing the timer frequency. With a tickless system (CONFIG_NO_HZ) , the timer frequency has little effect at all.

On the other hand, I'm not sure what timer support Geode CPUs have. You may want to run cyclictest with various kernel configurations to see what you need to get low latency performance. The test you ran tests maximum dispatch frequency, not dispatch latency, so comparing with cyclictest results would be interesting. If you need really low latency, the CONFIG_PREEMPT_RT patchset may also be of interest.

How to change kernel timer frequency?

Changes you make to /boot/config do not affect the running kernel. Please read more about the kernel config file here.

The config file you see in /boot/config (actually, it's more like config-[kernel_version]) is the config file that was USED to build the kernel. This means that every change you make to this config file does not affect anything.

To really make these changes you need to construct a new config file, with the modifications you require and compile and install a new kernel based on that config file. You can use the config file from /boot and just make the clock adjustments to fit.

How to change kernel Timer frequency

The timer frequency is fixed in Linux (unless you build a tickless kernel - CONFIG_NO_HZ=y - but the upper limit will still be fixed). You cannot change it at runtime or at boot time. You can only change it at compile time.

So the answer is: no. You need to rebuild the kernel when you want to change it.

Change kernel time frequency

It's easy enough to download sources. You can actually, on ubuntu, run:

apt-get source linux-image-amd64-4.4.0-83-generic
apt-get build-dep linux-image-amd64-4.4.0-83-generic

The first command will download the source for that kernel (to the current directory); the second will install all packages needed to build it. When that's done, enter the linux-image folder, and

zcat /proc/config.gz > .config

This will get the currently-running kernel config into this build area. Finally, make menuconfig

will open a text-based menu system. Press / to search, and find HZ. Browse to the setting, press <Enter> to open it, and select 1000. Exit out, saving the config, then make bzImage modules -j9 to build the kernel (replacing 9 with one more than your CPU core/thread count for speed).

make install will put the kernel and modules into /boot and /lib/modules/kernel_name_and_version respectively.

Linux kernel development

Try to use kernel timer API:

https://www.ibm.com/developerworks/library/l-timers-list/

The simplest method is a call to setup_timer, which initializes the timer and sets the user-provided callback function and context. Otherwise, the user can set these values (function and data) in the timer and simply call init_timer. Note that init_timer is called internally by setup_timer"

void init_timer( struct timer_list *timer );
void setup_timer( struct timer_list *timer,
void (*function)(unsigned long), unsigned long data );

How to modify kernel timer_list timeout

Kernel has no mechanism for detect changes in variables. Instead, you should perform corresponded actions before/after your code changes your variable.

When you add sysctl entry, you also set handler for it(ctl_table->proc_handler). This handler defines actions, which are executed when read/write method for entry is called. Standard proc_do* functions only set/get value of variable, so you should define your handler. Something like this:

int my_handler(struct ctl_table *table, int write,
void __user *buffer, size_t *lenp, loff_t *ppos)
{
// Call standard helper..
int res = proc_dointvec(table, write, buffer, lenp, ppos);
if(write && !res) {
// Additional actions on successfull write.
}
return res;
}

Modification of the timer's timeout can be performed using mod_timer function.

Write a robust timer in linux

But google told me that we can only set one handler per signal and I dont know if the other processus that are in my linux board use SIG_RTMIN.

No, it is one handler per signal per process.

That is, having a signal handler for SIGRTMIN in your own program will not interfere with SIGRTMIN handlers of any other processes. Similarly, creating a timer will not affect any other processes' timers either. All you need to worry about, is your own process.

(Technically, there are only a limited number of timers available, so you don't want to create hundreds of them in a single process.)

If you have only one thread in the process, consider the following timeout scheme:

// SPDX-License-Identifier: CC0-1.0

#define _POSIX_C_SOURCE 200809L
#include <stdlib.h>
#include <signal.h>
#include <string.h>
#include <time.h>
#include <errno.h>
#include <stdio.h>

#define TIMEOUT_SIGNAL (SIGRTMIN+0)
#define TIMEOUT_REPEAT_NS 1000000 /* Repeat every millisecond until canceled */

static volatile sig_atomic_t timeout_elapsed; /* Nonzero if timeout has elapsed */
static timer_t timeout_timer;

static void timeout_handler(int signum)
{
(void)signum; /* Silences warning about unused parameter; generates no code. */
timeout_elapsed = 1;
}

static int timeout_init(void)
{
struct sigaction act;
struct sigevent evt;

memset(&act, 0, sizeof act);
sigemptyset(&act.sa_mask);
act.sa_handler = timeout_handler;
act.sa_flags = 0;
if (sigaction(TIMEOUT_SIGNAL, &act, NULL) == -1)
return errno;

memset(&evt, 0, sizeof evt);
evt.sigev_notify = SIGEV_SIGNAL;
evt.sigev_signo = TIMEOUT_SIGNAL;
evt.sigev_value.sival_ptr = (void *)0;
if (timer_create(CLOCK_BOOTTIME, &evt, &timeout_timer) == -1)
return errno;

timeout_elapsed = 0;

return 0;
}

static void timeout_cancel(void)
{
struct itimerspec zero;

zero.it_value.tv_sec = 0;
zero.it_value.tv_nsec = 0;
zero.it_interval.tv_sec = 0;
zero.it_interval.tv_nsec = 0;

timer_settime(timeout_timer, 0, &zero, NULL);
}

static void timeout_set(double seconds)
{
struct itimerspec when;
sigset_t mask;

/* Block the timeout signal for now. */
sigemptyset(&mask);
sigaddset(&mask, TIMEOUT_SIGNAL);
sigprocmask(SIG_BLOCK, &mask, NULL);

/* Make sure any previous timeouts have been canceled. */
timeout_cancel();

/* Calculate the next (relative) timeout. */
if (seconds >= 0.000000001) {
long sec = (long)seconds;
long nsec = (long)(1000000000.0*(seconds - (double)sec));

if (nsec < 0)
nsec = 0;

if (nsec > 999999999) {
nsec = 0;
sec++;
}

when.it_value.tv_sec = sec;
when.it_value.tv_nsec = nsec;
} else {
when.it_value.tv_sec = 0;
when.it_value.tv_nsec = 1;
}

/* Set it to repeat, so that it is not easily missed. */
when.it_interval.tv_sec = 0;
when.it_interval.tv_nsec = TIMEOUT_REPEAT_NS;

/* Update the timer. */
timer_settime(timeout_timer, 0, &when, NULL);

/* Clear the flag, and unblock the signal. */
timeout_elapsed = 0;
sigprocmask(SIG_UNBLOCK, &mask, NULL);
}

int main(void)
{
char *line_ptr = NULL;
size_t line_max = 0;
ssize_t line_len;

if (timeout_init()) {
fprintf(stderr, "Cannot set up timeouts: %s.\n", strerror(errno));
return EXIT_FAILURE;
}

timeout_set(5.0);
printf("Please type input lines. This will timeout in five seconds.\n");
fflush(stdout);

while (!timeout_elapsed) {
line_len = getline(&line_ptr, &line_max, stdin);
if (line_len > 0) {
/* Remove trailing newlines */
line_ptr[strcspn(line_ptr, "\r\n")] = '\0';
printf("Read %zd bytes: \"%s\".\n", line_len, line_ptr);
fflush(stdout);
}
}

timeout_cancel();

free(line_ptr);
line_ptr = NULL;
line_max = 0;

printf("Done.\n");
return EXIT_SUCCESS;
}

Compile using gcc -Wall -Wextra -O2 example1.c -lrt -o example1 and run ./example1.

For a multithreaded process, the signal must be delivered to a specific thread, almost always the thread that sets the timeout in the first place. Here, I recommend a different approach: use a helper thread, a list or an array or a binary min-heap of CLOCK_REALTIME absolute times of the respective timeouts, waiting in pthread_cond_timedwait() for the next soonest timeout, or for a signal on the condition variable indicating the timeout list/array/heap has been updated.



Related Topics



Leave a reply



Submit