转自:http://blog.csdn.net/cyberlabs/article/details/6920138

使用makecontext实现用户线程

       现代Unix系统都在ucontext.h中提供用于上下文切换的函数,这些函数有getcontext, setcontext,swapcontext 和makecontext。其中,getcontext用于保存当前上下文,setcontext用于切换上下文,swapcontext会保存当前上下文并切换到另一个上下文,makecontext创建一个新的上下文。实现用户线程的过程是:我们首先调用getcontext获得当前上下文,然后修改ucontext_t指定新的上下文。同样的,我们需要开辟栈空间,但是这次实现的线程库要涉及栈生长的方向。然后我们调用makecontext切换上下文,并指定用户线程中要执行的函数。
在这种实现中还有一个挑战,即一个线程必须可以主动让出CPU给其它线程。swapcontext函数可以完成这个任务,图4展示了一个这种实现的样例程序,child线程和parent线程不断切换以达到多线程的效果。在libfiber-uc.c文件中可以看到完整的实现。 #include
#include
#include // 64kB stack
#define FIBER_STACK 1024*64 ucontext_t child, parent; // The child thread will execute this function
void threadFunction()
{
printf( "Child fiber yielding to parent" );
swapcontext( &child, &parent );
printf( "Child thread exiting\n" );
swapcontext( &child, &parent );
} int main()
{
// Get the current execution context
getcontext( &child ); // Modify the context to a new stack
child.uc_link = ;
child.uc_stack.ss_sp = malloc( FIBER_STACK );
child.uc_stack.ss_size = FIBER_STACK;
child.uc_stack.ss_flags = ;
if ( child.uc_stack.ss_sp == )
{
perror( "malloc: Could not allocate stack" );
exit( );
} // Create the new context
printf( "Creating child fiber\n" );
makecontext( &child, &threadFunction, ); // Execute the child context
printf( "Switching to child fiber\n" );
swapcontext( &parent, &child );
printf( "Switching to child fiber again\n" );
swapcontext( &parent, &child ); // Free the stack
free( child.uc_stack.ss_sp ); printf( "Child fiber returned and stack freed\n" ); return ;
}
图4用makecontext实现线程 用户级线程的抢占 抢占实现的一个最重要的因素就是定时触发的计时器中断,它的存在使得我们能够中断当前程序的执行,异步对进程的时间片消耗情况进行统计,并在必要的时候(可能是时间片耗尽,也可能是一个高优先级的程序就绪)从当前进程调度到其它进程去执行。 对于用户空间程序来说,与内核空间的中断相对应的就是信号,它和中断一样都是异步触发,并能引起执行流的跳转。所以要想实现用户级线程的抢占,我们可以借助定时器信号(SIGALRM),必要时在信号处理程序内部进行上下文的切换。 为了验证自己的想法,我在上篇文章提到的协同多线程的基础上加上了相关抢占代码,具体实现如下: #include <stdlib.h>
#include <stdio.h>
#include <ucontext.h>
#include <sys/time.h> #define STACK_SIZE 4096
#define UTHREAD_MAX_NUM 256
#define INIT_TICKS 10 typedef int uthread_t;
typedef void uthread_attr_t;
uthread_t current = ; #define uthread_self() current struct uthread_struct
{
int used;
ucontext_t context;
char stack[STACK_SIZE];
void* (*func)(void *arg);
void *arg;
void *exit_status;
int ticks;
}; static struct uthread_struct uthread_slots[UTHREAD_MAX_NUM]; void panic(void)
{
fprintf(stderr, "Panic, bala bala...\n");
exit(EXIT_FAILURE);
} void idle_thread(void)
{
int i; for (i = ; i < UTHREAD_MAX_NUM; i ++)
if (uthread_slots[i].used)
break; if (i == UTHREAD_MAX_NUM)
panic();
if (current != )
uthread_slots[current].used = ;
current = i;
swapcontext(&uthread_slots[].context,&uthread_slots[current].context);
} void uthread_context_init(int tid)
{
getcontext(&uthread_slots[tid].context);
uthread_slots[tid].context.uc_stack.ss_sp = uthread_slots[tid].stack;
uthread_slots[tid].context.uc_stack.ss_size =sizeof(uthread_slots[tid].stack);
uthread_slots[tid].context.uc_link = &uthread_slots[].context;
} void uthread_init(void)
{
uthread_context_init();
uthread_slots[].used = ;
makecontext(&uthread_slots[].context, idle_thread, );
} void uthread_schedule(void); void uthread_exit(void *exit_status)
{
uthread_slots[current].exit_status = exit_status;
uthread_slots[current].used = ;
uthread_schedule();
} void uthread_helper(void)
{
uthread_exit(uthread_slots[current].func(uthread_slots[current].arg));
} int uthread_create(uthread_t *thread, const uthread_attr_t *attr,
void* (*start_routine)(void*), void *arg)
{
static int last_used = ;
int i; for (i = (last_used + ) % UTHREAD_MAX_NUM; i != last_used;
i = (i + ) % UTHREAD_MAX_NUM)
if (!uthread_slots[i].used)
break;
if (i == last_used)
return -;
last_used = i; if (thread != NULL)
*thread = i;
uthread_context_init(i);
uthread_slots[i].used = ;
uthread_slots[i].func = start_routine;
uthread_slots[i].arg = arg;
uthread_slots[i].exit_status = ;
uthread_slots[i].ticks = uthread_slots[current].ticks / ;
uthread_slots[current].ticks -= uthread_slots[i].ticks;
makecontext(&uthread_slots[i].context, uthread_helper, ); return ;
} void uthread_schedule(void)
{
int i, prev; for (i = (current + ) % UTHREAD_MAX_NUM; i != current;
i = (i + ) % UTHREAD_MAX_NUM)
if (uthread_slots[i].used)
break;
if (i == current)
panic(); prev = current;
current = i;
swapcontext(&uthread_slots[prev].context,&uthread_slots[current].context);
} void* thread(void *arg)
{
int i; for (i = ; ; i ++) {
if (i % == )
printf("thread/%d(%s): i = %d\n", current, (char*)arg,i);
uthread_create(NULL, NULL, thread, arg);
if (i % == )
uthread_schedule();
}
} void sig_ticks_timer(int signo)
{
if (--uthread_slots[current].ticks <= ) {
uthread_slots[current].ticks = INIT_TICKS;
uthread_schedule();
}
} int main(int argc, char *argv[])
{
uthread_t tid;
struct itimerval ticks_timer; uthread_init(); uthread_create(&tid, NULL, thread, "hw1");
printf("tid is %d\n", tid);
uthread_create(&tid, NULL, thread, "hw2");
printf("tid is %d\n", tid); signal(SIGALRM, sig_ticks_timer);
ticks_timer.it_interval.tv_sec = ;
ticks_timer.it_interval.tv_usec = ;
ticks_timer.it_value.tv_sec = ;
ticks_timer.it_value.tv_usec = ;
setitimer(ITIMER_REAL, &ticks_timer, NULL); while ()
idle_thread(); return ;
} 似乎该有的都有了,也许真的可以在用户空间实现一个虚拟的操作系统环境玩呢...

