Utiliser un appel-système inconnu de la libC

Publié par cpb
Avr 05 2014

Lundi dernier, Linus a publié le nouveau noyau Linux 3.14. Ce dernier contient de nombreuses nouveautés, dont un mécanisme d’ordonnancement temps réel EDF (Earliest Deadline First) qu’il me tarde d’essayer. Pour cela deux nouveaux appels-système sched_getattr() et sched_setattr() sont proposés au programmeur, mais ils ne sont pas encore supportés par les bibliothèques C actuelles. Qu’à cela ne tienne, nous allons les invoquer directement…

Bien entendu, le contenu de cet article deviendra obsolète dans quelques semaines – ou quelques mois tout au plus – car le support de sched_getattr() et sched_setattr() sera rapidement inclus dans les mises à jours des bibliothèques C, mais le principe global restera valable pour de futurs appels-système.

syscallLes appels-système implémentés dans le noyau sont accessibles par l’intermédiaire de fonctions d’invocation qui se trouvent dans la bibliothèque C (par exemple GlibC). Lorsque celle-ci ignore l’existence d’appels-système trop récents, il est toutefois possible de les invoquer directement par l’intermédiaire de la fonction syscall(). Il est toutefois de notre ressort de connaître le nombre et le type des paramètres attendus par le noyau. C’est ce que nous allons réaliser ci-dessous.

Installation du nouveau noyau

La première étape de cette expérience, consiste évidemment à installer le nouveau noyau Linux.

Voici pour mémoire les étapes à suivre sur une distribution de type Debian/Ubuntu

$ wget http://www.kernel.org/pub/linux/kernel/v3.x/linux-3.14.tar.xz
$ tar xJf linux-3.14.tar.xz
$ cd linux-3.14
$ cp /boot/config-???(le plus récent) ./.config
$ sudo make-kpkg --append-to-version -perso --initrd kernel_image
$ cd ..
$ sudo dpkg -i linux-image-3.14.0-perso*
$ sudo reboot

Protoypes des appels-systèmes considérés

En cherchant dans les sources du noyau, nous pouvons déterminer les prototypes des appels-systèmes que nous souhaitons invoquer.

Plaçons nous dans le répertoire des sources, puis recherchons le premier appel-système.

