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

Linux中的线程

0.页表的正确理解

进程地址空间是进程能够使用的资源窗口,而页表则决定了进程实际上拥有的资源。

页表实际上是二级索引,以32位机器为例。内存管理的基本单位是4kb的页框。

一级页表是以地址的高10位为索引,二级页表是以次高10位为2级页表,找到页框的起始地址,最后再以低12位为偏移量找到实际上的物理地址

1. 线程的概念 

进程是承担资源分配的基本实体,线程则是在进程内部的执行单元,是CPU调度的基本单位。

在Linux中并没有真正意义上的线程,有的只是轻量级进程。当我们创建线程时,实际上是在进程内部再创建一个轻量级进程,它与父进程指向同一个mem_struct,使用同一个页表,属于进程的一部分。

 轻量级进程的实现实际上是用进程PCB模拟而来的,因此其具有与PCB相同的被执行,调度的方法。维护成本更低,更加的高效。

Linux只为我们提供了轻量级进程创建的接口,因此在此基础上为我们提供了一套原生线程库,实现了对底层接口的封装。

2.线程资源私有情况

线程之间绝大部分资源共享,但线程之间又有直接的私有空间

1️⃣线程pcb属性信息

2️⃣线程的上下文信息

3️⃣线程的独立栈结构

3.线程切换高效

CPU在处理线程切换时的工作要比进程切换的工作少。

进程切换时需要:切换进程地址空间,切换页表,切换PCB,切换上下文

线程切换时需要:切换PCB,切换上下文

但是最重要的是,进程切换时CPU中的Cache数据需要全部更新,而线程切换时CPU中的Cache数据并不用全部更新。因此线程切换要比进程切换更高效。

4.线程接口

4.1线程创建pthread_create

4.2线程终止

 1️⃣当线程任务结束return后就会终止

2️⃣线程中调用pthread_exit(return val)时也会终止线程

3️⃣线程取消pthread_cancel(tid)时就会终止指定线程此时线程的返回值是(void*)-1

4.3线程等待pthread_join(pthread_t , void **)

与进程等待相同,线程也需要等待,目的是:获取返回值和回收线程的PCB控制块。

4.4线程分离pthread_detach(pthread_t tid)

如果我们不关心线程的返回值,想像进程对SIGCHILD采取SIG_IGN,让子进程自己回收释放时,我们就能使用线程分离,这样线程就能自己回收释放了,此时线程将由joinable变为onjoinable状态,此时的线程将不能被等待。

如果我们想在线程内部时自己detach,那么可以用pthread_self()获取自己的tid。 

 练习代码

#include<cstdio>
#include<iostream>
#include<unistd.h>

#include<pthread.h>

using namespace std;
void* task(void* arg){
    int count = 0;
    const char* tmp = static_cast<const char*>(arg);
    while(true){
        printf("this is thread:%s\n",tmp);
        count++;
        if(count == 10) break;
        sleep(1);
    }
    pthread_exit((void*)"我是新线程,我结束了");
    //return (void*)"我是新线程,我结束了";
}

int main(){

    pthread_t tid  = 0;
    pthread_create(&tid,nullptr,task,(void*)"this is new thread");

    void* ret = nullptr;
    int cnt = 5;
    while(cnt--)  sleep(1);
    pthread_cancel(tid);    
    pthread_join(tid,&ret);
    printf("return val :%d\n",ret);

    return 0;
}
#include<cstdio>
#include<unistd.h>
#include<pthread.h>
#include<vector>
using namespace std;
// void* task(void* arg){
//     const char* buffer = static_cast<const char*>(arg);
//     while(true){
//         printf("%s\n",buffer);
//         sleep(1);
//     }
//     return nullptr;
// }

// int main(){
// #define NUM 10
//     pthread_t array[NUM] ={0};
//     for(int i = 0 ; i < NUM;i++ ){
//         char buffer[1024];
//         snprintf(buffer,sizeof buffer,"thread: %d",i);
//         pthread_t tid = 0;
//         pthread_create(&tid,nullptr,task,buffer);
//         array[i] = tid;
//     }

//     for(int i = 0 ; i < NUM; i++){
//         pthread_join(array[i],nullptr);
//     }

//     return 0;
// }
//上面的代码是错误的,会导致每个线程都是从9开始访问,因为其传的是地址,而每次for都会更改地址指向内容,而且线程进入的速度不同,导致异常情况

void* task(void* arg){
    const char* buffer = static_cast<const char*>(arg);
    int cnt = 10;
    while (cnt--)
    {
        printf("%s\n",buffer);
        sleep(1);
    }
    return 0;
}
struct thread{
    pthread_t tid;
    char buffer[1024];
};
int main(){

#define NUM 10
    vector<thread*> v(NUM);
    for(int i = 0 ; i < NUM;i++ ){
        v[i] = new thread;
        snprintf(v[i]->buffer,sizeof v[i]->buffer,"thread :%d",i);
        pthread_t tid = 0;
        pthread_create(&tid,nullptr,task,(void*)v[i]->buffer);
        printf("%x\n",tid);
        v[i]->tid = tid;
    }

    for(int i = 0 ; i < NUM; i++){
        pthread_join(v[i]->tid,nullptr);
        printf("on sucess:%x\n",v[i]->tid);
        delete v[i];
    }

    return 0;
}

