【LInux】线程thread从内核原理到C++封装

📢博客主页:https://blog.youkuaiyun.com/2301_779549673
📢博客仓库:https://gitee.com/JohnKingW/linux_test/tree/master/lesson
📢欢迎点赞 👍 收藏 ⭐留言 📝 如有错误敬请指正!
📢本文由 JohnKi 原创,首发于 优快云🙉
📢未来很长,值得我们全力奔赴更美好的生活✨

在这里插入图片描述

在这里插入图片描述


📢前言

笔者最近在学习Linux,打算对线程到网络部分的学习做一份较为全面的总结,笔者将把这段时间的博客代码都放在gittee仓库中,有需要可以自取

本篇博客所用代码位于


🏳️‍🌈一、线程的本质探秘

1.1 进程 vs 线程的"办公室哲学"

假设进程是一个公司:

  • 进程:独立办公楼(独立虚拟地址空间)
  • 线程:共享办公的员工(共享代码段/堆/打开文件)
// 进程fork示例
pid_t pid = fork(); // 创建新办公楼
// 线程创建示例
pthread_create(&tid, NULL, func, arg); // 招聘新员工

1.2 Linux线程实现原理

通过ps -eLf命令观察线程

UID        PID  PPID   LWP  C NLWP STIME TTY      TIME CMD
root         1     0     1  0    1 Aug29 ?    00:00:01 /sbin/init
root       123     1   123  0    3 Aug29 ?    00:00:00 /usr/sbin/sshd
root       456   123   456  0    1 Aug29 ?    00:00:00 sshd: root@pts/0

​LWP:轻量级进程ID(线程本质是轻量级进程)
​NLWP:线程数量

1.3 线程的"人生阶段"

	[*] --> NEW: 创建线程对象
    NEW --> READY: Start()
    READY --> RUNNING: 被调度
    RUNNING --> BLOCKED: 等待I/O
    BLOCKED --> READY: 资源就绪
    RUNNING --> TERMINATED: 执行完成
    RUNNING --> STOPPED: Stop()

🏳️‍🌈二、线程的控制手段

下图为整个线程以及进程的逻辑过程,我将借助此图进行分析和说明线程的4个主要控制手段 - 创建、停止、等待、分离
在这里插入图片描述

控制逻辑链

创建线程 → 分配task_struct → 绑定执行函数 → 加入调度队列
   │
   ├─ 停止:设置终止标志 → 释放资源 → 更新状态
   ├─ 等待:阻塞主线程 → 监听状态变化 → 资源回收
   └─ 分离:解除关联 → 自动回收资源

2.1 线程创建逻辑

  1. 内核视角(对应图中task_struct)
// 内核创建线程的简化逻辑
int kernel_thread(void (*fn)(void*), void* arg) {
    struct task_struct *tsk = alloc_task_struct(); // 分配task_struct
    tsk->mm = current->mm;       		// 共享父进程mm_struct(关键!)
    copy_files(tsk);             		// 共享文件描述符表
    tsk->stack = alloc_stack();  		// 分配独立内核栈
    setup_thread_stack(tsk, fn, arg); 	// 设置入口函数
    add_to_runqueue(tsk);        		// 加入调度队列
    return tsk->pid;
}

共享地址空间:tsk->mm = current->mm(区别于进程的mm = copy_mm())
独立资源:内核栈、寄存器值、调度优先级

  1. 用户态API(对应图中"Linux下的线程")
// 使用pthread_create创建线程
pthread_t tid;
pthread_create(&tid, NULL, thread_func, arg);

在这里插入图片描述

2.2 线程停止逻辑

内核处理流程:
	1. 设置tsk->flags |= PF_EXITING
	2. 触发取消点(系统调用处)
	3. 调用清理函数栈(pthread_cleanup_push注册的)
	4. 释放线程资源
  1. 协作式停止(推荐方式)
// 线程函数内检查终止标志
void* thread_func(void* arg) {
    ThreadContext* ctx = (ThreadContext*)arg;
    while(!ctx->should_stop) {  // 主动检查停止标志
        // 执行任务...
    }
    return NULL;
}

