sched_setaffinity(2) System Calls Manual sched_setaffinity(2) NOM sched_setaffinity, sched_getaffinity - Definir et recuperer le masque d'affinite CPU d'un thread BIBLIOTHEQUE Bibliotheque C standard (libc, -lc) SYNOPSIS #define _GNU_SOURCE /* Consultez feature_test_macros(7) */ #include int sched_setaffinity(pid_t pid, size_t cpusetsize, const cpu_set_t *mask); int sched_getaffinity(pid_t pid, size_t cpusetsize, cpu_set_t *mask); DESCRIPTION Le masque d'affinite CPU d'un thread determine l'ensemble des processeurs sur lesquels il est susceptible de s'executer. Sur un systeme multiprocesseur, definir le masque d'affinite CPU permet d'obtenir une meilleure performance. Par exemple, en dediant un CPU a un thread particulier (c'est-a-dire definir le masque d'affinite de ce thread pour indiquer un seul CPU, et definir le masque d'affinite de tous les autres threads pour exclure ce CPU), il est possible d'assurer une vitesse d'execution maximale pour ce thread. Restreindre un processus pour qu'il ne s'execute que sur un seul CPU reduit le cout lie a l'invalidation du cache qui se produit lorsqu'un thread cesse de s'executer sur un CPU puis est relance sur un autre CPU. Un masque d'affinite CPU est represente par la structure cpu_set_t, un ensemble de CPU (<< CPU set >>), pointe par mask. Des macros pour manipuler des ensembles de CPU sont decrites dans CPU_SET(3). sched_setaffinity() definit le masque d'affinite CPU du thread dont l'identifiant est pid a la valeur donnee par mask. Si pid est 0, le thread appelant est utilise. L'argument cpusetsize est la taille (en octets) de la structure pointee par mask. Normalement, cet argument doit etre specifie comme sizeof(cpu_set_t). Si le thread indique par pid n'est pas actuellement en cours d'execution sur l'un des CPU specifies dans mask, alors ce thread est migre vers l'un des CPU specifies dans mask. La fonction sched_getaffinity() ecrit dans la structure cpu_set_t pointee par mask le masque de preferences du thread pid. L'argument cpusetsize indique la taille (en octets) de mask. Si pid vaut zero, le masque du thread en cours est renvoye. VALEUR RENVOYEE sched_setaffinity() et sched_getaffinity() renvoient 0 s'ils reussissent (mais voir << Differences entre la bibliotheque C et le noyau >> ci-dessous, qui explique que le sched_getaffinity() sous-jacent differe dans son code de retour). En cas d'echec, -1 est renvoye et errno est positionne pour indiquer l'erreur. ERREURS EFAULT Une adresse memoire fournie n'est pas correcte. EINVAL Le masque de bits d'affinite mask ne contient pas de processeurs qui soient actuellement physiquement sur le systeme et autorises pour le thread d'apres les restrictions qui peuvent etre imposees par les cgroups cpuset ou le mecanisme << cpuset >> decrit dans cpuset(7). EINVAL (sched_getaffinity() et, avant Linux 2.6.9, sched_setaffinity()) cpusetsize est plus petit que la taille du masque d'affinite utilise par le noyau. EPERM (sched_setaffinity()) Le thread appelant n'a pas les privileges appropries. L'appelant doit avoir un UID effectif egal a l'UID effectif ou reel du thread identifie par pid, ou avoir la capacite CAP_SYS_NICE dans l'espace de noms de l'utilisateur du thread designe par pid. ESRCH Le thread numero pid n'existe pas. STANDARDS Linux. HISTORIQUE Linux 2.5.8, glibc 2.3. Au depart, les interfaces de la glibc avaient un parametre cpusetsize de type unsigned int. Dans glibc 2.3.3, ce parametre a ete supprime, mais il a ete reintroduit dans glibc 2.3.4, avec pour type size_t. NOTES Apres un appel a sched_setaffinity(), l'ensemble des CPU sur lesquels le thread s'executera est l'intersection de l'ensemble specifie dans le parametre mask et l'ensemble des CPU actuellement presents sur le systeme. Le systeme peut restreindre encore plus l'ensemble des CPU sur lesquels le thread peut tourner si le mecanisme << cpuset >>, decrit dans cpuset(7), est utilise. Ces restrictions sur le veritable ensemble de CPU sur lesquels le thread peut tourner sont imposees sans avertissement par le noyau. Il existe plusieurs manieres de determiner le nombre de processeurs disponibles sur le systeme, notamment l'inspection du contenu de /proc/cpuinfo, l'utilisation de sysconf(3) pour avoir les valeurs des parametres _SC_NPROCESSORS_CONF et _SC_NPROCESSORS_ONLN, et l'inspection de la liste des repertoires de processeur dans /sys/devices/system/cpu/. sched(7) decrit les politiques d'ordonnancement sous Linux. Le masque d'affinite est un attribut de thread, qui peut etre modifie independamment pour chacun des threads d'un groupe de threads. La valeur renvoyee par gettid(2) peut etre passee dans l'argument pid. Specifier un pid de 0 definira l'attribut pour le thread appelant, et une valeur egale a celle renvoyee par getpid(2) definira l'attribut pour le thread principal du groupe de threads. (Si vous utilisez l'API POSIX des threads, alors utilisez pthread_setaffinity_np(3) au lieu de sched_setaffinity().) L'option d'amorcage isolcpus peut etre utilisee pour isoler un ou plusieurs processeurs au moment du demarrage, afin qu'aucun processus n'utilise ces processeurs. Suite a l'utilisation de cette option, la seule maniere d'affecter des processus a un processeur isole est d'utiliser sched_setaffinity() ou le mecanisme cpuset(7). Pour plus d'informations, voir le fichier Documentation/admin-guide/kernel-parameters.txt des sources du noyau. Comme indique dans ce fichier, isolcpus est le mecanisme privilegie d'isolation des processeurs (plutot que de definir a la main l'affinite processeur de tous les processus du systeme). Un processus enfant cree par fork(2) herite du masque d'affinite CPU de son parent. Le masque d'affinite est conserve au travers d'un execve(2). Differences entre bibliotheque C et noyau Cette page de manuel decrit l'interface de la glibc pour les appels lies a l'affinite CPU. L'interface des appels systeme est legerement differente, mask ayant le type unsigned long *, montrant le fait que l'implementation des ensembles de CPU est en realite un simple masque de bits. En cas de succes, l'appel systeme sched_getaffinity() brut renvoie le nombre d'octets copies dans le tampon mask ; il s'agira de la taille minimale de cpusetsize et de la taille (en octets) du type de donnees cpumask_t utilisee en interne par le noyau pour representer le masque de bits du jeu de processeurs. Gestion des systemes ayant de grands masques d'affinite de processeur Les appels systeme sous-jacents (qui representent les masques de processeur par des masques de bits de type unsigned long *) n'imposent aucune restriction quant a la taille du masque de processeur. Toutefois, le type de donnees cpu_set_t utilise par la glibc a une taille fixe de 128 octets, si bien que le nombre maximal de processeurs qui peuvent etre representes est de 1023. Si le masque d'affinite de processeur du noyau est plus grand que 1024, les appels sous la forme : sched_getaffinity(pid, sizeof(cpu_set_t), &mask); echouent avec l'erreur EINVAL, celle produite par l'appel systeme sous-jacent si la taille mask indiquee dans cpusetsize est inferieure a celle du masque d'affinite utilisee par le noyau (selon la topologie des processeurs du systeme, le masque d'affinite du noyau peut etre beaucoup plus grand que le nombre de processeurs actifs sur le systeme). Si vous travaillez sur des systemes ayant de grands masques d'affinite de processeur, vous pouvez allouer de maniere dynamique l'argument mask (voir CPU_ALLOC(3)). Actuellement, la seule maniere de faire cela est de sonder la taille de masque necessaire en utilisant les appels sched_getaffinity() avec des tailles de masque croissantes (jusqu'a ce que l'appel n'echoue pas avec l'erreur EINVAL). Gardez en tete qu'il se peut que CPU_ALLOC(3) alloue un peu plus de processeurs que vous ne l'avez demande (car les ensembles de processeurs sont implementes sous forme de masques de bits alloues par unites de sizeof(long)). Par consequent, sched_getaffinity() peut positionner des bits au-dela de la taille d'allocation demandee car le noyau voit quelques bits supplementaires. Donc, l'appelant doit revenir sur les bits de l'ensemble renvoye en comptant ceux qui sont positionnes et s'arreter lorsqu'il atteint la valeur renvoyee par CPU_COUNT(3) (et non pas revenir sur les bits de l'ensemble dont une allocation a ete demandee). EXEMPLES Le programme ci-dessous cree un processus enfant. Puis le parent et l'enfant s'affectent mutuellement un processeur indique et executent des boucles identiques qui consomment du temps de processeur. Avant de se terminer, le parent attend que l'enfant s'acheve. Le programme prend trois parametres en ligne de commande : le numero de processeur du parent, celui de l'enfant et le nombre de boucles que doivent effectuer les deux processus. Comme le montre le modele ci-dessous, la quantite de temps processeur et de temps reel consomme lors de l'execution d'un programme dependra des effets de mise en cache a l'interieur du processeur et de l'utilisation ou non du meme processeur par les processus. On utilise d'abord lscpu(1) pour determiner que ce systeme (x86) comporte deux coeurs, chacun ayant deux processeurs : $ lscpu | egrep -i 'core.*:|socket' Thread(s) par coeur : 2 Coeur(s) par socket : 2 Socket(s) : 1 On chronometre alors l'operation du programme exemple dans trois cas : les deux processus en fonction sur le meme processeur, sur des processeurs differents du meme coeur et sur des processeurs differents sur des coeurs differents. $ time -p ./a.out 0 0 100000000 reel 14.75 utilisateur 3.02 sys 11.73 $ time -p ./a.out 0 1 100000000 reel 11.52 utilisateur 3.98 sys 19.06 $ time -p ./a.out 0 3 100000000 reel 7.89 utilisateur 3.29 sys 12.07 Source du programme #define _GNU_SOURCE #include #include #include #include #include #include int main(int argc, char *argv[]) { int parentCPU, childCPU; cpu_set_t set; unsigned int nloops; if (argc != 4) { fprintf(stderr, "Utillisation : %s CPU_parent CPU_enfant nb_boucles\n", argv[0]); exit(EXIT_FAILURE); } parentCPU = atoi(argv[1]); childCPU = atoi(argv[2]); nloops = atoi(argv[3]); CPU_ZERO(&set); switch (fork()) { case -1: /* Erreur */ err(EXIT_FAILURE, "fork"); case 0: /* Enfant */ CPU_SET(childCPU, &set); if (sched_setaffinity(getpid(), sizeof(set), &set) == -1) err(EXIT_FAILURE, "sched_setaffinity"); for (unsigned int j = 0; j < nloops; j++) getppid(); exit(EXIT_SUCCESS); default: /* Parent */ CPU_SET(parentCPU, &set); if (sched_setaffinity(getpid(), sizeof(set), &set) == -1) err(EXIT_FAILURE, "sched_setaffinity"); for (unsigned int j = 0; j < nloops; j++) getppid(); wait(NULL); /* Attendre que l'enfant se termine */ exit(EXIT_SUCCESS); } } VOIR AUSSI lscpu(1), nproc(1), taskset(1), clone(2), getcpu(2), getpriority(2), gettid(2), nice(2), sched_get_priority_max(2), sched_get_priority_min(2), sched_getscheduler(2), sched_setscheduler(2), setpriority(2), CPU_SET(3), get_nprocs(3), pthread_setaffinity_np(3), sched_getcpu(3), capabilities(7), cpuset(7), sched(7), numactl(8) TRADUCTION La traduction francaise de cette page de manuel a ete creee par Christophe Blaess , Stephan Rafin , Thierry Vignaud , Francois Micaux, Alain Portal , Jean-Philippe Guerard , Jean-Luc Coulon (f5ibh) , Julien Cristau , Thomas Huriaux , Nicolas Francois , Florentin Duneau , Simon Paillard , Denis Barbier , David Prevot , Cedric Boutillier , Frederic Hantrais et Jean-Philippe MENGUAL Cette traduction est une documentation libre ; veuillez vous reporter a la GNU General Public License version 3 concernant les conditions de copie et de distribution. Il n'y a aucune RESPONSABILITE LEGALE. Si vous decouvrez un bogue dans la traduction de cette page de manuel, veuillez envoyer un message a . Pages du manuel de Linux 6.06 31 octobre 2023 sched_setaffinity(2)