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->sec next;
}
}
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;idata)->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;idata)->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语言实现用户态线程库案例就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持毛票票。