优势:安全释放资源,避免状态不一致

  1. 强制停止(对应图中"停止"标签)
pthread_cancel(tid);  // 发送取消请求

2.3 线程等待逻辑

为什么需要线程等待?

  • 已经退出的线程,其空间没有被释放,仍然在进程的地址空间内。
  • 创建新的线程不会复⽤刚才退出线程的地址空间
int pthread_join(pthread_t thread, void **value_ptr);

thread:线程ID
value_ptr:它指向⼀个指针,后者指向线程的返回值

内核实现

// 内核源码片段(简化)
int do_join_thread(struct task_struct *tsk) {
    while (tsk->state != TASK_DEAD) {
        set_current_state(TASK_INTERRUPTIBLE);
        schedule_timeout(WAIT_TIME);  // 让出CPU
    }
    unlink_thread(tsk);  // 从进程线程列表中移除
    free_task_struct(tsk);
}

状态转移图示

stateDiagram
    [*] --> RUNNING : 创建
    RUNNING --> TERMINATED : 执行完成
    RUNNING --> ZOMBIE : 终止但未Join
    ZOMBIE --> [*] : 被Join

调用该函数的线程将挂起等待,直到id为thread的线程终止。thread线程以不同的方法终止,通过pthread join得到的终止状态是不同的,总结如下:

  1. 如果thread线程通过return返回,value_ptr所指向的单元里存放的是thread线程函数的返回值。
  2. 如果thread线程被别的线程调用pthread_cancel异常终掉,value_ptr所指向的单元里存放的是常数PTHREAD CANCELED。
  3. 如果thread线程是自己调用pthread_exit终止的,value_ptr所指向的单元存放的是传给pthread exit的参数。
  4. 如果对thread线程的终止状态不感兴趣,可以传NULL给value_ptr参数。

2.4 线程分离逻辑

  • 默认情况下,新创建的线程是joinable的,线程退出后,需要对其进行pthread_join操作,否则无法释放资源,从而造成系统泄漏。
  • 如果不关心线程的返回值,join是一种负担,这个时候,我们可以告诉系统,当线程退出时,自动释放线程资源。
pthread_detach(tid);

内核响应:

  • 修改tsk->exit_signal为SIGNAL_GROUP_EXIT
  • 设置tsk->ptrace = 0(解除跟踪)
  • 禁止后续Join操作

分离后的资源回收

线程终止 → 内核自动回收资源 → 无需用户干预

在这里插入图片描述

2.5 控制手段与内存管理

  1. 虚拟地址空间交互(对应mm_struct)
  • 创建时:共享父进程的mm_struct(CLONE_VM标志)
  • ​终止时:检查引用计数,若为0则释放mm_struct
  1. 页表操作示例
// 线程访问共享变量时的页表行为
void* shared_var = mmap(NULL, size, PROT_READ|PROT_WRITE, 
                       MAP_SHARED, -1, 0); 

// 内核处理:
// 1. 建立共享映射
// 2. 多个线程的页表项指向同一物理页
// 3. 修改时触发COW(若未用MAP_SHARED)

🏳️‍🌈三、手把手实现线程库

// 目标使用方式
Thread thread([](){
    std::cout << "线程执行中..." << std::endl; 
});
thread.Start();
thread.Stop();
thread.Join();
thread.Detach();

3.1 状态机管理

    // 线程状态枚举
    enum class TSTATUS
    {
        NEW,      // 新建未启动状态
        RUNNING,  // 运行中状态
        STOP      // 已停止状态
    };

3.2 成员变量管理

private:
        std::string _name;    // 线程名称标识
        pthread_t _tid;       // 线程ID(系统级)
        pid_t _pid;           // 所属进程ID
        bool _joinable;       // 是否是分离的,默认不是(可join状态)
        func_t _func;         // 用户回调函数
        TSTATUS _status;      // 当前线程状态