[linux-3.14]$ find . -type f | xargs grep sched_getattr
./kernel/sched/core.c: * sys_sched_getattr - similar to sched_getparam, but with sched_attr
./kernel/sched/core.c:SYSCALL_DEFINE4(sched_getattr, pid_t, pid, struct sched_attr __user *, uattr,
./include/linux/syscalls.h:asmlinkage long sys_sched_getattr(pid_t pid,
./include/uapi/asm-generic/unistd.h:#define __NR_sched_getattr 275
./include/uapi/asm-generic/unistd.h:__SYSCALL(__NR_sched_getattr, sys_sched_getattr)
[...]

L’appel est implémenté dans le fichier kernel/sched/core.c (seconde ligne de résultat) par la macro SYSCALL_DEFINE4. Celle-ci indique que la fonction prendra quatre arguments.

Le prototype est décrit dans include/linux/syscalls.h dont voici un extrait.

asmlinkage long sys_sched_setscheduler(pid_t pid, int policy,
                                        struct sched_param __user *param);
asmlinkage long sys_sched_setparam(pid_t pid,
                                        struct sched_param __user *param);
asmlinkage long sys_sched_setattr(pid_t pid,
                                        struct sched_attr __user *attr,
                                        unsigned int flags);
asmlinkage long sys_sched_getscheduler(pid_t pid);
asmlinkage long sys_sched_getparam(pid_t pid,
                                        struct sched_param __user *param);
asmlinkage long sys_sched_getattr(pid_t pid,
                                        struct sched_attr __user *attr,
                                        unsigned int size,
                                        unsigned int flags);

Nous notons la présence également du nouvel appel-système sched_setattr(). Ces deux fonctions prennent en argument un pointeur sur une structure sched_attr spécifiquement définie pour elles. Recherchons sa déclaration…

[linux-3.14]$ find . -name '*.h' | xargs grep sched_attr
./include/linux/syscalls.h:struct sched_attr;
./include/linux/syscalls.h:					struct sched_attr __user *attr,
./include/linux/syscalls.h:					struct sched_attr __user *attr,
./include/linux/sched.h: * This variant (sched_attr) is meant at describing a so-called
./include/linux/sched.h: * This is reflected by the actual fields of the sched_attr structure:
./include/linux/sched.h:struct sched_attr {
./include/linux/sched.h:	 * Original scheduling parameters. Copied here from sched_attr
./include/linux/sched.h:			 const struct sched_attr *);

Nous pouvons donc examiner cette structure dans include/linux/sched.h.

/*
 * Extended scheduling parameters data structure.
 *
 * This is needed because the original struct sched_param can not be
 * altered without introducing ABI issues with legacy applications
 * (e.g., in sched_getparam()).
 *
 * However, the possibility of specifying more than just a priority for
 * the tasks may be useful for a wide variety of application fields, e.g.,
 * multimedia, streaming, automation and control, and many others.
 *
 * This variant (sched_attr) is meant at describing a so-called
 * sporadic time-constrained task. In such model a task is specified by:
 *  - the activation period or minimum instance inter-arrival time;
 *  - the maximum (or average, depending on the actual scheduling
 *    discipline) computation time of all instances, a.k.a. runtime;
 *  - the deadline (relative to the actual activation time) of each
 *    instance.
 * Very briefly, a periodic (sporadic) task asks for the execution of
 * some specific computation --which is typically called an instance--
 * (at most) every period. Moreover, each instance typically lasts no more
 * than the runtime and must be completed by time instant t equal to
 * the instance activation time + the deadline.
 *
 * This is reflected by the actual fields of the sched_attr structure:
 *
 *  @size               size of the structure, for fwd/bwd compat.
 *
 *  @sched_policy       task's scheduling policy
 *  @sched_flags        for customizing the scheduler behaviour
 *  @sched_nice         task's nice value      (SCHED_NORMAL/BATCH)
 *  @sched_priority     task's static priority (SCHED_FIFO/RR)
 *  @sched_deadline     representative of the task's deadline
 *  @sched_runtime      representative of the task's runtime
 *  @sched_period       representative of the task's period
 *
 * Given this task model, there are a multiplicity of scheduling algorithms
 * and policies, that can be used to ensure all the tasks will make their
 * timing constraints.
 *
 * As of now, the SCHED_DEADLINE policy (sched_dl scheduling class) is the
 * only user of this new interface. More information about the algorithm
 * available in the scheduling class file or in Documentation/.
 */
struct sched_attr {
        u32 size;

        u32 sched_policy;
        u64 sched_flags;

        /* SCHED_NORMAL, SCHED_BATCH */
        s32 sched_nice;

        /* SCHED_FIFO, SCHED_RR */
        u32 sched_priority;

        /* SCHED_DEADLINE */
        u64 sched_runtime;
        u64 sched_deadline;
        u64 sched_period;
};

Numérotation des appels-système

Lorsque nous voudrons invoquer depuis un programme utilisateur ces nouveaux appels-système, nous pourrons utiliser la fonction syscall() de la bibliothèque C. Néanmoins celle-ci prend en argument le numéro de l’appel-système désiré, qu’il nous faut trouver dans les sources de Linux.

Pour compliquer cette tâche, ce numéro dépend de l’architecture, et notre programme applicatif devra être compilé spécifiquement pour une cible donnée. Les numéros d’appel-systèmes sont déclarés dans des fichiers d’en-tête, préfixés de __NR_.

[linux-3.14]$ find arch  -type f | xargs grep __NR_sched_getattr
arch/sparc/include/uapi/asm/unistd.h:#define __NR_sched_getattr	344
arch/m68k/include/uapi/asm/unistd.h:#define __NR_sched_getattr	350
arch/powerpc/include/uapi/asm/unistd.h:#define __NR_sched_getattr	356
arch/ia64/include/uapi/asm/unistd.h:#define __NR_sched_getattr		1337
arch/s390/include/uapi/asm/unistd.h:#define __NR_sched_getattr	346
arch/x86/include/generated/uapi/asm/unistd_64.h:#define __NR_sched_getattr 315
arch/x86/include/generated/uapi/asm/unistd_x32.h:#define __NR_sched_getattr (__X32_SYSCALL_BIT + 315)
arch/x86/include/generated/uapi/asm/unistd_32.h:#define __NR_sched_getattr 352
arch/mips/include/uapi/asm/unistd.h:#define __NR_sched_getattr		(__NR_Linux + 350)
arch/mips/include/uapi/asm/unistd.h:#define __NR_sched_getattr		(__NR_Linux + 310)
arch/mips/include/uapi/asm/unistd.h:#define __NR_sched_getattr		(__NR_Linux + 314)
arch/arm/include/uapi/asm/unistd.h:#define __NR_sched_getattr		(__NR_SYSCALL_BASE+381)
arch/xtensa/include/uapi/asm/unistd.h:#define __NR_sched_getattr			335
arch/parisc/include/uapi/asm/unistd.h:#define __NR_sched_getattr	(__NR_Linux + 335)
[linux-3.14]$ find arch  -type f | xargs grep __NR_sched_setattr
arch/sparc/include/uapi/asm/unistd.h:#define __NR_sched_setattr	343
arch/m68k/include/uapi/asm/unistd.h:#define __NR_sched_setattr	349
arch/powerpc/include/uapi/asm/unistd.h:#define __NR_sched_setattr	355
arch/ia64/include/uapi/asm/unistd.h:#define __NR_sched_setattr		1336
arch/s390/include/uapi/asm/unistd.h:#define __NR_sched_setattr	345
arch/x86/include/generated/uapi/asm/unistd_64.h:#define __NR_sched_setattr 314
arch/x86/include/generated/uapi/asm/unistd_x32.h:#define __NR_sched_setattr (__X32_SYSCALL_BIT + 314)
arch/x86/include/generated/uapi/asm/unistd_32.h:#define __NR_sched_setattr 351
arch/mips/include/uapi/asm/unistd.h:#define __NR_sched_setattr		(__NR_Linux + 349)
arch/mips/include/uapi/asm/unistd.h:#define __NR_sched_setattr		(__NR_Linux + 309)
arch/mips/include/uapi/asm/unistd.h:#define __NR_sched_setattr		(__NR_Linux + 313)
arch/arm/include/uapi/asm/unistd.h:#define __NR_sched_setattr		(__NR_SYSCALL_BASE+380)
arch/xtensa/include/uapi/asm/unistd.h:#define __NR_sched_setattr			334
arch/parisc/include/uapi/asm/unistd.h:#define __NR_sched_setattr	(__NR_Linux + 334)
[linux-3.14]$

Nous avons donc, pour le couple sched_getattr()/sched_setattr() les valeurs 352/351 pour un PC x86 32 bits, 315/314 pour un x86 64 bits, et ainsi de suite pour les autres architectures.

Il nous manque une dernière valeur, celle de la constante SCHED_DEADLINE utilisée pour choisir l’ordonnancement EDF.

[linux-3.14]$ find include -name '*.h' | xargs grep SCHED_DEADLINE
include/linux/sched.h: * As of now, the SCHED_DEADLINE policy (sched_dl scheduling class) is the
include/linux/sched.h:	/* SCHED_DEADLINE */
include/linux/sched/deadline.h:#ifndef _SCHED_DEADLINE_H
include/linux/sched/deadline.h:#define _SCHED_DEADLINE_H
include/linux/sched/deadline.h: * SCHED_DEADLINE tasks has negative priorities, reflecting
include/linux/sched/deadline.h:#endif /* _SCHED_DEADLINE_H */
include/generated/autoconf.h:#define CONFIG_IOSCHED_DEADLINE 1
include/uapi/linux/sched.h:#define SCHED_DEADLINE      6

Il s’agit donc de la constante 6.

Implémentation des appels-système dans un programme utilisateur

Attention ! L’invocation directe des appels-système est une source d’erreurs faciles, et donc potentiellement dangereuse pour votre système et vos données. Vous voici prévenus 😉

Nous allons implémenter les invocations des appels-système et la déclaration de la structure sched_param, en protégeant ce code par une compilation conditionnelle sur la constante SCHED_DEADLINE. Ainsi lorsque les prochaines mises à jour de la bibliothèque C viendront implémenter ces fonctions, notre programme pourra continuer à fonctionner, espérons-le, de manière transparente.

En examinant l’implémentation de ces fonctions dans le noyau, nous pouvons voir que certaines suppositions sont faites sur le contenu des champs de la structure sched_attr lorsque l’on choisit un ordonnancement SCHED_DEADLINE

  • le champ sched_priority doit être nul
  • le champ sched_flags doit contenir la valeur SCHED_FLAG_RESET_ON_FORK (définie à 1). En effet, une tâche ordonnancée en deadline ne peut pas invoquer fork()
  • le champ sched_runtime doit être supérieur ou égal à 1024
  • le champ sched_deadline doit être supérieur ou égal à sched_runtime
  • le champ sched_period doit être nul, sinon il doit être supérieur ou égal à sched_deadline

Commençons par créer une petite bibliothèque contenant nos appels-système.

schedattr.c:

#define _GNU_SOURCE
#include <sched.h>
#include <unistd.h>
#include <sys/syscall.h>

#ifndef SCHED_DEADLINE

#include <schedattr.h>

// Definitions pour x86_64
#define SCHED_SETATTR_SYSCALL_NB 314
#define SCHED_GETATTR_SYSCALL_NB 315

// Definitions pour i386
//#define SCHED_SETATTR_SYSCALL_NB 351
//#define SCHED_GETATTR_SYSCALL_NB 352

long sched_getattr(pid_t pid, struct sched_attr * attr, unsigned int size, unsigned int flags)
{
  return syscall(SCHED_GETATTR_SYSCALL_NB, pid, attr, size, flags);
}

#define SCHED_FLAG_RESET_ON_FORK  1

long sched_setattr(pid_t pid, struct sched_attr * attr, unsigned int flags)
{
  if (attr->sched_policy == SCHED_DEADLINE) {
    attr->sched_priority = 0;
    attr->sched_flags |= SCHED_FLAG_RESET_ON_FORK;
  }
  return syscall(SCHED_SETATTR_SYSCALL_NB, pid, attr, flags);
}
#endif

Lors de la compilation, définissez les constantes symboliques SCHED_SETATTR_SYSCALL_NB et SCHED_GETATTR_SYSCALL_NB avec les numéros d’appels-système correspondant à votre architecture.

Les prototypes des fonctions et la déclaration de la structure se trouvent dans un fichier d’en-tête.

schedattr.h:
#ifndef LIB_SCHED_ATTR
#define LIB_SCHED_ATTR

#ifndef SCHED_DEADLINE
#define SCHED_DEADLINE  6

struct sched_attr {
  __uint32_t size;

  __uint32_t sched_policy;
  __uint64_t sched_flags;

  /* SCHED_OTHER */
  __int32_t sched_nice;

  /* SCHED_FIFO, SCHED_RR */
  __uint32_t sched_priority;

  /* SCHED_DEADLINE */
  __uint64_t sched_runtime; 
  __uint64_t sched_deadline;
  __uint64_t sched_period;
};

extern long sched_getattr(pid_t pid, struct sched_attr * attr, unsigned int size, unsigned int flags);

extern long sched_setattr(pid_t pid, struct sched_attr * attr, unsigned int flags);

#endif
#endif

Voici un premier programme qui utilise notre bibliothèque pour lire le type d’ordonnancement des processus indiqués sur sa ligne de commande.

sched-getattr.c: 

#include <sched.h>
#include <stdio.h>
#include <stdlib.h>
#include <schedattr.h>
#include <unistd.h>

int main(int argc, char * argv [])
{
  int i;
  int pid;
  struct sched_attr attr;

  for (i = 1; i < argc; i ++) {
    if (sscanf(argv[i], "%d", & pid) != 1) {
      fprintf(stderr, "usage: %s pid...\n", argv[0]);
      exit(EXIT_FAILURE);
    }
    if (sched_getattr(pid, & attr, sizeof(struct sched_attr), 0) != 0) {
      perror(argv[i]);
      continue;
    }
    printf("%d: ", pid);
    if (attr.sched_policy == SCHED_FIFO) {
      printf("Realtime FIFO scheduling, priority=%d\n", attr.sched_priority);
      continue;
    }
    if (attr.sched_policy == SCHED_RR) {
      printf("Realtime RR scheduling, priority=%d\n", attr.sched_priority);
      continue;
    }
    if (attr.sched_policy == SCHED_DEADLINE) {
      printf("Deadline scheduling, runtime=%lu, deadline=%lu, period=%lu\n",
             attr.sched_runtime, attr.sched_deadline, attr.sched_period);
      continue;
    }
    printf("Timesharing scheduling, nice=%d\n", attr.sched_nice);
  }
  return EXIT_SUCCESS;
}

Et un second programme qui permet de modifier entièrement l’ordonnancement de n’importe quel processus existant (un peu à la manière de la commande chrt).

sched-setattr.c:

#include <sched.h>
#include <stdio.h>
#include <stdlib.h>
#include <<string.h>
#include <<schedattr.h>
#include <unistd.h>

void usage_exit(const char * name)
{
  fprintf(stderr, "usage: %s R <priority> <pid>        (RR)\n", name);
  fprintf(stderr, "          F <priority> <pid>        (Fifo)\n");
  fprintf(stderr, "          D <Runtime> <dealine> <period> <pid>\n");
  fprintf(stderr, "          O <nice> <pid>            (Other)\n");
  exit(EXIT_FAILURE);
}

int main(int argc, char * argv [])
{
  int i;
  int pid;
  struct sched_attr attr;

  i = 0;
  while (++i < argc) {

    memset(& attr, 0, sizeof(attr));

    if (strcmp(argv[i], "R") == 0) {
      attr.sched_policy = SCHED_RR;
      if (++i == argc) {
        fprintf(stderr, "missing priority...\n");
        usage_exit(argv[0]);
      }
      if (sscanf(argv[i], "%d", & (attr.sched_priority)) != 1) {
        fprintf(stderr, "wrong priority...\n");
        usage_exit(argv[0]);
      }
    } else if (strcmp(argv[i], "F") == 0) {
      attr.sched_policy = SCHED_FIFO;
      if (++i == argc) {
        fprintf(stderr, "missing priority...\n");
        usage_exit(argv[0]);
      }
      if (sscanf(argv[i], "%d", & (attr.sched_priority)) != 1) {
        fprintf(stderr, "wrong priority...\n");
        usage_exit(argv[0]);
      }
    } else if (strcmp(argv[i], "O") == 0) {
      attr.sched_policy = SCHED_OTHER;
      if (++i == argc) {
        fprintf(stderr, "missing nice value...\n");
        usage_exit(argv[0]);
      }
      if (sscanf(argv[i], "%d", & (attr.sched_nice)) != 1) {
        fprintf(stderr, "wrong nice value...\n");
        usage_exit(argv[0]);
      }
    } else if (strcmp(argv[i], "D") == 0) {
      attr.sched_policy = SCHED_DEADLINE;
      if (++i == argc) {
        fprintf(stderr, "missing runtime value...\n");
        usage_exit(argv[0]);
      }
      if (sscanf(argv[i], "%lu", & (attr.sched_runtime)) != 1) {
        fprintf(stderr, "wrong runtime value...\n");
        usage_exit(argv[0]);
      }
      if (++i == argc) {
        fprintf(stderr, "missing deadline value...\n");
        usage_exit(argv[0]);
      }
      if (sscanf(argv[i], "%lu", & (attr.sched_deadline)) != 1) {
        fprintf(stderr, "wrong deadline value...\n");
        usage_exit(argv[0]);
      }
      if (++i == argc) {
        fprintf(stderr, "missing period value...\n");
        usage_exit(argv[0]);
      }
      if (sscanf(argv[i], "%lu", & (attr.sched_period)) != 1) {
        fprintf(stderr, "wrong period value...\n");
        usage_exit(argv[0]);
      }
    } else {
      fprintf(stderr, "unknown %s scheduling...\n", argv[i]);
      usage_exit(argv[0]);
    }

    if (++i == argc) {
      fprintf(stderr, "missing pid...\n");
      usage_exit(argv[0]);
    }
    if (sscanf(argv[i], "%d", & pid) != 1) {
      fprintf(stderr, "wrong pid...\n");
      usage_exit(argv[0]);
    }
    if (sched_setattr(pid, & attr, 0) != 0) {
      perror(argv[i]);
    }
  }

  return EXIT_SUCCESS;
}

Pour finir, voici le fichier Makefile permettant de tout compiler.

Makefile:

CC=${CROSS_COMPILE}gcc

.PHONY: all

all: libschedattr.so.1.0 sched-getattr sched-setattr

libschedattr.so.1.0: libschedattr.so
  ln -sf libschedattr.so libschedattr.so.1
  ln -sf libschedattr.so.1 libschedattr.so.1.0

libschedattr.so: schedattr.o
  ${CC} -shared -Wl,-soname,libschedattr.so.1.0 schedattr.o -o libschedattr.so

schedattr.o: schedattr.c
  ${CC} -c -fPIC -Wall schedattr.c -I.

sched-getattr: sched-getattr.c libschedattr.so.1.0
  ${CC} -Wall -I. -L. -o sched-getattr sched-getattr.c -lschedattr

sched-setattr: sched-setattr.c libschedattr.so.1.0
  ${CC} -Wall -I. -L. -o sched-setattr sched-setattr.c -lschedattr

.PHONY: clean

clean:
  rm -f schedattr.o libschedattr*
  rm -f sched-getattr
  rm -f sched-setattr

Vous trouverez l’ensemble de tous ces fichiers dans cette archive.

Utilisation

Après compilation, nous trouvons dans notre répertoire le fichier de la bibliothèque (libschedattr.so.1.0), deux liens symboliques permettant d’y accéder, et deux fichiers exécutables.

[~]$ tar xjf schedattr.tar.bz2 
[~]$ cd schedattr/
[schedattr]$ make
gcc -c -fPIC -Wall schedattr.c -I.
gcc -shared -Wl,-soname,libschedattr.so.1.0 schedattr.o -o libschedattr.so
ln -sf libschedattr.so libschedattr.so.1
ln -sf libschedattr.so.1 libschedattr.so.1.0
gcc -Wall -I. -L. -o sched-getattr sched-getattr.c -lschedattr
gcc -Wall -I. -L. -o sched-setattr sched-setattr.c -lschedattr
[schedattr]$ ls -l
total 60
-rwxrwxr-x 1 cpb cpb  7960 avril  5 06:27 libschedattr.so
lrwxrwxrwx 1 cpb cpb    15 avril  5 06:27 libschedattr.so.1 -> libschedattr.so
lrwxrwxrwx 1 cpb cpb    17 avril  5 06:27 libschedattr.so.1.0 -> libschedattr.so.1
-rw-rw-r-- 1 cpb cpb   726 avril  5 06:11 Makefile
-rw-rw-r-- 1 cpb cpb  1685 avril  5 06:14 schedattr.c
-rw-rw-r-- 1 cpb cpb  1465 avril  5 06:16 schedattr.h
-rw-rw-r-- 1 cpb cpb  1664 avril  5 06:27 schedattr.o
-rwxrwxr-x 1 cpb cpb  8873 avril  5 06:27 sched-getattr
-rw-rw-r-- 1 cpb cpb  1892 avril  5 06:16 sched-getattr.c
-rwxrwxr-x 1 cpb cpb 13108 avril  5 06:27 sched-setattr
-rw-rw-r-- 1 cpb cpb  3604 avril  5 06:16 sched-setattr.c
[schedattr]$

Pour que les programmes puissent fonctionner, il faut qu’ils soient capables de trouver la bibliothèque. Deux possibilités :

  • vous copiez le fichier libschedattr.so.1.0 dans /usr/local/lib par exemple puis lancez la commande ldconfig pour que les liens nécessaires y soient créés.
  • vous remplissez la variable d’environnement LD_LIBRARY_PATH avec le répertoire contenant la bibliothèque (ou le point pour représenter le répertoire courant) avant de lancer les commandes

C’est la seconde solution que je vais adopter.

sched-getattr

Commençons par vérifier le comportement de sched-getattr. La variable $$ contient toujours le PID du shell courant.

[schedattr]$ export LD_LIBRARY_PATH=.
[schedattr]$ echo $$
16337
[schedattr]$ ./sched-getattr 16337
16337: Timesharing scheduling, nice=0

Le shell est ordonnancé en temps partagé avec une valeur de nice nulle, c’est normal. Modifions cette valeur.

[schedattr]$ renice -n +5 16337
16337 (process ID) old priority 0, new priority 5
[schedattr]$ ./sched-getattr 16337
16337: Timesharing scheduling, nice=5

Notre programme a vu la modification, l’appel-système sched_getattr() semble fonctionner. Passons le shell en temps réel avec la commande chrt et vérifions ce qu’indique sched-getattr.

[schedattr]$ sudo chrt -pf 20 16337
[schedattr]$ ./sched-getattr 16337
16337: Realtime FIFO scheduling, priority=20

Parfait. Vérifions pour l’ordonnancement Round Robin.

[schedattr]$ sudo chrt -pr 40 16337
[schedattr]$ ./sched-getattr 16337
16337: Realtime RR scheduling, priority=40
[schedattr]$

sched-setattr

Nous allons maintenant vérifier le fonctionnement du programme sched-setattr. Commençons par les ordonnancements temps partagé, temps réel Fifo et Round Robin en vérifiant le résultat du changement d’ordonnancement avec chrt.

[schedattr]$ ./sched-setattr --help
unknown --help scheduling...
usage: ./sched-setattr R <priority> <pid>        (RR)
          F <priority> <pid>        (Fifo)
          D <Runtime> <Dealine> <Period> <pid>
          O <nice> <pid>            (Other)
[schedattr]$ sudo LD_LIBRARY_PATH=.   ./sched-setattr O 0 16337
[schedattr]$ chrt -p 16337
pid 16337's current scheduling policy: SCHED_OTHER
pid 16337's current scheduling priority: 0
[schedattr]$ sudo LD_LIBRARY_PATH=.   ./sched-setattr R 10 16337
[schedattr]$ chrt -p 16337
pid 16337's current scheduling policy: SCHED_RR
pid 16337's current scheduling priority: 10
[schedattr]$ sudo LD_LIBRARY_PATH=.   ./sched-setattr F 20 16337
[schedattr]$ chrt -p 16337
pid 16337's current scheduling policy: SCHED_FIFO
pid 16337's current scheduling priority: 20

Notez la syntaxe permettant d’initialiser une variable d’environnement spécialement pour exécuter la commande se trouvant sur le reste de la ligne. Ceci est nécessaire à cause du sudo qui efface l’environnement avant de lancer la commande qu’on lui transmet.

Passons en ordonnancement deadline et vérifions avec chrt.

[schedattr]$ sudo LD_LIBRARY_PATH=.   ./sched-setattr D 1024 2048 4096 16337
[schedattr]$ chrt -p 16337
chrt: unknown scheduling policy
chrt: échec d'obtention des attributs du PID 16337: Argument invalide
pid 16337's current scheduling policy: [schedattr]$

Hé oui ! La commande chrt ne connaît pas encore ce type d’ordonnancement. Elle échoue donc (et se plante un peu maladroitement…) en essayant d’interpréter les résultats obtenus avec l’ancien appel sched_getschedparam(), le seul appel-système qu’elle connaisse. Mais nous pouvons vérifier le comportement avec sched_getattr():

[schedattr]$ ./sched-getattr 16337
16337: Deadline scheduling, runtime=1024, deadline=2048, period=4096
[schedattr]$

Conclusion

Le but de cet article est d’utiliser des appels-système nouveaux, inconnus de la bibliothèque C. Ce que l’on a vu ci-dessus pourrait s’appliquer à d’autres circonstances : invocation d’un appel-système personnalisé, utilisation d’une bibliothèque C ancienne sur un noyau récent, etc.

Nous avons obtenu deux programmes qui nous donnent accès au mécanisme d’ordonnancement temps réel deadline apparu dans le noyau 3.14. Il nous reste à comprendre comment fonctionne cet ordonnancement… Ce qui fera l’objet d’un prochain article.

4 Réponses

  1. H2G2 dit :

    Très instructif ! Ne pas oublier de mettre le prototype de syscall ! Ça me surprend de voir une fonction de ce type, j’ai toujours cru que cette partie était construite en assembleur.

  2. requinham dit :

    Très bon article de contournement de ce temporaire manque de la libc.
    Au prochain article sur le plus de ce type d’ordonnancement
    Merci

  3. Matlo dit :

    Merci pour cet article.
    J’attends la suite avec impatience 🙂

  4. Serign dit :

    Thank you very much.

    I really enjoy reading your articles. You are my hero.

    Regards

URL de trackback pour cette page