使用makecontext实现用户线程【转】的更多相关文章

  1. Java用户线程和守护线程

    今天看Java一个关于多线程返回值方式的示例,发现一个自己不太能理解的问题,就是在主线程中启动了几个工作线程,主线程中也没有join,工作线程居然也是正常输出了回调的结果.这个跟linux C++下的 ...

  2. java中线程分两种,守护线程和用户线程。

    java中线程分为两种类型:用户线程和守护线程. 通过Thread.setDaemon(false)设置为用户线程: 通过Thread.setDaemon(true)设置为守护线程. 如果不设置次属性 ...

  3. MySQL 用户连接与用户线程

    本文转载自公众号数据库随笔,作者happypig 微信看起来麻烦 pig已经好长一段时间没有分享文章了,有点对不起订阅的朋友.最近在做比较复杂跟困难的事情,也并不一定最终会有成果,因此必须对此沉默. ...

  4. JVM内的守护线程Deamon与用户线程User Thread

    转载请注明原文地址:http://www.cnblogs.com/ygj0930/p/6561771.html  一:守护线程Daemon 守护线程:Daemon在希腊神话中解作“守护神”,顾名思义就 ...

  5. 【java多线程】用户线程和守护线程的区别

    java中线程分为两种类型:用户线程和守护线程.通过Thread.setDaemon(false)设置为用户线程:通过Thread.setDaemon(true)设置为守护线程.如果不设置次属性,默认 ...

  6. 用户线程 (User Thread)、守护线程 (Daemon Thread)

    在Java中有两类线程:用户线程 (User Thread).守护线程 (Daemon Thread). 所谓守护 线程,是指在程序运行的时候在后台提供一种通用服务的线程,比如垃圾回收线程就是一个很称 ...

  7. (转)Linux内核本身和进程的区别 内核线程、用户进程、用户线程

    转自:http://blog.csdn.net/adudurant/article/details/23135661 这个概念是很多人都混淆的了,我也是,刚开始无法理解OS时,把Linux内核也当做一 ...

  8. java高并发系列 - 第9天:用户线程和守护线程

    守护线程是一种特殊的线程,在后台默默地完成一些系统性的服务,比如垃圾回收线程.JIT线程都是守护线程.与之对应的是用户线程,用户线程可以理解为是系统的工作线程,它会完成这个程序需要完成的业务操作.如果 ...

  9. java 用户线程和守护线程

    在Java中通常有两种线程:用户线程和守护线程(也被称为服务线程)通过Thread.setDaemon(false)设置为用户线程通过Thread.setDaemon(true)设置为守护线程线程属性 ...