3.3 构造函数 && 析构函数

        // 构造函数:接收用户回调函数
        Thread(func_t func) : _func(func), _status(TSTATUS::NEW), _joinable(true)
        {
            // 生成唯一线程名称(Thread-1, Thread-2...)
            _name = "Thread-" + std::to_string(number++);
            _pid = getpid();  // 记录所属进程ID
        }
		
		// 析构函数
		~Thread(){}

3.4 启动线程

        // 静态线程入口函数(POSIX线程要求格式)
        static void *Routine(void *args)
        {
            // 将void*参数转换回Thread对象指针
            Thread *t = static_cast<Thread *>(args);
            t->_status = TSTATUS::RUNNING;  // 更新线程状态
            t->_func();                     // 执行用户回调函数
            return nullptr;
        }
        // 启动线程方法
        bool Start()
        {
            if (_status != TSTATUS::RUNNING)  // 防止重复启动
            {
                // 创建POSIX线程(Routine为入口,this指针作为参数)
                // pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);
                    //   thread:指向线程ID的指针
                    //   attr:线程属性(可选)
                    //   start_routine:线程入口函数
                    //   arg:入口函数参数(这里是this指针)
                // 返回值:0表示成功,其他表示失败
                int n = ::pthread_create(&_tid, nullptr, Routine, this); // TODO
                return n == 0;  // 返回创建结果
            }
            return false;
        }

3.5 停止线程

// 强制停止线程(慎用)
        bool Stop()
        {
            if (_status == TSTATUS::RUNNING)
            {
                // 发送取消请求到目标线程
                int n = ::pthread_cancel(_tid);
                if (n == 0) _status = TSTATUS::STOP;
                return n == 0;
            }
            return false;
        }

3.6 线程等待

// 等待线程结束(阻塞调用)
        bool Join()
        {
            if (_joinable)  // 只有非分离线程可调用
            {
                int n = ::pthread_join(_tid, nullptr);
                if (n == 0) _status = TSTATUS::STOP;
                return n == 0;
            }
            return false;
        }

3.7 分离线程

// 设置线程分离
        void Detach()
        {
            EnableDetach();          // 修改内部状态
            pthread_detach(_tid);    // 系统级分离设置
        }

🏳️‍🌈四、添加模板

// 线程模板类(支持带参数执行)
    template <typename T>
    class Thread
T _data;              // 数据副本(存储在栈区)

🏳️‍🌈五、整体代码

当前为v2版,即可用模板版

5.1 Thread.hpp

#ifndef _THREAD_HPP__
#define _THREAD_HPP__

#include <iostream>
#include <string>
#include <pthread.h>
#include <functional>
#include <sys/types.h>
#include <unistd.h>

// v2
#define v2
#ifdef v2
namespace ThreadModule
{
    static int number = 1;  // 线程实例计数器(对应图示中的task_struct数量)
    
    // 线程状态机(对应图示中task_struct的状态变化)
    enum class TSTATUS {
        NEW,      // 新建状态(已创建未启动)
        RUNNING,  // 运行状态(对应CPU调度队列中的task_struct)
        STOP      // 终止状态(等待资源回收)
    };

    // 线程模板类(支持带参数执行)
    template <typename T>
    class Thread
    {
        using func_t = std::function<void(T)>;  // 带参数的执行函数类型
    
    private:
        // POSIX线程入口函数(需满足C链接约定)
        static void* Routine(void* args) {
            // 将void*参数还原为Thread对象指针(对应图示中的task_struct关联)
            Thread<T>* t = static_cast<Thread<T>*>(args);
            t->_status = TSTATUS::RUNNING;  // 更新状态为运行中
            t->_func(t->_data);             // 执行用户逻辑(访问共享内存区)
            return nullptr;                 // 线程自然终止
        }

        // 设置分离状态(修改图示中的task_struct标志位)
        void EnableDetach() { _joinable = false; }