5.线程库,tid,独立栈,线程局部存储

我们编写的代码链接线程库时,需要-lphread指定链接的库才能链接成功。

当运行程序后,线程动态库将会加载到进程地址空间的共享区,线程库为我们提供了方法接口。线程的私有数据,例如线程的上下文结构,线程私有栈,线程的局部存储数据都是在mmap分配的。

线程的pthread_t tid是实际上是一个地址,其指向的是线程数据的起始位置

线程局部数据存储

例如我们定义了一个全局变量g_val,将其用__thread修饰,那么每个进程都会拥有其副本,这样的称为线程的局部数据

#include<cstdio>
#include<unistd.h>
#include<pthread.h>
#include<vector>
using namespace std;
// void* task(void* arg){
//     const char* buffer = static_cast<const char*>(arg);
//     while(true){
//         printf("%s\n",buffer);
//         sleep(1);
//     }
//     return nullptr;
// }

// int main(){
// #define NUM 10
//     pthread_t array[NUM] ={0};
//     for(int i = 0 ; i < NUM;i++ ){
//         char buffer[1024];
//         snprintf(buffer,sizeof buffer,"thread: %d",i);
//         pthread_t tid = 0;
//         pthread_create(&tid,nullptr,task,buffer);
//         array[i] = tid;
//     }

//     for(int i = 0 ; i < NUM; i++){
//         pthread_join(array[i],nullptr);
//     }

//     return 0;
// }
//上面的代码是错误的,会导致每个线程都是从9开始访问,因为其传的是地址,而每次for都会更改地址指向内容,而且线程进入的速度不同,导致异常情况

__thread int g_val = 10;

void* task(void* arg){
    // const char* buffer = static_cast<const char*>(arg);
    // int cnt = 10;
    // while (cnt--)
    // {
    //     printf("%s\n",buffer);
    //     sleep(1);
    // }
    printf("%x\n",&g_val);
    return 0;
}
struct thread{
    pthread_t tid;
    char buffer[1024];
};
int main(){
    printf("%x\n",&g_val);
#define NUM 10
    vector<thread*> v(NUM);
    for(int i = 0 ; i < NUM;i++ ){
        v[i] = new thread;
        snprintf(v[i]->buffer,sizeof v[i]->buffer,"thread :%d",i);
        pthread_t tid = 0;
        pthread_create(&tid,nullptr,task,(void*)v[i]->buffer);
        //rintf("%x\n",tid);
        v[i]->tid = tid;
    }

    for(int i = 0 ; i < NUM; i++){
        pthread_join(v[i]->tid,nullptr);
        //printf("on sucess:%x\n",v[i]->tid);
        delete v[i];
    }

    return 0;
}

我们观察到每个线程的g_val的地址都不同。 

6.线程与进程

进程是承担分配资源的基本实体,线程是进程中的执行流,是CPU调度的基本单位。

信号的作用实体是进程,会让每个PCB中的信号pending位改变,因此线程推出时并不会涉及到信号,因为当线程异常时,如果有信号,那么会杀死整个进程。

相关文章:

  • 小刚说C语言刷题——每日一题东方博宜1000熟悉OJ环境
  • VS 基于git工程编译版本自动添加版本号
  • 【网络安全】通过 JS 寻找接口实现权限突破
  • 【秣厉科技】LabVIEW工具包——OpenCV 教程(19):拾遗 - imgproc 基础操作(上)
  • 软件测试过程模型:v模型、w模型、x模型、H模型
  • 软件项目经理PM实战操作手册【附全文阅读】
  • clickhouse中常用的几个函数
  • 鸿蒙公共通用组件封装实战指南:从基础到进阶
  • 4月份到9月份看6本书第二天【ERP与企业管理】
  • selinux 没有关闭导致ssh 无法免密连接问题
  • PDF转换格式失败?原因及解决方法全解析
  • 祁连山国家公园shp格式数据
  • 如何打造干净的网页版B站(包括Bing搜索)
  • 4.14代码随想录第四十三天打卡
  • 六、分布式嵌入
  • 测试基础笔记第三天
  • 算法学习~
  • ffmpeg入门
  • testssl.sh:自动化检测SSL/TLS的配置漏洞
  • 计算机视觉与深度学习 | 钢筋捆数识别
  • 当代读书人的暗号:不是拆快递,是拆出版社样书!|世界读书日特辑
  • 继微软之后,亚马逊也放缓人工智能数据中心计划
  • 杭州萧山区两宗地块收金约44.73亿元,最高溢价率74.4%
  • 民建吉林省委提案:当前生育政策集中鼓励多孩生育,应该转变思路
  • 荣膺劳伦斯大奖实至名归,杜普兰蒂斯的传奇没有极限
  • 沙龙 | 新书分享:中国电商崛起的制度密码