C语言实现用户态线程库案例
轮子年年有人造,我们也来凑热闹,参考协程实现,大概有以下几种方法:
1)利用setjmp,longjmp
2)利用ucontext接口函数
3)汇编
(线程无非就是多了个抢占功能,由定时器触发,而非自愿让出运行权限)
因为我写的时候还没看到其他帖子,如果看到了,铁定会用最直观的ucontext接口写的(注意,在macOSX中已经标注为废除,头文件得换做sys/ucontext.h),结果就是我用了汇编来写,但是尽量不用汇编来写整个switch_to调度函数(这样有个明显的坏处,那就是用gas/nasm的标准汇编格式写的函数在macOSX下不能编译通过,这个与系统自带的编译工具有关),而用经量少的内嵌汇编来写。switch_to函数参考的是minix操作系统中任务切换函数实现的,用软件时钟器每隔1s发信号以激发switch_to函数切换任务。下面直接贴代码了,对外提供了类似pthread的接口(只有两个,分别是threadCreate和threadJoin)。现在的代码还非常的buggy,只能安全地支持在线程函数里头纯计算,其他的行为非常可能引发buserror和segmentationfault。(要更加严谨地研究用户态线程库,请去看gnupth的实现代码)
thread.h
#pragmaonce #include#include #include #include #include #include #include #defineJMP(r)asmvolatile\ ("pushl%3\n\t"\ "popfd\n\t"\ "movl%2,%%ebp\n\t"\ "movl%0,%%esp\n\t"\ "jmp*%1\n\t"\ :\ :"m"(r._esp),"m"(r._eip),"m"(r._ebp),"m"(r._eflags)\ :\ ) #defineSAVE()asmvolatile\ ("movl%%eax,%0\n\t"\ "movl%%ecx,%1\n\t"\ "movl%%edx,%2\n\t"\ "movl%%ebx,%3\n\t"\ "movl%%esp,%4\n\t"\ "movl%%ebp,%5\n\t"\ "movl%%esi,%6\n\t"\ "movl%%edi,%7\n\t"\ "pushfd\n\t"\ "movl(%%esp),%%eax\n\t"\ "movl%%eax,%8\n\t"\ "popfd\n\t"\ :"=m"(_eax),"=m"(_ecx),"=m"(_edx),"=m"(_ebx)\ ,"=m"(_esp),"=m"(_ebp)\ ,"=m"(_esi),"=m"(_edi),"=m"(_eflags)\ :\ :"%eax"\ ) #defineRESTORE(r)asmvolatile\ ("movl%0,%%eax\n\t"\ "movl%1,%%ecx\n\t"\ "movl%1,%%edx\n\t"\ "movl%3,%%ebx\n\t"\ "movl%4,%%esi\n\t"\ "movl%5,%%edi\n\t"\ :\ :"m"(r._eax),"m"(r._ecx),"m"(r._edx),"m"(r._ebx)\ ,"m"(r._esi),"m"(r._edi)\ ) typedefvoidFunc(int); /*__timerstructistherealTimerstructweuse *idisuniquetoeachtimer *intersecistheintevalsecondstoeachsignalforwardingthethisTimer *sigactoristhehandlerforthisTimer *nextisainternalmemberusedforlinkedlist */ struct__timer { void*next; unsignedintsec; unsignedintintersec; intid; Func*sigactor; }; /*structalarmisuglyforthecompatibilitywithearlystruct. *Ishouldhaveusedunnamedmemberinsteadof__inner. */ typedefstructalarm*Timer; structalarm { union{ struct { Timernext; unsignedintsec; }; struct__timer__inner; }; }; typedefstructlist*Header; structlist { Timerhead; }; typedefstruct__thread_table_regsRegs; struct__thread_table_regs { int_edi; int_esi; int_ebp; int_esp; int_ebx; int_edx; int_ecx; int_eax; int_eip; int_eflags; }; typedefstruct__ez_threadThread_t; struct__ez_thread { Regsregs; inttid; sigset_tsigmask; unsignedintpriority; inttick; intstate; interrno; unsignedintstacktop; unsignedintstacksize; void*stack; void*retval; volatileint__reenter; }; typedefstruct__pnodepNode; struct__pnode { pNode*next; pNode*prev; Thread_t*data; }; typedefstruct__loopcursorCursor; struct__loopcursor { inttotal; pNode*current; }; typedefstruct__stack*Stack_t; struct__stack { int__pad[4096]; }; voidswitch_to(int); externHeaderhdr_ptr; externCursorlive; externCursordead; externThread_tpmain;
thread.c
/*MITLicense Copyright(c)2017Yuandong-Chen Permissionisherebygranted,freeofcharge,toanypersonobtainingacopy ofthissoftwareandassociateddocumentationfiles(the"Software"),todeal intheSoftwarewithoutrestriction,includingwithoutlimitationtherights touse,copy,modify,merge,publish,distribute,sublicense,and/orsell copiesoftheSoftware,andtopermitpersonstowhomtheSoftwareis furnishedtodoso,subjecttothefollowingconditions: Theabovecopyrightnoticeandthispermissionnoticeshallbeincludedinall copiesorsubstantialportionsoftheSoftware. THESOFTWAREISPROVIDED"ASIS",WITHOUTWARRANTYOFANYKIND,EXPRESSOR IMPLIED,INCLUDINGBUTNOTLIMITEDTOTHEWARRANTIESOFMERCHANTABILITY, FITNESSFORAPARTICULARPURPOSEANDNONINFRINGEMENT.INNOEVENTSHALLTHE AUTHORSORCOPYRIGHTHOLDERSBELIABLEFORANYCLAIM,DAMAGESOROTHER LIABILITY,WHETHERINANACTIONOFCONTRACT,TORTOROTHERWISE,ARISINGFROM, OUTOFORINCONNECTIONWITHTHESOFTWAREORTHEUSEOROTHERDEALINGSINTHE SOFTWARE.*/ #include"thread.h" /*************************Alarmfacility*************************/ structlistlinkedlist; Headerhdr_ptr=&linkedlist; TimermallocTimer(intid,Func*actor,unsignedintsec,unsignedintinterval) { Timerret=(Timer)malloc(sizeof(structalarm)); assert(ret); ret->__inner.id=id; ret->__inner.sigactor=actor; ret->__inner.intersec=interval; ret->sec=sec; returnret; } /*findTimerinlinkedlistwhichidisid. *return:returnNULLifnotfound,-1ifit'sheaderlink, *otherwiseprevwhichisthepreviousTimermembertothisTimer */ TimerfindTimerPrev(Headerh,intid) { assert(h); if(h->head==NULL) returnNULL; Timert=h->head; Timerprev=NULL; while(t) { if(t->__inner.id==id){ if(prev==NULL) return(Timer)-1; else returnprev; } prev=t; t=t->next; } returnNULL; } /*deleteTimerinlinkedlist. *return:nothing,weensurethistisdeletedinthelinkedlist. */ voiddelTimer(Headerh,Timert) { assert(h); assert(t); Timerprevtodel=findTimerPrev(h,t->__inner.id); unsignedintbase=0; if(prevtodel) { if(prevtodel==(Timer)-1){ unsignedintres=(h->head)->sec; if(res!=0) { base=res; } else { kill(getpid(),SIGALRM); return; } h->head=(h->head)->next; Timertmp=(h->head); while(tmp){ tmp->sec+=base; tmp=tmp->next; } return; } else { base=(prevtodel->next)->sec; prevtodel->next=(prevtodel->next)->next; Timertmp=(prevtodel->next); while(tmp){ tmp->sec+=base; tmp=tmp->next; } return; } } return; } /*appendTimerinappropriateplaceinlinkedlist. *theappropriateplacemeansalltimersinlinkedlistarearranged *accordingtheirnextalarmseconds. *ThealgorithmweusehereisthattherealleftalarmsecondsforthisTimer *isthesumofallthesecmemberinTimerinlinkedlistprevtothisTimer *plusitssecmember.Forexample,weadd3Timerstothelinkedlist, *whosesecare4,3,2respectively.Thenthelinkedlistlookslike: *2(realsec=2)-->1(realsec=2+1=3)-->1(realsec=2+1+1=4) *Theadvantageisobviously,wedontneedtorememberhowmanysecondspassed. *Wealwaysfetchtheheadertorespondthealarmsignalandsetnextalarmsec *asthenexttimerinthelinkedlist.(Therealsituationisalittlebitmore *complex,forexampleifupcomingtimers'secequals0,weneedtocalltheir *handlerrightawayalltogetherinacertainsequence.Ifitsintersecisnot *zero,weneedtoappendittothelinkedlistagainasquickaspossible) *note:delTimeralsoaddressthisproblem.IfwedeleteanyTimer,weneedto *recalculatethesecsafterthistimerinthelinkedlist.(simplytoaddsecto *thenexttimeranddeletethistimernode) *return:only0ifsuccess,otherwisetheholeprocessfailed. */ intappendTimer(Headerh,Timert) { assert(h); assert(t); delTimer(h,t); if(h->head==NULL) { h->head=t; return0; } Timertmp=h->head; Timerprev=NULL; unsignedintprevbase=0; unsignedintbase=0; while(tmp) { prevbase=base; base+=tmp->sec; if(t->secnext; } } if(prev==NULL) { (h->head)->sec-=t->sec; t->next=h->head; h->head=t; return0; } if(tmp==NULL) t->sec-=base; else t->sec-=prevbase; prev->next=t; t->next=tmp; if(tmp) tmp->sec-=t->sec; return0; } /*popheadertimerinlinkedlist. *return:itshander */ Func*popTimer(Headerh) { assert(h); if(h->head==NULL) return(Func*)-1; Func*ret=(h->head)->__inner.sigactor; Timertodel=h->head; h->head=(h->head)->next; //ifitsintersecgreaterthan0,weappenditrightawaytothelinkedlist if(todel->__inner.intersec>0) { todel->sec=todel->__inner.intersec; appendTimer(h,todel); } returnret; } voidprintList(Headerh) { assert(h); if(h->head==NULL) return; Timertmp=h->head; while(tmp) { printf("timer[%d]=%usaved%u\n",tmp->__inner.id,tmp->sec,tmp->__inner.intersec); tmp=tmp->next; } } /*it'stherealsignalhandlerrespondingtoeverySIGALRM. */ voidsig_alarm_internal(intsigno) { voidfuncWrapper(intsigno,Func*func); if(hdr_ptr->head==NULL) return; Func*recv; if((recv=popTimer(hdr_ptr))==(Func*)-1){ funcWrapper(SIGALRM,recv); } else { //signalourselfifnexttimer'ssec=0 if(hdr_ptr->head){ ((hdr_ptr->head)->sec>0?alarm((hdr_ptr->head)->sec):kill(getpid(),SIGALRM)); } funcWrapper(SIGALRM,recv); } } /*Alarmfunctionsimulatesnativealarmfunction. *whatifSIGALRMarriveswhenprocessisrunninginAlarm? *wejustblockthesignalsincethereisnoslowfunctioninAlarm, *sig_alarm_internalwillforsureaddressthesignalverysoon. */ unsignedintAlarm(Headerh,Timermtimer) { sigset_tmask; sigset_told; sigemptyset(&mask); sigaddset(&mask,SIGALRM); sigprocmask(SIG_BLOCK,&mask,&old); unsignedintres=0; Timert; if((t=findTimerPrev(h,mtimer->__inner.id))==NULL) gotoLL; t=h->head; while(t) { res+=t->sec;//it'snotprecise,weshouldusealarm(0)forthefirstsec. //However,itssimpleenoughtoimplement. if(t->__inner.id==mtimer->__inner.id) break; t=t->next; } LL: if(mtimer->sec==0) { delTimer(h,mtimer); sigprocmask(SIG_SETMASK,&old,NULL); returnres; } appendTimer(h,mtimer); if(mtimer->__inner.id==(h->head)->__inner.id) ((h->head)->sec>0?alarm((h->head)->sec):kill(getpid(),SIGALRM)); sigprocmask(SIG_SETMASK,&old,NULL); returnres; } voidinitTimer() { structsigactionact; act.sa_handler=sig_alarm_internal; act.sa_flags=SA_RESTART|SA_NODEFER; sigemptyset(&act.sa_mask); sigaction(SIGALRM,&act,NULL); } voidfuncWrapper(intsigno,Func*func) { sigset_tmask; sigset_told; sigemptyset(&mask); sigaddset(&mask,SIGALRM); sigprocmask(SIG_UNBLOCK,&mask,&old); func(signo); sigprocmask(SIG_SETMASK,&old,NULL); } /*************************Threadfacility*************************/ Cursorlive; Cursordead; Thread_tpmain; voidinitCursor(Cursor*cur) { cur->total=0; cur->current=NULL; } Thread_t*findThread(Cursor*cur,inttid) { sigset_tmask,old; sigemptyset(&mask); sigaddset(&mask,SIGALRM); sigprocmask(SIG_BLOCK,&mask,&old); intcounter=cur->total; if(counter==0){ sigprocmask(SIG_SETMASK,&old,NULL); returnNULL; } inti; pNode*tmp=cur->current; for(inti=0;i data)->tid==tid){ sigprocmask(SIG_SETMASK,&old,NULL); returntmp->data; } tmp=tmp->next; } sigprocmask(SIG_SETMASK,&old,NULL); returnNULL; } intappendThread(Cursor*cur,Thread_t*pth) { sigset_tmask,old; sigemptyset(&mask); sigaddset(&mask,SIGALRM); sigprocmask(SIG_BLOCK,&mask,&old); if(cur->total==0) { //notethisneverfreedforsimpleimplementation cur->current=(pNode*)malloc(sizeof(pNode)); assert(cur->current); (cur->current)->data=pth; (cur->current)->prev=cur->current; (cur->current)->next=cur->current; cur->total++; sigprocmask(SIG_SETMASK,&old,NULL); return0; } else { #defineMAXTHREADS5 if(cur->total>MAXTHREADS) { assert((cur->total==MAXTHREADS)); sigprocmask(SIG_SETMASK,&old,NULL); return-1; } //freedatthreadJoinforsimpleimplementation pNode*tmp=malloc(sizeof(pNode)); assert(tmp); tmp->data=pth; tmp->prev=cur->current; tmp->next=(cur->current)->next; ((cur->current)->next)->prev=tmp; (cur->current)->next=tmp; cur->total++; sigprocmask(SIG_SETMASK,&old,NULL); return0; } } pNode*deleteThread(Cursor*cur,inttid) { sigset_tmask,old; sigemptyset(&mask); sigaddset(&mask,SIGALRM); sigprocmask(SIG_BLOCK,&mask,&old); intcounter=cur->total; inti; pNode*tmp=cur->current; for(inti=0;i data)->tid==tid){ (tmp->prev)->next=tmp->next; (tmp->next)->prev=tmp->prev; if(tmp==cur->current) { cur->current=cur->current->next; } //free(tmp); cur->total--; assert(cur->total); sigprocmask(SIG_SETMASK,&old,NULL); returntmp; } tmp=tmp->next; } sigprocmask(SIG_SETMASK,&old,NULL); returnNULL; } voidprintThread(Thread_t*pth) { printf("pthtid:%d\n",pth->tid); printf("pthstacktop:%x\n",pth->stacktop); printf("pthstacksize:%u\n",pth->stacksize); printf("pthstate:%d\n",pth->state); printf("ptherrno:%d\n",pth->errno); printf("pthretval:%p\n",pth->retval); printf("pthsigmask:%u\n",pth->sigmask); printf("pthpriority:%d\n",pth->priority); printf("pthtick:%d\n",pth->tick); printf("EFLAGS:%x\t",pth->regs._eflags); printf("EIP:%x\t",pth->regs._eip); printf("EAX:%x\t",pth->regs._eax); printf("ECX:%x\n",pth->regs._ecx); printf("EDX:%x\t",pth->regs._edx); printf("EBX:%x\t",pth->regs._ebx); printf("ESP:%x\t",pth->regs._esp); printf("EBP:%x\n",pth->regs._ebp); printf("ESI:%x\t",pth->regs._esi); printf("EDI:%x\n",pth->regs._edi); } voidprintLoop(Cursor*cur) { intcount=0; pNode*tmp=cur->current; assert(tmp); do{ printThread(tmp->data); tmp=tmp->next; count++; }while(tmp!=cur->current); printf("realtotal:%d\n",count); printf("totalrecord:%d\n",cur->total); assert(count==cur->total); } intfetchTID() { staticinttid; return++tid; } voidreal_entry(Thread_t*pth,void*(*start_rtn)(void*),void*args) { //printf("inrealentry:%p\n",start_rtn); pth->retval=(*start_rtn)(args); //deleteThread(&live,pth->tid); /*somecleanjobhere*/ //free(pth->stack); //pth->stack=NULL; //pth->stacktop=0; //pth->stacksize=0; #defineDETACHED1 deleteThread(&live,pth->tid); appendThread(&dead,pth); if(pth->state==DETACHED) threadJoin(pth,NULL); switch_to(-1); } intthreadCreat(Thread_t**pth,void*(*start_rtn)(void*),void*arg) { sigset_tmask,old; sigemptyset(&mask); sigaddset(&mask,SIGALRM); sigprocmask(SIG_BLOCK,&mask,&old); //freedatthreadJoinforsimpleimplementation *pth=malloc(sizeof(Thread_t)); #definePTHREAD_STACK_MIN4096 //freedatthreadJoinforsimpleimplementation (*pth)->stack=malloc(PTHREAD_STACK_MIN); assert((*pth)->stack); (*pth)->stacktop=(((int)(*pth)->stack+PTHREAD_STACK_MIN)&(0xfffff000)); (*pth)->stacksize=PTHREAD_STACK_MIN-(((int)(*pth)->stack+PTHREAD_STACK_MIN)-(*pth)->stacktop); (*pth)->state=0;//0JOINABLE1DETACHED (*pth)->priority=1;//oneseconds (*pth)->tick=(*pth)->priority; (*pth)->tid=fetchTID(); sigprocmask(0,NULL,&((*pth)->sigmask)); /*setparams*/ void*dest=(*pth)->stacktop-12; memcpy(dest,pth,4); dest+=4; memcpy(dest,&start_rtn,4); dest+=4; memcpy(dest,&arg,4); (*pth)->regs._eip=&real_entry; (*pth)->regs._esp=(*pth)->stacktop-16; (*pth)->regs._edi=0; (*pth)->regs._esi=0; (*pth)->regs._ebp=0; (*pth)->regs._eax=0; (*pth)->regs._ebx=0; (*pth)->regs._ecx=0; (*pth)->regs._edx=0; (*pth)->regs._eflags=0; appendThread(&live,(*pth)); sigprocmask(SIG_SETMASK,&old,NULL); return0; } intthreadJoin(Thread_t*pth,void**rval_ptr) { sigset_tmask,old; sigemptyset(&mask); sigaddset(&mask,SIGALRM); sigprocmask(SIG_BLOCK,&mask,&old); Thread_t*find1,*find2; find1=findThread(&live,pth->tid); find2=findThread(&dead,pth->tid); if((find1==NULL)&&(find2==NULL)){ sigprocmask(SIG_SETMASK,&old,NULL); return-1; } if(find2){ if(rval_ptr!=NULL) *rval_ptr=find2->retval; sigprocmask(SIG_SETMASK,&old,NULL); return0; } sigprocmask(SIG_SETMASK,&old,NULL); while(1) { if((find2=findThread(&dead,pth->tid))!=NULL){ if(rval_ptr!=NULL) *rval_ptr=find2->retval; pNode*tmp=deleteThread(&dead,pth->tid); free(tmp); free((Stack_t)find2->stack); free(find2); return0; } } return-1; } voidinit() { initTimer(); initCursor(&live); initCursor(&dead); appendThread(&live,&pmain); Alarm(hdr_ptr,mallocTimer(1,switch_to,1,1)); } voidswitch_to(intsigno) { sigset_tmask,old; sigemptyset(&mask); sigaddset(&mask,SIGALRM); sigprocmask(SIG_BLOCK,&mask,&old); Regsregs; //printf(""); if(signo==-1) { regs=live.current->data->regs; sigprocmask(SIG_SETMASK,&old,NULL); JMP(regs); assert(0); } int_edi; int_esi; int_ebp; int_esp; int_ebx; int_edx; int_ecx; int_eax; int_eip=&&_REENTERPOINT; int_eflags; live.current->data->__reenter=0; /*savecurrentcontext*/ SAVE(); /*savecontextincurrentthread*/ live.current->data->regs._eflags=_eflags; live.current->data->regs._eip=_eip; live.current->data->regs._eax=_eax; live.current->data->regs._ecx=_ecx; live.current->data->regs._edx=_edx; live.current->data->regs._ebx=_ebx; live.current->data->regs._esp=_esp; live.current->data->regs._ebp=_ebp; live.current->data->regs._esi=_esi; live.current->data->regs._edi=_edi; if(!live.current->data->__reenter) { goto_END; } _REENTERPOINT: regs=live.current->data->regs; if(live.current->data->__reenter){ live.current->data->__reenter=0; sigprocmask(SIG_SETMASK,&old,NULL); return; } _END: live.current->data->__reenter=1; regs=live.current->next->data->regs; live.current=live.current->next; sigprocmask(SIG_SETMASK,&old,NULL); JMP(regs); assert(0); } /*************************Test*************************/ /** *Note:Theimplementationisreallybugy,rightnowonlysupportcomputeinthread. *EvenstandardI/OinthethreadwillcauseI/Obuserrororsegmentationerrorbecause *allpthread-reentrantfunctionisnotguaranteedinourthreadmodel. *(pthread_mutex_tcannotblockthreadinourmodelcausewemodifyeipdirectly) */ void*sum1tod(void*d) { inti,k,j=0; for(i=0;i<=(int)d;++i) { /*code*/ j+=i; } return((void*)j); } intmain(intargc,charconst*argv[]) { intres=0; inti; init(); Thread_t*tid1,*tid2; int*res1,*res2; threadCreat(&tid1,sum1tod,100); threadCreat(&tid2,sum1tod,100); for(i=0;i<=100;++i){ res+=i; } threadJoin(tid1,&res1); threadJoin(tid2,&res2); printf("parallelcompute:%d=5050*3\n",(int)res1+(int)res2+(int)res); return0; }
以上这篇C语言实现用户态线程库案例就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持毛票票。