    public:
        // 构造函数:初始化执行函数和数据副本(在栈区创建数据副本)
        Thread(func_t func, T data) : _func(func), _data(data), 
            _status(TSTATUS::NEW), _joinable(true) 
        {
            _name = "Thread-" + std::to_string(number++);  // 生成唯一标识
            _pid = getpid();  // 记录所属进程PID(对应图示中的mm_struct所属进程)
        }

        // 启动线程(创建轻量级进程)
        bool Start() {
            if (_status != TSTATUS::RUNNING) {
                // 创建POSIX线程(对应图示中的clone系统调用)
                int n = ::pthread_create(&_tid, nullptr, Routine, this);
                return n == 0;  // 返回内核创建结果
            }
            return false;  // 防止重复启动
        }

        // 强制终止线程(可能引发资源泄漏)
        bool Stop() {
            if (_status == TSTATUS::RUNNING) {
                // 发送取消请求(可能中断页表查询过程)
                int n = ::pthread_cancel(_tid);
                if (n == 0) _status = TSTATUS::STOP;
                return n == 0;  // 返回内核操作结果
            }
            return false;
        }

        // 等待线程结束(处理ZOMBIE状态)
        bool Join() {
            if (_joinable) {
                // 阻塞等待并回收资源(对应图示中的task_struct回收)
                int n = ::pthread_join(_tid, nullptr);
                if (n == 0) _status = TSTATUS::STOP;
                return n == 0;
            }
            return false;
        }

        // 设置线程分离(自动回收资源)
        void Detach() {
            EnableDetach();         // 修改可连接状态
            pthread_detach(_tid);   // 通知内核自动回收(对应图示中的SIGNAL_GROUP_EXIT)
        }

        // 状态查询接口
        bool IsJoinable() { return _joinable; }
        std::string Name() { return _name; }

        ~Thread() {
            // 建议添加:if(_joinable) Join(); // RAII方式自动回收
        }

    private:
        std::string _name;    // 线程名称(调试用)
        pthread_t _tid;       // 线程ID(对应图示中的LWP)
        pid_t _pid;           // 进程ID(共享mm_struct的标识)
        bool _joinable;       // 连接状态(控制ZOMBIE状态转换)
        func_t _func;         // 执行函数(存储在代码区)
        TSTATUS _status;      // 状态机(NEW→RUNNING→STOP)
        T _data;              // 数据副本(存储在栈区)
    };
}

// v1
#else
namespace ThreadModule
{
    using func_t = std::function<void()>;  // 定义无参回调函数类型
    static int number = 1;                // 线程编号计数器
    
    // 线程状态枚举
    enum class TSTATUS
    {
        NEW,      // 新建未启动状态
        RUNNING,  // 运行中状态
        STOP      // 已停止状态
    };

    class Thread
    {
    private:
        // 静态线程入口函数(POSIX线程要求格式)
        static void *Routine(void *args)
        {
            // 将void*参数转换回Thread对象指针
            Thread *t = static_cast<Thread *>(args);
            t->_status = TSTATUS::RUNNING;  // 更新线程状态
            t->_func();                     // 执行用户回调函数
            return nullptr;
        }

        // 设置分离状态(内部方法)
        void EnableDetach() { _joinable = false; }

    public:
        // 构造函数:接收用户回调函数
        Thread(func_t func) : _func(func), _status(TSTATUS::NEW), _joinable(true)
        {
            // 生成唯一线程名称(Thread-1, Thread-2...)
            _name = "Thread-" + std::to_string(number++);
            _pid = getpid();  // 记录所属进程ID
        }

        // 启动线程方法
        bool Start()
        {
            if (_status != TSTATUS::RUNNING)  // 防止重复启动
            {
                // 创建POSIX线程(Routine为入口,this指针作为参数)
                // pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);
                    //   thread:指向线程ID的指针
                    //   attr:线程属性(可选)
                    //   start_routine:线程入口函数
                    //   arg:入口函数参数(这里是this指针)
                // 返回值:0表示成功,其他表示失败
                int n = ::pthread_create(&_tid, nullptr, Routine, this); // TODO
                return n == 0;  // 返回创建结果
            }
            return false;
        }

