Je fais un td qui a pour but de me faire programmer un scheduler qui tourne sur une machine, les coeurs de la machine sont des pthreads et les threads de la machine sont des contextes. Mon problème est que lorsque j'envoie un signal à un thread (pour la 2e fois), au lieu d'appeler le signal handler, il fait un segmentation de fault. Il est un peu gros, mais je vous met les parties utiles à la compréhension de mon problème.
Le thread principal attends sur join. Il y a un autre thread qui appelle la fonction rr (pour roundrobin). Elle s'applique sur un tableau de threads (vcpu_tab) de taille nb_vcpu. La voici.
void* rr(void* param){
while(1){
redistribution();
dring();
sleep(scheduler.quantum);
}
} void dring() {//envoie à chacun des nb_vcpu threads un signal sigusr1
if(nb_vcpu>=nb_uth){//si pas trop de uth, assigner les vcpu aux uthreads de façon cannonique for(int i=0;i<nb_uth;i++){ vcpu_tab[i].next_uthread=i;
} for(int i=nb_uth;i<nb_vcpu;i++){ vcpu_tab[i].next_uthread=-1; } scheduler.nxt_uth = 0 ; } else{//si plus de uth que de vcpu, faire les rotations for(int i=0;i<nb_vcpu;i++){ vcpu_tab[i].next_uthread=scheduler.nxt_uth; scheduler.nxt_uth=(scheduler.nxt_uth+1)%nb_uth; }
}
}
Chaque thread du tableau est lancé sur la fonction :
void* idle_vcpu(void* vcpu) {
vcpu_t* vcp=(vcpu_t*)vcpu;
vcp->tid=pthread_self();
//configuration du signal handler
struct sigaction sa;
sa.sa_handler = idle_handlersig;
sa.sa_flags = SA_RESTART;
sigaction(SIGUSR1,&sa,NULL);
sigemptyset(&sa.sa_mask);
//sigaddset(&sa.sa_mask,SIGUSR1);
//SIGUSR1 sera le signal qui servira à faire les changements de uThreads
while(1){
sigsuspend(&sa.sa_mask);
printf("Pause dans le sigsuspend\n");
}
return NULL;
}
Le sigaction du signal SIGUSR1 est
void idle_handlersig(int signal){
printf("On rentre dans handlesignal\n");
int i;
if(signal==SIGUSR1){
i=get_vcpu_indice();
printf("Le i c'est %d\n",i);
if (i!=-1 && vcpu_tab[i].current_uthread == -1) { // Alors, c'est qu'on est au moment où l'on dit à un vcpu d'effectuer son premier uThread
printf("E1\n");
int nxt=vcpu_tab[i].next_uthread;
printf("E2\n");
printf("Le vcpu %d va s'occupper du uthread %d.\n",i,nxt);
if(nxt!=-1 && nxt!=vcpu_tab[i].current_uthread){
//passage du contexte idle au contexte spécifié
vcpu_tab[i].current_uthread=nxt;
swapcontext(&(vcpu_tab[i].idle_contxt),&uth_tab[nxt]);
}
}
else { // Sinon, c'est un signal que le Scheduler lui envoie pour qu'il passe à son prochain uThread.
int nxt = vcpu_tab[i].next_uthread;
int cur = vcpu_tab[i].current_uthread ;
printf("Le vcpu %d va s'occupper du uthread %d.\n",i,nxt);
if(nxt!=-1){
//passage d'un contexte à un autre
swapcontext(&uth_tab[cur],&uth_tab[nxt]);
}
else{
//passage d'un contexte à idle
swapcontext(&uth_tab[nxt],&(vcpu_tab[i].idle_contxt));
}
}
}
}
En gros a la réception du signal sigusr1, un threads du tableau vcpu_tab peut switcher de context, pour la fonction que j'ai mis en main:
void g(){
//int* a=d;
while(1){
printf("Salut uthread ecrit %d ( c la fct g la)\n",5);
sleep(1);
}
}
int main(){
config_sched(ROUND_ROBIN,3);
create_vcpu(1);
int arg1=1;
//int arg2=2, arg3=3;
uthread_create(&g,&arg1);
//uthread_create(&f,&arg2);
//uthread_create(&f,&arg3);
run();
join();
}
Et à l'éxecution, la fonction f se lance une fois, puis au 2e tour de roundrobin, j'ai le message "Segmentation fault". En débogant, j'ai vu que cette erreur apparait en réception de sigusr1, avant le début de l'appel au signal handler. Entre la réception des deux signaux, il y a un context switch.
J'ai conscience du fait que j'envoie un pavé, mais je ne m'en sors pas. Merci de votre aide.
- Edité par BelhouariSéhane 22 mai 2022 à 18:54:29
J'ai conscience du fait que j'envoie un pavé, mais je ne m'en sors pas. Merci de votre aide.
- Edité par BelhouariSéhane il y a 21 minutes
Bonjour,
lorsque tu as des segfault il y a un outil indispensable : valgrind.
Installe le, compile ton projet en activant les options de debug (-g avec gcc/clang), lance ton appli avec valgrind en lui donnant les options --leak-check=full --track-origins=yes --show-reachable=yes.
Tu auras un log précis de ce qui cause ton segfault. Éventuellement tu peux utiliser un debuger pour bien cibler en mode pas à pas … mais en général le log valgrind suffit. Comme tu fais du multithread, note bien les TID pour t'y retrouver.
Si tu as des soucis de compréhension du log, reviens vers nous.
Quand un homme a faim, mieux vaut lui apprendre à pêcher que de lui donner un poisson. Confucius.
Ok. J'ai essayé, mais quand je lance le projet avec vargrind, le segfault n'apparait pas, et le programme fonctionne, alors que lancé normalement, il y a u segfault
Address 0x4a745a8 is 8 bytes before a block of size 16,284 alloc'd
==26100== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-lin
Je vois pas comment. Ais-je mal utilisé le contexte ?
- Edité par BelhouariSéhane 22 mai 2022 à 20:09:48
uthread est une structure et j'imagine que la définition se trouve dans une fonction vu le malloc sur stack donc oui peut être faudrait t'il l'allouer aussi
pense aussi à vérifier que le retour de malloc ne soit pas NULL sur tes allocations
Invalid write of size 8
==27422== at 0x48D4154: makecontext (makecontext.c:113)
==27422== by 0x10944E: uthread_create (uthread.c:35)
==27422== by 0x109DA3: main (main.c:63)
==27422== Address 0x4ab0de8 is 8 bytes before a block of size 16,284 alloc'd
==27422== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==27422== by 0x10938C: uthread_create (uthread.c:24)
==27422== by 0x109DA3: main (main.c:63)
- Edité par BelhouariSéhane 22 mai 2022 à 20:22:59
Un autre problème est apparmement qu'un thread 2 utilise malloc, met le pointeur dans une variable globale, qu'un autre thread 3 utilise et génère un segfault. Comment rendre l'adresse comune ?
- Edité par BelhouariSéhane 22 mai 2022 à 20:57:14
Je vois pas la différence dans le code. Je pense pas qu'il faille utiliser strcpy puisque l'espace mémoire est représenté dans la structure par un pointeur sur un char non initialisé au début.
Valgrind a écrit at makecontext by uthread_create donc je pense que les lignes correspondent à la fonction que j'ai envoyé
- Edité par BelhouariSéhane 22 mai 2022 à 21:10:44
Un autre problème est apparmement qu'un thread 2 utilise malloc, met le pointeur dans une variable globale, qu'un autre thread 3 utilise et génère un segfault. Comment rendre l'adresse comune ?
- Edité par BelhouariSéhane il y a 33 minutes
Il faut protéger l'accès aux variables que tu modifies et qui sont partagées (lock, condition variable, modifications atomiques, …).
valgrind propose aussi un outil pour détecter dans une certaine mesure les race conditions, cf l'option --tool=helgrind
d'un accès concurrent, une lecture effectuée par un thread alors que l'écriture n'est pas terminée par un autre thread par exemple. À moins qu'une donnée soit immutable (jamais modifiée, donc uniquement utilisée en lecture) il faut toujours en protéger l'accès en lecture et écriture. C'est la base en programmation multithread.
Ensuite, quand tu as des messages genre «invalid write of size …» c'est que tu écris dans une zone mémoire qui ne t'appartient pas ⇒ c'est un gros souci.
C'est déjà très étrange, puisque je suis persuadé de ce que les allocations soient licites. Vargrind écriyt autre chose d'ailleurs:
Thread 2:
==33776== Syscall param rt_sigaction(act->sa_mask) points to uninitialised byte(s)
==33776== at 0x4870496: __libc_sigaction (sigaction.c:58)
==33776== by 0x109894: idle_vcpu (vcpu.c:80)
==33776== by 0x4864608: start_thread (pthread_create.c:477)
==33776== by 0x499E162: clone (clone.S:95)
==33776== Address 0x5673d98 is on thread 2's stack
==33776== in frame #0, created by __libc_sigaction (sigaction.c:43)
==33776== Uninitialised value was created by a heap allocation
==33776== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==33776== by 0x10985E: idle_vcpu (vcpu.c:76)
==33776== by 0x4864608: start_thread (pthread_create.c:477)
==33776== by 0x499E162: clone (clone.S:95)
Je ne comprends pas comment interpréter ce message
C'est déjà très étrange, puisque je suis persuadé de ce que les allocations soient licites. Vargrind écriyt autre chose d'ailleurs:
Thread 2:
==33776== Syscall param rt_sigaction(act->sa_mask) points to uninitialised byte(s)
==33776== at 0x4870496: __libc_sigaction (sigaction.c:58)
==33776== by 0x109894: idle_vcpu (vcpu.c:80)
==33776== by 0x4864608: start_thread (pthread_create.c:477)
==33776== by 0x499E162: clone (clone.S:95)
==33776== Address 0x5673d98 is on thread 2's stack
==33776== in frame #0, created by __libc_sigaction (sigaction.c:43)
==33776== Uninitialised value was created by a heap allocation
==33776== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==33776== by 0x10985E: idle_vcpu (vcpu.c:76)
==33776== by 0x4864608: start_thread (pthread_create.c:477)
==33776== by 0x499E162: clone (clone.S:95)
Je ne comprends pas comment interpréter ce message
Chaque thread a sa propre pile, tu ne peux pas filer l'adresse d'un élément sur la pile d'un thread à un autre, entre autre à un signal handler qui ne va pas s'exécuter dans le contexte de ton thread.
De plus, tu as un malloc dans la fonction idle_vcpu à la ligne 76 du fichier vcpu.c, tu alloues une zone mémoire que tu n'initialises pas mais que tu utilises à la ligne 80 comme si elle l'était …
uthread.c: In function ‘uthread_create’:
uthread.c:62:42: warning: passing argument 2 of ‘makecontext’ from incompatible pointer type [-Wincompatible-pointer-types]
62 | makecontext(&(idle_uthread_tab[nb_uth]),idle_uthread_func,1,nb_uth);
| ^~~~~~~~~~~~~~~~~
| |
| void (*)(int)
In file included from uthread.h:17,
from Scheduler.h:2,
from Scheduler.c:1,
from main.c:6:
/usr/include/ucontext.h:51:52: note: expected ‘void (*)(void)’ but argument is of type ‘void (*)(int)’
51 | extern void makecontext (ucontext_t *__ucp, void (*__func) (void)
«/usr/include/ucontext.h:51:52: note: expected ‘void (*)(void)’ but argument is of type ‘void (*)(int)’»
il faut donner un void (*)(void) comme second d'après la déclaration de makecontext mais tu lui fournis un void (*)(int) ⇒ en effet les types ne sont pas compatibles.
Oui mais si tu regarde la definition de makecontext, dans ce cas, comme j'ai donné les arguments (...,1, nb_uth), la fonction en paramètre 2 devrait avoir le type que j'ai donné et pas un type qui ne prends pas de paramètres.
Oui mais si tu regarde la definition de makecontext, dans ce cas, comme j'ai donné les arguments (...,1, nb_uth), la fonction en paramètre 2 devrait avoir le type que j'ai donné et pas un type qui ne prends pas de paramètres.
Alors un rtfm :
MAKECONTEXT(3) Linux Programmer's Manual MAKECONTEXT(3)
NAME
makecontext, swapcontext - manipulate user context
SYNOPSIS
#include <ucontext.h>
void makecontext(ucontext_t *ucp, void (*func)(), int argc, ...);
cela signifie que le second paramètre makecontext est de type void (*)() et toi tu lui fournis un void (*)(int) … ce qui n'est pas compatible …
Le second paramètre est la fonction … ce qui est clairement indiqué par le message de ton compilo.
× Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
× Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
yasakani no magatama
yasakani no magatama
yasakani no magatama
yasakani no magatama
yasakani no magatama
yasakani no magatama
yasakani no magatama
yasakani no magatama