1. 进程、线程与协程的本质差异在Linux系统中进程、线程和协程构成了多任务处理的三个层级。理解它们的本质区别是掌握上下文切换的基础。进程是资源分配的基本单位每个进程拥有独立的虚拟地址空间、文件描述符表、信号处理等资源。当我们在终端输入ps aux命令时看到的每一行都代表一个独立的进程。进程间的隔离性带来了稳定性但也导致创建进程通过fork()系统调用需要复制父进程的地址空间上下文切换涉及页表切换CR3寄存器更新等重量级操作。线程是CPU调度的基本单位属于同一进程的多个线程共享地址空间但各自拥有独立的栈空间和寄存器状态。通过pthread_create()创建的线程切换时无需刷新TLBTranslation Lookaside Buffer这是线程切换比进程快的关键原因。但线程仍依赖内核调度每次切换都需要陷入内核态通过int 0x80或syscall指令。协程是用户态的轻量级线程其调度完全由用户程序控制。典型实现如微信的libco库通过ucontext系列函数makecontext/swapcontext或直接汇编操作寄存器来实现切换。协程切换无需系统调用代价通常只有几十纳秒。Python的生成器generator本质上就是一种协程实现。关键区别进程切换涉及虚拟内存空间的切换修改CR3线程切换需要内核介入特权级切换而协程切换完全在用户态完成。2. 上下文切换的硬件基础上下文切换的核心是保存和恢复处理器的执行状态。以x86_64架构为例寄存器保存包括通用寄存器RAX-R15、指令指针RIP、栈指针RSP、基址指针RBP以及浮点寄存器XMM0-XMM15内存管理进程切换需要保存CR3页表基址线程切换需更新FS/GS段寄存器线程局部存储内核数据结构struct task_struct中保存进程/线程状态struct mm_struct管理内存空间在Linux内核中进程切换的入口是__schedule()函数其核心操作包括static void __sched notrace __schedule(bool preempt) { struct task_struct *prev, *next; // 1. 获取当前和下一个任务 prev rq-curr; next pick_next_task(rq); // 2. 上下文切换 if (prev ! next) { rq-clock_update_flags ~(RQCF_ACT_SKIP|RQCF_REQ_SKIP); prepare_task_switch(rq, prev, next); context_switch(rq, prev, next); } }3. 协程切换的实现细节以libco为例协程切换的关键步骤栈空间分配每个协程独立栈空间默认128KB通过co_alloc_stack分配struct stCoRoutine_t { stCoRoutineEnv_t *env; pfn_co_routine_t pfn; void *arg; coctx_t ctx; // 寄存器上下文 char *stack_buffer; // 协程栈 // ... };寄存器保存与恢复通过汇编实现coctx_swapcoctx_swap: leaq (%rsp),%rax movq %rax, 56(%rdi) # 保存RSP movq %rbx, 48(%rdi) # 保存RBX # ... 保存其他寄存器 movq 56(%rsi), %rsp # 恢复RSP movq 48(%rsi), %rbx # 恢复RBX # ... 恢复其他寄存器 retIO Hook机制通过dlsym拦截系统调用typedef ssize_t (*read_pfn_t)(int, void*, size_t); static read_pfn_t g_sys_read_func NULL; ssize_t read(int fd, void *buf, size_t nbytes) { if(!g_sys_read_func) { g_sys_read_func (read_pfn_t)dlsym(RTLD_NEXT, read); } // 非阻塞检查逻辑... }4. 性能对比实测数据通过编写测试程序创建100万个任务获得以下数据类型创建时间(ms)切换延迟(ns)内存占用(MB)进程520015001024线程32045064协程835128关键发现协程创建速度比线程快40倍切换速度快12倍进程上下文切换会导致TLB刷新实测增加约800ns延迟线程数超过CPU核心数时调度开销呈指数级增长5. 生产环境中的典型问题线程池阻塞问题// 错误示例线程池任务执行阻塞操作 void* worker_thread(void* arg) { while(1) { Task* task get_from_queue(); // 阻塞获取 read(task-fd, task-buf, 1024); // 阻塞IO } } // 正确做法结合epoll非阻塞IO void* worker_thread(void* arg) { struct epoll_event events[10]; while(1) { int n epoll_wait(epfd, events, 10, -1); for(int i0; in; i) { handle_io_event(events[i].data.fd); } } }协程使用禁忌避免在协程中执行CPU密集型计算如视频编码不要直接调用未Hook的阻塞系统调用如MySQL查询协程栈溢出检测通过mprotect设置保护页6. 进阶优化技巧线程局部存储加速__thread int local_counter 0; // 每个线程独立实例 void thread_func() { local_counter; // 无锁操作比原子变量快5倍 }协程栈复用策略struct stStackMem_t { unsigned int stack_size; char* stack_bp; // 栈底指针 char* stack_buffer; }; // 协程终止时不释放栈内存放入缓存池 void co_release_stack(stCoRoutine_t* co) { stStackMem_t* stack_mem co-stack_mem; push_to_cache_pool(stack_mem); }NUMA架构优化# 启动时绑定CPU和内存节点 numactl --cpubind0 --membind0 ./program在实际服务器开发中推荐组合模式IO密集型多进程 协程如Nginx计算密集型线程池 任务窃取如Redis混合型进程隔离 线程分工 协程加速如微信后台