        // 强制停止线程(慎用)
        bool Stop()
        {
            if (_status == TSTATUS::RUNNING)
            {
                // 发送取消请求到目标线程
                int n = ::pthread_cancel(_tid);
                if (n == 0) _status = TSTATUS::STOP;
                return n == 0;
            }
            return false;
        }

        // 等待线程结束(阻塞调用)
        bool Join()
        {
            if (_joinable)  // 只有非分离线程可调用
            {
                int n = ::pthread_join(_tid, nullptr);
                if (n == 0) _status = TSTATUS::STOP;
                return n == 0;
            }
            return false;
        }

        // 设置线程分离
        void Detach()
        {
            EnableDetach();          // 修改内部状态
            pthread_detach(_tid);    // 系统级分离设置
        }

        // 状态查询方法
        bool IsJoinable() { return _joinable; }
        std::string Name() { return _name; }

        ~Thread(){}

    private:
        std::string _name;    // 线程名称标识
        pthread_t _tid;       // 线程ID(系统级)
        pid_t _pid;           // 所属进程ID
        bool _joinable;       // 是否是分离的,默认不是(可join状态)
        func_t _func;         // 用户回调函数
        TSTATUS _status;      // 当前线程状态
    };
}

#endif
#endif

5.2 Thread.cc

#include <unordered_map>
#include <memory>

#include "Thread.hpp"

#define NUM 10

using namespace ThreadModule;
// using thread_ptr_t = std::shared_ptr<ThreadModule::Thread>;

using namespace ThreadModule;

class threadData
{
public:
    int max;
    int start;
};

#define v2
#ifdef v2
void Count(threadData td)
{
    for (int i = td.start; i < td.max; i++)
    {
        std::cout << "i == " << i << std::endl;
        sleep(1);
    }
}

#else
void Count(){
    std::cout << "线程执行中..." << std::endl; 
}
#endif

int main()
{
    threadData td;
    td.max = 60;
    td.start = 50;
    // v2 版
    ThreadModule::Thread<threadData> thread(Count, td);

    // v1 版
    // ThreadModule::Thread thread(Count);

    

    thread.Start();

    thread.Join();

    // 先描述,在组织!
    // std::unordered_map<std::string, thread_ptr_t> threads;
    // // 如果我要创建多线程呢???
    // for (int i = 0; i < NUM; i++)
    // {
    //     thread_ptr_t t = std::make_shared<ThreadModule::Thread>([](){
    //         while(true)
    //         {
    //             std::cout << "hello world" << std::endl;
    //             sleep(1);
    //         }
    //     });
    //     threads[t->Name()] = t;
    // }

    // for(auto &thread:threads)
    // {
    //     thread.second->Start();
    // }

    // for(auto &thread:threads)
    // {
    //     thread.second->Join();
    // }

    // ThreadModule::Thread t([](){
    //     while(true)
    //     {
    //         std::cout << "hello world" << std::endl;
    //         sleep(1);
    //     }
    // });

    // t.Start();
    // std::cout << t.Name() << "is running" << std::endl;
    // sleep(5);

    // t.Stop();
    // std::cout << "Stop thread : " << t.Name()<< std::endl;
    // sleep(1);
    // t.Join();
    // std::cout << "Join thread : " << t.Name()<< std::endl;

    return 0;
}

5.3 Makefile

bin=testThread
cc=g++
src=$(wildcard *.cc)
obj=$(src:.cc=.o)

$(bin):$(obj)
	$(cc) -o $@ $^ -lpthread
%.o:%.cc
	$(cc) -c $< -std=c++17

.PHONY:clean
clean:
	rm -f $(bin) $(obj)

.PHONY:test
test:
	echo $(src)
	echo $(obj)

👥总结

本篇博文对 线程thread从内核原理到C++封装 做了一个较为详细的介绍,不知道对你有没有帮助呢

觉得博主写得还不错的三连支持下吧!会继续努力的~

请添加图片描述

评论 27
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值