`
hai_lei
  • 浏览: 3033 次
  • 性别: Icon_minigender_2
  • 来自: 大连
最近访客 更多访客>>
文章分类
社区版块
存档分类
最新评论

用户态线程库

阅读更多
更轻量的线程 
严格说来, 异步化的事件驱动方式出现的比线程模式更早, 线程的出现本身就是为了减轻复杂的状态机编程。 在一般情况下确实给我们的程序带来了便利. 当时另一方面也带来了不少麻烦 
锁的应用, 需要小心死锁等问题. 另外互斥情况下的性能开销也是不可忽视的 
共享数据的修改和读取很容易出现问题, 而且不好排查,即使有了valgrind 这样工具,很多问题依然是很容易出现 
但另一方面, 这些问题对于上面的提到异步方式也是同样存在的, 本质上还是存在线程间的互斥问题。不过对于这种模式可以采用单进程的方式的来规避其中的一些问题, 但在利用多CPU的问题上还是需要另外的考虑 
前面提到过多线程的本质是记录环境的上下文,保存CPU的状态。 在glibc中提供了makecontext, swapcontext的方式来记录这些信息, 利用这两个调用,我们可以在用户态上实现轻量级的线程库。 
makecontext 创建一个新的上下文,可以传入我们自己定义的栈空间。它记录CPU的各种信息 
swapcontext 切换上下文,其实就是改变CPU的相关寄存器 状态,替换到另外的可执行的上下文中 

一个简单例子: 
 一个简单例子: 
 
C/C++ code
<!-- Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ --> void call_thread(uint32_t p1, uint32_t p2) { ucontext_t* ctx = (ucontext_t*)((uint64_t)p2 | ((uint64_t)p1) << 32); ucontext_t u; //。。。运行线程 swapcontext(&u, ctx); } void thread_mgr() { //设置上下文 ucontext_t ctx, u; //初始化 u u.uc_stack.ss_sp = (char*)stack_buff + pagesize; u.uc_stack.ss_size = thread_stack_size; u.uc_stack.ss_flags = 0; uint32_t p1, p2; //低版本glibc实现问题不支持64bit指针 p1 = (uint32_t)((0x00000000FFFFFFFF)&((uint64_t)(&ctx))&gt;&gt;32); //高位 p2 = (uint32_t)(0x00000000FFFFFFFF&(uint64_t)(&ctx)); //低位 makecontext(&u, (void(*)(void))(&call_thread), 2, p1, p2); swapcontext(&ctx, &u); //记住当前位置,切换到u的位置上, 在 call_thread 中swapcontext切换回来 ... }

通过这样的切换方式我们可以在单线程程序中简单实现一个轻量级的线程库,由线程库负责在可用的调度实体上调度用户线程。这会使得线程上下文切换非常的快,这主要避免了系统调用。在测试中切换的性能是pthread库的切换(从pthread_create到线程运行线程 与 makecontext后的比较) 的10倍以上. 
事实上这是m * n 的线程库的基本实现方式, 不过这里存在几个问题 
单线程运行不能利用多CPU, 需要以来pthread或者clone的多线程实现 或者干脆利用多CPU 
调度必须自己考虑, 没有外部来进行线程的切换。 
一个轻线程出现死锁,或者需要长时间等待操作的时候会影响一批线程 
不过对于这些,我们可以在yield()主动让出CPU的语义上进行扩展, 当轻线程调用到yield(), 就主动进行切换,让另外的轻线程运行。 通过在这个层面上的封装,我们可以实现各种不同的调度方式,比如发起IO操作,非堵塞方式处理没有完全处理完毕,就切换,如果处理完毕就继续运行。 
记录当前的栈状态, 切换出去,然后再切回来, 整个流程类似下面: 
 
C/C++ code
<!-- Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ --> class task { public: virtual int run() { //... //切换, 监控句柄 放入pool中 } } 1 //epoll_wait 等待句柄激活 //激活的调度 if (fd 激活) { //上下文件切换到 fd对应的地方运行

将所有的切换都采用网络句柄或者管道(纯CPU采用管道封装,统一化),这点上和前面的异步事件模型采用类似的方式, 都是通过事件方式进行激活 
事实上这种方式在erlang, ruby等语言中也都有相应的实现, 基本原理就如上面所述,每个运行实例都维护着自己的上下文和栈空间. 这种线程另一种叫法是协程(Coroutine), 本质上是属于一种非抢占式线程 
注:在具体线程实现方式上,也可以利用longjump来实现,性能上来说更好的, 不过这里对于各种状态需要自己记录,并且还需要考虑CPU缓存等问题相对问题比较(全部考虑完就和makecontext差不多了)。所以这里采用的是makecontext的方式。
可控制的异步调用 
在用户态的线程库中,我们可以采用同步思维方式写出等价于异步效果的程序,并且在性能提升方面占有一定的优势. 
一方面可以付出一定的内存开销(异步条件上下文完全通过内存池维护空间会小一些)开辟出更多的线程(几十W级别规模), 另一方面编程模式不会发生太大的改变 
计算异步化 
这里主要是考虑,类似 GPU或者数据压缩卡这样的计算模式, 将一部分的计算拿出去进行计算, CPU可以继续进行其他工作,从而短时间内处理更多的工作。
分享到:
评论

相关推荐

    Unix下用户级线程库

    与内核级线程(Kernel-Level Threads, KLTs)不同,用户级线程的调度和管理全部由应用程序自身或者特定的线程库负责,而不是由操作系统内核直接支持。这个压缩包提供的就是一个简单的用户级线程库实现,对于学习和...

    基于嵌入式Linux系统的内核级线程库的研究与实现.pdf

    【嵌入式Linux系统内核级线程库】 嵌入式Linux系统在当前的应用中扮演着重要的角色,尤其是在性能要求较高的领域。然而,一个普遍存在的问题是,大多数嵌入式Linux应用运行在用户态,这导致频繁地在内核态和用户态...

    多线程库支持库

    总的来说,多线程库是现代编程中不可或缺的一部分,它们简化了多线程编程,帮助开发者充分利用多核处理器的计算能力,提高了程序的效率和用户体验。无论是ethreadnewfne还是GBB的线程支持库,它们都是为了让开发者更...

    系统线程(内核线程)和用户线程区别 - 简书.pdf

    用户线程是用户程序中实现的线程,不需要内核支持,应用进程利用线程库提供创建、同步、调度和管理线程的函数来控制用户线程。用户线程的优点是线程的调度不需要内核直接参与,控制简单,可以在不支持线程的操作系统...

    linux线程库例子

    同时,了解不同线程模型(如用户级线程和内核级线程)的优缺点,以及如何根据应用场景选择合适的线程库。 通过以上内容,我们可以了解到Linux线程库在服务端和客户端编程中的重要性和使用方法。具体的DEVP文件内容...

    11121_操作系统_答案1

    在不支持线程的古老UNIX上编写多线程程序,可以通过实现用户态线程库,线程调度和同步都在用户空间完成,不涉及内核改造。 2. 要满足服务器的需求,可以创建多个文件系统,每个用户主目录对应一个文件系统,邮箱则...

    lab3-171830635俞星凯1

    - 提供用户态线程库,包括pthread_create、pthread_join、pthread_yield、pthread_exit等接口。 - 用户程序需要测试上述库函数,验证其功能正确性。 5. **实验过程**: - 启动阶段,从实模式进入保护模式,加载...

    西电操作系统考试练习+答案

    - 内核级线程由操作系统直接调度,而用户级线程由用户态线程库调度。 - 调度粒度:线程比进程更细,因此可以在相同时间内切换更多的线程。 3. **切换开销**: - 进程切换开销大,因为需要保存和恢复整个上下文...

    MFC 多线程应用, 包括工作线程, 用户界面线程

    多线程可以在CPU空闲时切换执行,提高资源利用率,但同时也带来了线程同步、竞态条件等问题。 2. **MFC中的线程**:MFC通过`CWinThread`类来表示线程,有两种主要类型的线程:工作线程和用户界面线程。 - **工作...

    C语言编写的跨平台线程库

    如果它是源代码,那么用户可以查看并学习如何实现跨平台线程库;如果是库文件,用户可以直接在项目中链接并使用这个库来创建和管理线程。 总结来说,这个C语言编写的跨平台线程库提供了一种标准化的方式来在多种...

    MFC用户界面多线程工程案例

    MFC提供了一些同步对象,如CSemaphore(信号量)、CCriticalSection(临界区)和CEvent(事件)等,用于控制线程的访问顺序和防止竞态条件。在本案例中,如果涉及到用户界面更新,就需要确保只由主线程进行,以免...

    FC线程库模型介绍

    而协程,它是一种用户态的轻量级线程,由程序员在应用中显式调度,与传统操作系统线程相比,它能更加高效地在多个线程之间进行切换,适合于需要大量并发操作的场景。 在FC线程模型中,上下文(context)是一个重要...

    线程c语言封装库

    在实际开发中,线程库的应用非常广泛,例如在服务器端处理多个客户端请求、在图形用户界面中实现异步更新、在科学计算中并行处理大量数据等场景。这个库的稳定性和易用性使得开发者可以更专注于业务逻辑,而不是底层...

    内核线程和用户线程的区别1

    用户线程的一个显著特点是其创建和切换速度快,因为不需要涉及用户态和核心态之间的上下文切换。然而,这也带来了几个限制: 1. 当一个用户线程执行I/O操作或遇到页错误而被阻塞时,整个进程都会被阻塞,因为内核...

    WorkerFactory AS3多线程库

    WorkerFactory AS3 多线程库是一个专门为ActionScript 3(AS3)设计的库,它使得在Flash环境中实现多线程操作成为可能。ActionScript是Adobe Flash Player和Adobe AIR平台上的主要编程语言,通常用于创建交互式内容...

    哈工大操作系统-L10用户级线程1

    实现上,线程库会维护一个线程队列,当前线程调用`yield`后,线程库选择另一个就绪线程继续执行。 - `creat`函数用于创建新线程,它需要为新线程分配TCB和栈空间,并设置初始状态。新线程创建后,就可以加入到线程...

    Linux_C语言线程实现机制分析.pdf

    混合线程模型是指核心级线程和用户级线程的结合,既提供核心线程以满足 SMP 系统的需要,也支持用线程库的方式在用户态实现另一套线程机制。这种模型可以满足多处理机系统的需要,也可以最大限度的减小调度开销。 ...

Global site tag (gtag.js) - Google Analytics