随机推荐

  1. 【python】 可迭代对象、迭代器、生成器

    可迭代对象 iterable 可直接作用于for循环的对象统称为可迭代对象. 有 list. dict.tuple.set.str等数据类型,还有 generator(包括生成器和带yield的gen ...

  2. 【bzoj4903/uoj300】[CTSC2017]吉夫特 数论+状压dp

    题目描述 给出一个长度为 $n$ 的序列,求所有长度大于等于2的子序列个数,满足:对于子序列中任意两个相邻的数 $a$ 和 $b$ ($a$ 在 $b$ 前面),${a\choose b}\mod 2 ...

  3. [Leetcode] maximun subarray 最大子数组

    Find the contiguous subarray within an array (containing at least one number) which has the largest ...

  4. HDU.1285 确定比赛名次 (拓扑排序 TopSort)

    HDU.1285 确定比赛名次 (拓扑排序 TopSort) 题意分析 裸的拓扑排序 详解请移步 算法学习 拓扑排序(TopSort) 只不过这道的额外要求是,输出字典序最小的那组解.那么解决方案就是 ...

  5. HDOJ.1113 Word Amalgamation(map)

    Word Amalgamation 点我挑战题目 点我一起学习STL-MAP 题意分析 给出字典.之后给出一系列======乱序======单词,要求你查字典,如过这个乱序单词对用有多个有序单词可以输 ...

  6. AOJ.592 神奇的叶子

    神奇的叶子 Time Limit: 1000 ms Case Time Limit: 1000 ms Memory Limit: 64 MB Total Submission: 920 Submiss ...

  7. 常见的shell命令总结

    1.查看一个程序是否运行   ps –ef|grep tomcat 查看所有有关tomcat的进程 2.终止线程   kill -9 2222  3.查看文件,包含隐藏文件   ls -al 4.当前 ...

  8. js正则:两边字符固定,中间任意字符

    求些一个js正则!两边字符固定,中间任意字符.在一个长字符串里面匹配一小段,这一小段字符串开头和结尾都是固定的字符,就是中间是任意长度的字符.怎么写? /aa.+aa/ aa是你的固定字符,如果是反斜 ...

  9. 淘淘相关DTO

    result 用于Controller层返回值或Controller于service层之间返回值 package com.taotao.common.pojo; import java.util.Li ...

  10. 一维的Haar小波变换

    小波变换的基本思想是用一组小波函数或者基函数表示一个函数或者信号,例如图像信号.为了理解什么是小波变换,下面用一个具体的例子来说明小波变换的过程. 1. 求有限信号的均值和差值 [例] 假设有一幅分辨 ...