成功swapcontext后返回函数执行时出现段错误

时间:2014-09-15 05:05:44

标签: c multithreading context-switch

我试图编写一个库来管理线程使用上下文(getcontext,setcontext,makecontext,swapcontext),没有pthreads。

函数MyThreadYield(),暂停当前线程上下文,将其发送到就绪队列的末尾,并开始在就绪队列的头部执行线程。

在MyThreadYield()中,我能够通过swapcontext()检索暂停线程的上下文,但是当它返回到函数执行时,我得到一个段错误。

例如: - 假设线程1是init线程,它运行并创建线程2.然后它产生。现在线程2运行,然后调用yield。这里swapcontext运行,我可以通过"%p"验证交换是否成功。但是当它试图从MyThreadYield()返回以恢复线程1的函数运行时,我得到了seg错误。

这是我的图书馆代码: -

        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);
        }

以下是使用此库的程序: -

            #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");
            }

以下是传递参数n = 2的程序的输出: -

            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)

如上所示,我的调试消息表明地址已正确交换,因此我无法找出segfault的原因。

尝试使用gdb对它进行调试,但是我在我的智慧结束时仍然没有任何线索。

任何帮助都将受到高度赞赏!

1 个答案:

答案 0 :(得分:3)

我能够通过朋友的帮助解决这个问题。 我非常愚蠢和琐碎的错误。 我试图通过本地堆栈将内存分配给上下文,这可能会被杀死并导致问题。请参阅下面的注释行: -

//char sti[8192];
//cthread->ctextptr->uc_stack.ss_sp = sti;
//cthread->ctextptr->uc_stack.ss_size = sizeof sti;

在将以上行更改为以下内容并使用直接分配时,我的代码运行正常。

cthread->ctextptr->uc_stack.ss_sp = malloc(8192);
cthread->ctextptr->uc_stack.ss_size = 8192;