当前位置: 首页 > news >正文

Linux线程与进程的栈管理、页表机制及线程封装

目录

一、线程栈管理

进程(主线程)栈管理

子线程栈管理

二、页表和页表项

页表标志位定义

关键数据结构

页表分配函数

三、通用线程封装实现

实现要点

四、直接调用clone系统调用

关键点说明


一、线程栈管理

虽然Linux内核将线程和进程统一在task_struct结构中管理,但在地址空间的栈(stack)管理上仍有显著区别:

进程(主线程)栈管理

  • 主线程栈:可以简单理解为main()函数的栈空间

  • 创建机制:在fork()时复制父进程的stack空间地址,采用写时拷贝(COW)机制

  • 动态增长:栈空间可以动态增长,但有上限限制

  • 溢出处理

    • 超出扩充上限时会触发栈溢出,内核会发送段错误信号(SIGSEGV)给进程

    • 进程栈是唯一可以访问未映射页而不一定会立即发生段错误的情况——只有超出扩充上限才会报错

子线程栈管理

  • 创建方式:通常通过glibc/uclibc的pthread_create()接口创建

  • 内存分配

    • 使用mmap系统调用在文件映射区(共享区)分配固定大小的栈空间

    • 关键代码(来自glibc的nptl/allocatestack.c):

      mem = mmap(NULL, size, prot, MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, -1, 0);
    • 默认栈大小通常为8MB(可通过pthread_attr_setstacksize()调整)

  • 与进程栈的区别

    • 栈空间是预先固定分配的,不能动态增长

    • 用尽栈空间会导致未定义行为(通常是崩溃),而不会像进程栈那样触发动态增长

  • 系统调用流程:glibc通过mmap获取栈内存后,调用sys_clone:

    int sys_clone(struct pt_regs *regs) {unsigned long clone_flags;unsigned long newsp;// ...clone_flags = regs->bx;newsp = regs->cx; // 获取mmap得到的线程栈指针// ...return do_fork(clone_flags, newsp, regs, 0, parent_tidptr, child_tidptr);
    }
  • 内存访问特性

    • 线程栈在进程地址空间中通过mmap映射的私有内存区域

    • 理论上线程私有,但同一进程的线程会浅拷贝生成者的task_struct字段,其他线程可能访问到


二、页表和页表项

Linux内核采用两级页表结构,维护硬件页表和Linux内部页表两套体系:

页表标志位定义

/* 页表标志位 */
#define L_PTE_PRESENT   (1 << 0)   // 页表项存在标志
#define L_PTE_FILE      (1 << 1)   // 仅当!PRESENT时使用,表示非内存映射文件
#define L_PTE_YOUNG     (1 << 1)   // 页被访问过(用于页面置换算法)
#define L_PTE_BUFFERABLE (1 << 2)  // 可缓冲
#define L_PTE_CACHEABLE (1 << 3)   // 可缓存
#define L_PTE_USER      (1 << 4)   // 用户可访问
#define L_PTE_WRITE     (1 << 5)   // 可写
#define L_PTE_EXEC      (1 << 6)   // 可执行
#define L_PTE_DIRTY     (1 << 7)   // 页被修改过
#define L_PTE_COHERENT  (1 << 9)   // I/O一致性(xsc3)
#define L_PTE_SHARED    (1 << 10)  // CPU间共享(v6)
#define L_PTE_ASID      (1 << 11)  // 非全局(使用ASID, v6)

关键数据结构

typedef struct { unsigned long pte; } pte_t;  // 页表项
typedef struct { unsigned long pgd; } pgd_t;  // 页全局目录项struct mm_struct {struct vm_area_struct *mmap;        // 虚拟内存区域链表struct rb_root mm_rb;               // VMA红黑树根节点unsigned long mmap_base;            // mmap区域基址unsigned long task_size;            // 任务虚拟内存空间大小pgd_t *pgd;                         // 页目录起始地址// ...
};

页表分配函数

页全局目录分配

pgd_t *pgd_alloc(struct mm_struct *mm) {pgd_t *ret = (pgd_t *)__get_free_page(GFP_KERNEL | __GFP_ZERO);// 初始化页表项...return ret;
}

页表项分配

pte_t *pte_alloc_one_kernel(struct mm_struct *mm, unsigned long address) {pte_t *pte = (pte_t *)__get_free_page(GFP_KERNEL|__GFP_REPEAT|__GFP_ZERO);return pte;
}

三、通用线程封装实现

下面是一个支持任意参数传递的线程封装类实现:

#include <iostream>
#include <functional>
#include <memory>
#include <pthread.h>
#include <unistd.h>class Thread {
public:Thread() : thread_id_(0), running_(false) {}~Thread() {if (running_) {pthread_detach(thread_id_);}}template <typename Callable, typename... Args>bool start(Callable&& func, Args&&... args) {if (running_) {std::cerr << "Thread is already running!" << std::endl;return false;}// 使用完美转发和std::bind打包任务auto task = std::make_shared<std::function<void()>>(std::bind(std::forward<Callable>(func), std::forward<Args>(args)...));// 创建线程,传递任务指针auto* task_ptr = new std::shared_ptr<std::function<void()>>(task);if (pthread_create(&thread_id_, nullptr, &Thread::threadEntry, task_ptr) != 0) {delete task_ptr; // 失败时清理std::cerr << "Failed to create thread!" << std::endl;return false;}running_ = true;return true;}void join() {if (running_) {pthread_join(thread_id_, nullptr);running_ = false;}}private:pthread_t thread_id_;bool running_;static void* threadEntry(void* arg) {// 使用unique_ptr自动管理资源std::unique_ptr<std::shared_ptr<std::function<void()>>> task_ptr(static_cast<std::shared_ptr<std::function<void()>>*>(arg));// 执行任务(*(*task_ptr))();return nullptr;}
};// 示例函数
void printMessage(const std::string& message, int value, int a, int b, int c) {std::cout << "Message: " << message << ", Value: " << value << std::endl;std::cout << "a:" << a << std::endl;std::cout << "b:" << b << std::endl;std::cout << "c:" << c << std::endl;sleep(10);
}int main() {Thread thread;thread.start(printMessage, "Hello, World!", 42, 1, 2, 3);thread.join();return 0;
}

实现要点

  1. 完美转发:使用std::forward保持参数的左值/右值属性

  2. 任务打包:将可调用对象和参数打包为std::function<void()>

  3. 内存管理

    • 使用shared_ptr确保任务对象在线程执行期间有效

    • 使用unique_ptr在线程入口函数中自动释放资源

  4. 线程安全:正确处理线程创建失败时的资源释放


四、直接调用clone系统调用

下面是一个直接使用clone()系统调用的示例:

#define _GNU_SOURCE
#include <sched.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>#define STACK_SIZE (1024 * 1024) // 1MB栈空间static int child_func(void *arg) {printf("Child process: PID = %d\n", getpid());return 0;
}int main() {// 分配子进程栈空间char *stack = (char*)malloc(STACK_SIZE);if (!stack) {perror("malloc");exit(EXIT_FAILURE);}// 使用clone创建子进程// CLONE_VM: 共享虚拟内存空间// SIGCHLD: 子进程退出时发送SIGCHLD信号pid_t pid = clone(child_func, stack + STACK_SIZE, CLONE_VM | SIGCHLD, NULL);if (pid == -1) {perror("clone");free(stack);exit(EXIT_FAILURE);}printf("Parent process: PID = %d, Child PID = %d\n", getpid(), pid);// 等待子进程结束if (waitpid(pid, NULL, 0) == -1) {perror("waitpid");free(stack);exit(EXIT_FAILURE);}free(stack);return 0;
}

关键点说明

  1. 栈分配

    • 必须手动分配栈空间

    • 栈指针需要指向分配空间的末尾(因为栈向下增长)

  2. clone标志

    • CLONE_VM: 共享虚拟内存空间

    • CLONE_FS/CLONE_FILES: 共享文件系统信息/文件描述符

    • CLONE_SIGHAND: 共享信号处理程序

    • SIGCHLD: 子进程退出时发送SIGCHLD信号

  3. 资源管理

    • 必须手动管理栈内存的分配和释放

    • 需要正确处理错误情况下的资源释放

  4. 与pthread_create的区别

    • 更底层,提供更多控制选项

    • 需要手动处理更多细节(如栈管理)

    • 通常用于实现线程库或特殊需求的进程创建

这个示例展示了Linux下线程/进程创建的底层机制,与高层pthread接口形成对比,有助于深入理解Linux的多任务处理机制。

http://www.dtcms.com/a/546322.html

相关文章:

  • 专门建设网站的公司提供网站建设定制
  • 网站建设提高信息绍兴网站建设方案服务
  • 网页制作与网站建设答案传奇霸业手游官网
  • Linux中NPTL线程库的线程ID、内存布局与独立上下文
  • 做前端网站要注意哪些wordpress 只显示标题
  • PaddleOCR-VL:基于0.9B超轻量视觉-语言模型的高效多语言文档解析
  • 门户网站广告是什么网站设计风格有哪些
  • 网站建设系统规划南昌网站建设那家好
  • 一个专门做各种恐怖片的电影网站筛网怎么做网站
  • 网站seo诊断工具长沙便宜网站建设
  • JDBC快速入门
  • 国家2000(CGCS2000)是什么?
  • 以下哪些是付费推广方式seo作弊
  • Linux : I/O 模型
  • Rust——或模式(Or Patterns)的语法:Rust模式匹配的优雅演进
  • 教做3d的网站宁津哪个网络公司做网站比较好
  • 仓颉语言异常处理入门:从特性解读到实践落地
  • 通义DeepResearch技术报告解读
  • Java—代码块、内部类
  • 照片做视频模板下载网站旅游网站建设步骤
  • 狂人站群系统在线制作图谱
  • 婚礼策划网站模板中文中山做网站的
  • 专业建设网站多少钱淘客返利网站怎么做
  • 解决C++内存泄漏:Effective STL第7条的实践与智能指针的应用
  • 导入谷歌的zxing,实现二维码
  • 花生壳内网穿透网站如何做seo优化个人外贸公司网站
  • Unity jar更新不生效怎么解决
  • 边缘计算场景模型推理延迟的评估
  • 李沧做网站公司wordpress 导航不可点击
  • 淄博网站建设淄博深圳企业网站制作中心