轮子年年有人造,我们也来凑热闹,参考协程实现,大概有以下几种方法:
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,只能安全地支持在线程函数里头纯计算,其他的行为非常可能引发bus error和segmentation fault。(要更加严谨地研究用户态线程库,请去看gnu pth的实现代码)
thread.h
#pragma once #include#include #include #include #include #include #include #define JMP(r) asm volatile ( "pushl %3nt" "popfdnt" "movl %2, %%ebpnt" "movl %0, %%espnt" "jmp *%1nt" : : "m"(r._esp),"m"(r._eip),"m"(r._ebp),"m"(r._eflags) : ) #define SAVE() asm volatile ( "movl %%eax, %0nt" "movl %%ecx, %1nt" "movl %%edx, %2nt" "movl %%ebx, %3nt" "movl %%esp, %4nt" "movl %%ebp, %5nt" "movl %%esi, %6nt" "movl %%edi, %7nt" "pushfdnt" "movl (%%esp), %%eaxnt" "movl %%eax, %8nt" "popfdnt" : "=m"(_eax),"=m"(_ecx),"=m"(_edx),"=m"(_ebx) ,"=m"(_esp),"=m"(_ebp) , "=m"(_esi),"=m"(_edi),"=m"(_eflags) : : "%eax" ) #define RESTORE(r) asm volatile ( "movl %0, %%eaxnt" "movl %1, %%ecxnt" "movl %1, %%edxnt" "movl %3, %%ebxnt" "movl %4, %%esint" "movl %5, %%edint" : :"m"(r._eax),"m"(r._ecx),"m"(r._edx),"m"(r._ebx) , "m"(r._esi),"m"(r._edi) ) typedef void Func(int); struct __timer { void *next; unsigned int sec; unsigned int intersec; int id; Func *sigactor; }; typedef struct alarm *Timer; struct alarm { union{ struct { Timer next; unsigned int sec; }; struct __timer __inner; }; }; typedef struct list *Header; struct list { Timer head; }; typedef struct __thread_table_regs Regs; struct __thread_table_regs { int _edi; int _esi; int _ebp; int _esp; int _ebx; int _edx; int _ecx; int _eax; int _eip; int _eflags; }; typedef struct __ez_thread Thread_t; struct __ez_thread { Regs regs; int tid; sigset_t sigmask; unsigned int priority; int tick; int state; int errno; unsigned int stacktop; unsigned int stacksize; void *stack; void *retval; volatile int __reenter; }; typedef struct __pnode pNode; struct __pnode { pNode *next; pNode *prev; Thread_t *data; }; typedef struct __loopcursor Cursor; struct __loopcursor { int total; pNode *current; }; typedef struct __stack *Stack_t; struct __stack { int __pad[4096]; }; void switch_to(int); extern Header hdr_ptr; extern Cursor live; extern Cursor dead; extern Thread_t pmain;
thread.c
#include "thread.h"
struct list linkedlist;
Header hdr_ptr = &linkedlist;
Timer mallocTimer(int id, Func *actor,unsigned int sec, unsigned int interval)
{
Timer ret = (Timer)malloc(sizeof(struct alarm));
assert(ret);
ret->__inner.id = id;
ret->__inner.sigactor = actor;
ret->__inner.intersec = interval;
ret->sec = sec;
return ret;
}
Timer findTimerPrev(Header h, int id)
{
assert(h);
if(h->head == NULL)
return NULL;
Timer t = h->head;
Timer prev = NULL;
while(t)
{
if(t->__inner.id == id){
if(prev == NULL)
return (Timer)-1;
else
return prev;
}
prev = t;
t = t->next;
}
return NULL;
}
void delTimer(Header h, Timer t)
{
assert(h);
assert(t);
Timer prevtodel = findTimerPrev(h, t->__inner.id);
unsigned int base = 0;
if(prevtodel)
{
if(prevtodel == (Timer)-1){
unsigned int res = (h->head)->sec;
if(res != 0)
{
base = res;
}
else
{
kill(getpid(),SIGALRM);
return;
}
h->head = (h->head)->next;
Timer tmp = (h->head);
while(tmp){
tmp->sec += base;
tmp = tmp->next;
}
return;
}
else
{
base = (prevtodel->next)->sec;
prevtodel->next = (prevtodel->next)->next;
Timer tmp = (prevtodel->next);
while(tmp){
tmp->sec += base;
tmp = tmp->next;
}
return;
}
}
return;
}
int appendTimer(Header h, Timer t)
{
assert(h);
assert(t);
delTimer(h, t);
if(h->head == NULL)
{
h->head = t;
return 0;
}
Timer tmp = h->head;
Timer prev = NULL;
unsigned int prevbase = 0;
unsigned int base = 0;
while(tmp)
{
prevbase = base;
base += tmp->sec;
if(t->sec < base){
break;
}
else{
prev = tmp;
tmp = tmp->next;
}
}
if(prev == NULL)
{
(h->head)->sec -= t->sec;
t->next = h->head;
h->head = t;
return 0;
}
if(tmp == NULL)
t->sec -=base;
else
t->sec -=prevbase;
prev->next = t;
t->next = tmp;
if(tmp)
tmp->sec -= t->sec;
return 0;
}
Func* popTimer(Header h)
{
assert(h);
if(h->head == NULL)
return (Func *)-1;
Func *ret = (h->head)->__inner.sigactor;
Timer todel = h->head;
h->head = (h->head)->next;
// if its intersec greater than 0, we append it right away to the linked list
if(todel->__inner.intersec > 0)
{
todel->sec = todel->__inner.intersec;
appendTimer(h, todel);
}
return ret;
}
void printList(Header h)
{
assert(h);
if(h->head == NULL)
return;
Timer tmp = h->head;
while(tmp)
{
printf("timer[%d] = %u saved %un", tmp->__inner.id, tmp->sec, tmp->__inner.intersec);
tmp = tmp->next;
}
}
void sig_alarm_internal(int signo)
{
void funcWrapper(int signo, Func *func);
if(hdr_ptr->head == NULL)
return;
Func *recv;
if((recv = popTimer(hdr_ptr)) == (Func *)-1){
funcWrapper(SIGALRM, recv);
}
else
{
// signal ourself if next timer's sec = 0
if(hdr_ptr->head){
((hdr_ptr->head)->sec > 0?alarm((hdr_ptr->head)->sec):kill(getpid(), SIGALRM));
}
funcWrapper(SIGALRM, recv);
}
}
unsigned int Alarm(Header h, Timer mtimer)
{
sigset_t mask;
sigset_t old;
sigemptyset(&mask);
sigaddset(&mask, SIGALRM);
sigprocmask(SIG_BLOCK, &mask, &old);
unsigned int res = 0;
Timer t;
if((t = findTimerPrev(h, mtimer->__inner.id)) == NULL)
goto LL;
t = h->head;
while(t)
{
res += t->sec; // it's not precise, we should use alarm(0) for the first sec.
// However, its simple enough to implement.
if(t->__inner.id == mtimer->__inner.id)
break;
t = t->next;
}
LL:
if(mtimer->sec == 0)
{
delTimer(h, mtimer);
sigprocmask(SIG_SETMASK, &old, NULL);
return res;
}
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);
return res;
}
void initTimer()
{
struct sigaction act;
act.sa_handler = sig_alarm_internal;
act.sa_flags = SA_RESTART|SA_NODEFER;
sigemptyset(&act.sa_mask);
sigaction(SIGALRM, &act, NULL);
}
void funcWrapper(int signo, Func *func)
{
sigset_t mask;
sigset_t old;
sigemptyset(&mask);
sigaddset(&mask, SIGALRM);
sigprocmask(SIG_UNBLOCK, &mask, &old);
func(signo);
sigprocmask(SIG_SETMASK, &old, NULL);
}
Cursor live;
Cursor dead;
Thread_t pmain;
void initCursor(Cursor *cur)
{
cur->total = 0;
cur->current = NULL;
}
Thread_t *findThread(Cursor *cur, int tid)
{
sigset_t mask,old;
sigemptyset(&mask);
sigaddset(&mask, SIGALRM);
sigprocmask(SIG_BLOCK, &mask, &old);
int counter = cur->total;
if(counter == 0){
sigprocmask(SIG_SETMASK, &old, NULL);
return NULL;
}
int i;
pNode *tmp = cur->current;
for (int i = 0; i < counter; ++i)
{
if((tmp->data)->tid == tid){
sigprocmask(SIG_SETMASK, &old, NULL);
return tmp->data;
}
tmp = tmp->next;
}
sigprocmask(SIG_SETMASK, &old, NULL);
return NULL;
}
int appendThread(Cursor *cur, Thread_t *pth)
{
sigset_t mask,old;
sigemptyset(&mask);
sigaddset(&mask, SIGALRM);
sigprocmask(SIG_BLOCK, &mask, &old);
if(cur->total == 0)
{
//note this never freed for simple implementation
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);
return 0;
}
else
{
#define MAXTHREADS 5
if(cur->total > MAXTHREADS)
{
assert((cur->total == MAXTHREADS));
sigprocmask(SIG_SETMASK, &old, NULL);
return -1;
}
//freed at threadJoin for simple implementation
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);
return 0;
}
}
pNode *deleteThread(Cursor *cur, int tid)
{
sigset_t mask,old;
sigemptyset(&mask);
sigaddset(&mask, SIGALRM);
sigprocmask(SIG_BLOCK, &mask, &old);
int counter = cur->total;
int i;
pNode *tmp = cur->current;
for (int i = 0; i < counter; ++i)
{
if((tmp->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);
return tmp;
}
tmp = tmp->next;
}
sigprocmask(SIG_SETMASK, &old, NULL);
return NULL;
}
void printThread(Thread_t *pth)
{
printf("pth tid: %dn", pth->tid);
printf("pth stack top: %xn", pth->stacktop);
printf("pth stack size: %un", pth->stacksize);
printf("pth state: %dn", pth->state);
printf("pth errno: %dn", pth->errno);
printf("pth retval: %pn", pth->retval);
printf("pth sigmask: %un", pth->sigmask);
printf("pth priority: %dn", pth->priority);
printf("pth tick: %dn", pth->tick);
printf("EFLAGS: %xt", pth->regs._eflags);
printf("EIP: %xt", pth->regs._eip);
printf("EAX: %xt", pth->regs._eax);
printf("ECX: %xn", pth->regs._ecx);
printf("EDX: %xt", pth->regs._edx);
printf("EBX: %xt", pth->regs._ebx);
printf("ESP: %xt", pth->regs._esp);
printf("EBP: %xn", pth->regs._ebp);
printf("ESI: %xt", pth->regs._esi);
printf("EDI: %xn", pth->regs._edi);
}
void printLoop(Cursor *cur)
{
int count = 0;
pNode *tmp = cur->current;
assert(tmp);
do{
printThread(tmp->data);
tmp = tmp->next;
count ++;
}while(tmp != cur->current);
printf("real total: %dn", count);
printf("total record:%dn", cur->total);
assert(count == cur->total);
}
int fetchTID()
{
static int tid;
return ++tid;
}
void real_entry(Thread_t *pth, void *(*start_rtn)(void *), void* args)
{
//printf("in real entry: %pn", start_rtn);
pth->retval = (*start_rtn)(args);
//deleteThread(&live, pth->tid);
//free(pth->stack);
//pth->stack = NULL;
//pth->stacktop = 0;
//pth->stacksize = 0;
#define DETACHED 1
deleteThread(&live, pth->tid);
appendThread(&dead, pth);
if(pth->state == DETACHED)
threadJoin(pth, NULL);
switch_to(-1);
}
int threadCreat(Thread_t **pth, void *(*start_rtn)(void *), void *arg)
{
sigset_t mask,old;
sigemptyset(&mask);
sigaddset(&mask, SIGALRM);
sigprocmask(SIG_BLOCK, &mask, &old);
//freed at threadJoin for simple implementation
*pth = malloc(sizeof(Thread_t));
#define PTHREAD_STACK_MIN 4096
//freed at threadJoin for simple implementation
(*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; // 0 JOINABLE 1 DETACHED
(*pth)->priority = 1; //one seconds
(*pth)->tick = (*pth)->priority;
(*pth)->tid = fetchTID();
sigprocmask(0,NULL,&((*pth)->sigmask));
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);
return 0;
}
int threadJoin(Thread_t *pth, void **rval_ptr)
{
sigset_t mask,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);
return 0;
}
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);
return 0;
}
}
return -1;
}
void init()
{
initTimer();
initCursor(&live);
initCursor(&dead);
appendThread(&live, &pmain);
Alarm(hdr_ptr,mallocTimer(1, switch_to, 1, 1));
}
void switch_to(int signo)
{
sigset_t mask,old;
sigemptyset(&mask);
sigaddset(&mask, SIGALRM);
sigprocmask(SIG_BLOCK, &mask, &old);
Regs regs;
//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;
SAVE();
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);
}
void *sum1tod(void *d)
{
int i, k, j=0;
for (i = 0; i <= (int)d; ++i)
{
j+=i;
}
return ((void *)j);
}
int main(int argc, char const *argv[])
{
int res = 0;
int i;
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("parallel compute: %d = 5050 * 3n", (int)res1+(int)res2+(int)res);
return 0;
}
以上这篇C语言实现用户态线程库案例就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持考高分网。



