问题
I'm trying to write a library to manage threads using contexts(getcontext, setcontext, makecontext, swapcontext), without pthreads.
A function MyThreadYield(), pauses the current thread context, sends it to the end of ready queue and starts executing the thread at the head of ready queue.
In MyThreadYield(), I am able to retrieve context of paused thread via swapcontext() but just when it is returning to the function execution I get a segfault.
Eg :- Suppose thread 1 is the init thread, it runs and creates thread 2. Then it yields. Now thread 2 runs and in turn calls yield. Here the swapcontext runs and I can verify that swap is successful through "%p". But when it tries to return from MyThreadYield() to restore function run of thread 1, I'm getting seg fault.
Here is my library code :-
typedef void *MyThread;
typedef void *MySemaphore;
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include <ucontext.h>
#include <string.h>
// structure of threads
struct tnode {
ucontext_t* ctextptr;
int tid; // own thread id
int pid; // parent thread id
} ;
struct tnode* cthread = NULL;
// linked list for ready queue
struct rnode {
struct tnode* thread;
struct rnode* next;
} ;
struct rnode* rhead = NULL;
struct rnode* rtail;
static int tindex; // will generate thread id (tid) for new threads
// linked list for blocked queue
struct bnode {
struct tnode* thread;
struct bnode* next;
int wid[20]; // tid of threads bthread is waiting on //limit is 20
} ;
struct bnode* bhead = NULL;
struct bnode* btail;
int b; // keeps track of size of bacche
int bacche[20]; // array to store children of current running thread
//Pushes blocked thread into the blocked linked list
void AddToBlist (struct tnode* thd , char s[])
{
struct bnode* newbie = NULL;
newbie = malloc (sizeof(struct bnode));
newbie->thread = thd;
int i;
for(i=0; i<20; i++)
newbie->wid[i] = 0;
char * pch; i=0;
pch = strtok(s," "); //Reference : http://stackoverflow.com/questions/4513316/split-string-in-c-every-white-space
while (pch!=NULL)
{
newbie->wid[i] = atoi(pch);
i++;
pch = strtok (NULL, " ");
}
printf("\n thread wait array : \t"); //ff
for(i=0; i<20; i++)
printf("%d\t",newbie->wid[i]); //ff
newbie->next = NULL;
btail = newbie;
if(bhead==NULL)
{
bhead = newbie;
} else {
struct bnode* current = bhead;
while (current->next != NULL)
{
current = current->next;
}
current->next = newbie;
}
}
// Scan blocked queue to find a matching thread as specified by id
int scanB (int id)
{
int retval = 0;
struct bnode* current = bhead;
while (current != NULL)
{
if((current->thread)->tid == id )
{
retval = 1;
break;
}
current = current->next;
}
return retval;
}
// Scan blocked queue for parent id listed
int scanBP (int id)
{
int retval = 0;
struct bnode* current = bhead;
while (current != NULL)
{
if((current->thread)->pid == id )
{
bacche[b] = (current->thread)->tid;
b++;
retval ++;
}
current = current->next;
}
return retval;
}
// Clears a blocked thread and moves it to ready queue
// Reference : https://www.cs.bu.edu/teaching/c/linked-list/delete/
void clearB (int id)
{
if (bhead==NULL)
{
//return NULL;
}
struct bnode* bcur = bhead;
struct bnode* bpre = bhead;
while (bcur!= NULL)
{
int i;
for(i=0; i<20; i++)
{
if (bcur->wid[i] == id)
{
bcur->wid[i] = 0;
break;
}
}
int k = 0;
for(i=0; i<20; i++)
{
if (bcur->wid[i] == 0)
k++;
}
if (k==20)
{
printf("\n thread no longer blocked .... moving to ready queue \n"); //ff
AddToRlist(bcur->thread);
if (bcur == bhead)
{
struct bnode* temp = bhead;
bhead = bhead->next;
free(temp);
bcur = bhead;
bpre = bhead;
} else {
struct bnode* temp = bcur;
bcur = bcur->next;
bpre->next = bcur;
free(temp);
}
} else {
bpre = bcur;
bcur = bcur->next;
}
}
}
//Pushes newly created context into the linked list
void AddToRlist (struct tnode* thd)
{
struct rnode* newbie = NULL;
newbie = malloc (sizeof(struct rnode));
newbie->thread = thd;
newbie->next = NULL;
rtail = newbie;
if(rhead==NULL)
{
rhead = newbie;
} else {
struct rnode* current = rhead;
while (current->next != NULL)
{
current = current->next;
}
current->next = newbie;
}
}
// Scan ready queue to find a matching thread as specified by id
int scanR (int id)
{
int retval = 0;
struct rnode* current = rhead;
while (current != NULL)
{
if((current->thread)->tid == id )
{
retval = 1;
break;
}
current = current->next;
}
return retval;
}
// Checks for parent id among ready queue elements
int scanRP (int id)
{
int retval = 0;
struct rnode* current = rhead;
while (current != NULL)
{
if((current->thread)->pid == id )
{
bacche[b] = (current->thread)->tid;
b++;
retval++;
}
current = current->next;
}
return retval;
}
// ****** THREAD OPERATIONS ******
// Create a new thread.
MyThread MyThreadCreate(void(*start_funct)(void *), void *args)
{
tindex++;
struct tnode* tnew = NULL;
tnew = malloc(sizeof (struct tnode));
memset(tnew, 0, sizeof(struct tnode));
tnew->tid = tindex;
tnew->pid = cthread->tid;
char stc[8192];
tnew->ctextptr = (ucontext_t *) malloc(sizeof(ucontext_t));
getcontext(tnew->ctextptr);
tnew->ctextptr->uc_stack.ss_sp = stc;
tnew->ctextptr->uc_stack.ss_size = sizeof stc;
tnew->ctextptr->uc_stack.ss_flags = 0;
tnew->ctextptr->uc_link = NULL;
makecontext(tnew->ctextptr, (void (*)(void))start_funct, 1, args);
AddToRlist(tnew);
return((MyThread)tnew);
}
// Yield invoking thread
void MyThreadYield(void)
{
if (rhead == NULL)
{
return;
} else {
printf("cthread addr :%p\n",cthread);
printf("rhead thd addr :%p\n",rhead->thread);
AddToRlist(cthread);
cthread = rhead->thread;
rhead = rhead->next;
printf("rtail thd addr :%p\n",rtail->thread);
printf("cthread addr :%p\n",cthread);
printf("\n before swap\n"); //ff
int ty = swapcontext((rtail->thread)->ctextptr, cthread->ctextptr);
printf("\n after swap ty = %d, cthread tid :%d\n",ty,cthread->tid); //ff
}
}
// Join with a child thread
int MyThreadJoin(MyThread thread)
{
if (cthread->tid != ((struct tnode*)thread)->pid)
{
printf("\n Join Thread not a child of invoking thread, returning -1 \n");
return -1;
}
int check_rlist = scanR(((struct tnode*)thread)->tid);
int check_blist = scanB(((struct tnode*)thread)->tid);
if (check_rlist == 0 && check_blist == 0)
{
printf("\n Join Thread seems to have been terminated, returning -1 \n");
return -1;
}
printf ("\n Join call successful, proceeding with join operation \n");
int wid = ((struct tnode*)thread)->tid;
char w[15];
sprintf(w, "%d", wid);
AddToBlist(cthread,w);
cthread = rhead->thread;
rhead = rhead->next;
printf("\n before swap inside join\n"); //ff
int tj = swapcontext((btail->thread)->ctextptr, cthread->ctextptr);
printf("\n after swap tj = %d, cthread tid :%d\n",tj,cthread->tid); //ff
}
// Join with all children
void MyThreadJoinAll(void)
{
int k; b=0;
for(k=0;k<20;k++)
bacche[k]=0;
int check_rlist = scanRP(cthread->tid);
int check_blist = scanBP(cthread->tid);
if (check_blist == 0 && check_rlist == 0)
{
printf("\n can't find any active children, exiting joinall \n");
return;
}
printf("\n generated bacche array : \t"); //ff
for(k=0;k<20;k++) //ff
printf("%d\t",bacche[k]); //ff
int len;
char s[50]="\0";
for (k=0;k<b;k++)
{
char dig = (char)(((int)'0')+bacche[k]);
len=strlen(s);
s[len]=dig; s[len+1]=' '; s[len+2]='\0';
}
printf("\n generated wid string : [%s] \n",s); //ff
printf("cthread addr :%p\n",cthread);
printf("rhead addr :%p\n",rhead->thread);
AddToBlist(cthread,s);
cthread = rhead->thread;
rhead = rhead->next;
printf("\n before swap inside join all\n"); //ff
printf("btail tid :%d\n",(btail->thread)->tid);
printf("cthread tid :%d\n",cthread->tid);
printf("btail thd addr :%p\n",btail->thread);
printf("cthread addr :%p\n",cthread);
int tj = swapcontext((btail->thread)->ctextptr, cthread->ctextptr);
printf("\n after swap tj = %d, cthread tid :%d\n",tj,cthread->tid); //ff
}
// Terminate invoking thread
void MyThreadExit(void)
{
printf("\n In thread exit \n"); //ff
clearB(cthread->tid); //Move threads blocked on current thread to ready queue
printf("\n clearB done \n"); //ff
printf("\n removing parent (invoking) thread's children \n"); //ff
if (rhead == NULL)
{
printf("\n ready queue is empty, exiting \n"); //ff
//cthread = NULL;
//setcontext (NULL);
} else {
cthread = rhead->thread;
rhead = rhead->next;
printf("cthread tid :%d\n",cthread->tid);
setcontext (cthread->ctextptr);
}
}
// ****** SEMAPHORE OPERATIONS ******
// Create a semaphore
MySemaphore MySemaphoreInit(int initialValue);
// Signal a semaphore
void MySemaphoreSignal(MySemaphore sem);
// Wait on a semaphore
void MySemaphoreWait(MySemaphore sem);
// Destroy on a semaphore
int MySemaphoreDestroy(MySemaphore sem);
// ****** CALLS ONLY FOR UNIX PROCESS ******
// Create and run the "main" thread
void MyThreadInit(void(*start_funct)(void *), void *args)
{
tindex = 1;
cthread = malloc (sizeof(struct tnode));
memset(cthread, 0, sizeof(struct tnode));
cthread->tid = tindex;
cthread->pid = 0;
ucontext_t* ctxmain;
ctxmain = (ucontext_t *) malloc(sizeof(ucontext_t));
getcontext(ctxmain);
char sti[8192];
cthread->ctextptr = (ucontext_t *) malloc(sizeof(ucontext_t));
getcontext(cthread->ctextptr);
cthread->ctextptr->uc_stack.ss_sp = sti;
cthread->ctextptr->uc_stack.ss_size = sizeof sti;
cthread->ctextptr->uc_link = ctxmain;
cthread->ctextptr->uc_stack.ss_flags = 0;
makecontext(cthread->ctextptr, (void (*)(void))start_funct, 1, args);
swapcontext(ctxmain, cthread->ctextptr);
}
Here is the program which uses this library :-
#include <stdio.h>
#include "mythread.h"
int n;
void t1(void * who)
{
int i;
printf("\n checkpoint 2 \n");
printf("\n who: %d \n",(int)who);
//sleep(5);
printf("t%d start\n", (int)who);
for (i = 0; i < n; i++) {
printf("t%d yield\n", (int)who);
printf("\n oogaa \n");
MyThreadYield();
printf("\n boogaa \n");
}
printf("t%d end\n", (int)who);
MyThreadExit();
printf("\n checkpoint 3 \n");
}
void t0(void * dummy)
{
printf("\n dummy: %d \n",(int)dummy);
//sleep(5);
printf("\n checkpoint 1 \n");
MyThreadCreate(t1, (void *)1);
printf(" hello 6\n");
t1(0);
printf("\n checkpoint 4 \n");
}
int main(int argc, char *argv[])
{
if (argc != 2)
return -1;
n = atoi(argv[1]);
MyThreadInit(t0, (void*)7);
printf("\n checkpoint 5 \n");
}
And here is the output for the program with argument n=2 passed :-
eos$ ./ping.exe 2
dummy: 7
checkpoint 1
hello 6
checkpoint 2
who: 0
t0 start
t0 yield
oogaa
cthread addr :0x1151010
rhead thd addr :0x1151790
rtail thd addr :0x1151010
cthread addr :0x1151790
before swap
checkpoint 2
who: 1
t1 start
t1 yield
oogaa
cthread addr :0x1151790
rhead thd addr :0x1151010
rtail thd addr :0x1151790
cthread addr :0x1151010
before swap
after swap ty = 0, cthread tid :1
boogaa
t0 yield
oogaa
cthread addr :0x1151010
rhead thd addr :0x1151790
rtail thd addr :0x1151010
cthread addr :0x1151790
before swap
after swap ty = 0, cthread tid :2
Segmentation fault (core dumped)
As you can see above, my debug messages indicate that addresses are properly exchanged, so I'm not able to figure out the reason for segfault.
Tried debugging it using gdb, but I'm at my wits end and still without any clue.
Any help would be highly appreciated!
回答1:
I was able to solve this through a friend's help. Very stupid and trivial mistake by me. I was trying to allocate memory to context via a local stack which probably gets killed and causes problems. See commented lines below :-
//char sti[8192];
//cthread->ctextptr->uc_stack.ss_sp = sti;
//cthread->ctextptr->uc_stack.ss_size = sizeof sti;
On changing above lines to the following and using direct allocation, my code ran fine.
cthread->ctextptr->uc_stack.ss_sp = malloc(8192);
cthread->ctextptr->uc_stack.ss_size = 8192;
来源:https://stackoverflow.com/questions/25841144/seg-fault-when-returning-to-function-execution-after-successful-swapcontext