深入Linux轻量级进程管理:线程创建、线程ID解析与进程地址空间页表探究

Jupiter· 2024-09-08 14:37:10 阅读 52

🍑个人主页:Jupiter.

🚀 所属专栏:Linux从入门到进阶

欢迎大家点赞收藏评论😊

在这里插入图片描述

在这里插入图片描述

目录

`🚲Linux线程控制``🐏POSIX线程库``🐕创建线程``🐟指令查看轻量级进程``指令:ps -aL`

`🐒线程ID及进程地址空间布局`*pthread_t 到底是什么类型呢?*

`🦔__thread与线程的局部存储`


<code>🚲Linux线程控制

🐏POSIX线程库

在Linux中,没有线程的概念,只有轻量级进程的概念,所以Linux系统只提供了轻量级进程的系统调用,没有线程相关的系统调用,但是用户不知道轻量级进程的概念,只知道线程和进程,所以Linux将轻量级进程的系统调用进行封装,转成线程相关的接口语义提供给用户,就形成了pthread库

与线程有关的函数构成了一个完整的系列,绝大多数函数的名字都是以“pthread_”开头的。要使用这些函数库,要通过引入头文<pthread.h> 。链接这些线程函数库时要使用编译器命令“-lpthread”选项。

🐕创建线程

功能:创建一个新的线程

原型

int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void*), void *arg);

参数

thread:返回线程IDattr:设置线程的属性,attr为NULL表示使用默认属性start_routine:是个函数地址,线程启动后要执行的函数arg:传给线程启动函数的参数(可以是对象等等)返回值:成功返回0;失败返回错误码

错误检查:

传统的一些函数是,成功返回0,失败返回-1,并且对全局变量errno赋值以指示错误。pthreads函数出错时不会设置全局变量errno(而大部分其他POSIX函数会这样做)。而是将错误代码通过返回值返回pthreads同样也提供了线程内的errno变量,以支持其它使用errno的代码。对于pthreads函数的错误,建议通过返回值判定,因为读取返回值要比读取线程内的errno变量的开销更小。

注意:新线程和主线程谁先运行?这是未知的。

示例代码:

#include <iostream>

#include <unistd.h>

#include <pthread.h>

void *routine(void *name)

{

std::cout << "i am new thread,name:" << (char *)name << std::endl;

sleep(1);

return nullptr;

}

int main()

{

pthread_t tid;

pthread_create(&tid, nullptr, routine, (void *)("thread-1"));

while (true)

{

std::cout << "i am process,pid:" << getpid() << std::endl;

sleep(1);

}

return 0;

}

运行结果:

<code>🐟指令查看轻量级进程

指令:ps -aL

根据上面的结果可知:他们的pid一样,所以属于同一个进程,而且可知:<code>OS在进行调度的时候,用的是LWP进行调度的。(因为pid可能一样,不能唯一性标识)

🐒线程ID及进程地址空间布局

pthread_ create函数会产生一个线程ID,存放在第一个参数指向的地址中。

前面讲的线程ID属于进程调度的范畴。因为线程是轻量级进程是操作系统调度器的最小单位,所以需要一个数值来唯一表示该线程。

pthread_ create函数第一个参数指向一个虚拟内存单元,该内存单元的地址即为新创建线程的线程ID,属于NPTL线程库的范畴。线程库的后续操作,就是根据该线程ID来操作线程的。

线程库NPTL提供了pthread_ self函数,可以获得线程自身的ID

pthread_t pthread_self(void);

pthread_t 到底是什么类型呢?

取决于实现。对于Linux目前实现的NPTL实现而言,pthread_t类型的线程ID本质就是一个进程地址空间上的一个地址

线程的管理库维护,即描述线程的结构体与管理线程的数据结构(类似于数组)就在库里面的,其中pthread_t tid即是描述tid线程结构体的起始地址

线程的栈与局部存储是在库中维护的。虽然栈是线程独立的,但是其他线程也是可以访问的。

示例代码:

<code>#include <iostream>

#include <unistd.h>

#include <pthread.h>

#include <string.h>

#include <stdio.h>

std::string ToHex(pthread_t tid) //将id转换为十六进制

{

char buff[64];

snprintf(buff, 64, "0x%lx", tid);

return buff;

}

void *routine(void *name)

{

while (true)

{

sleep(1);

std::cout << "i am new thread,name:" << (char *)name << " my tid is :" << ToHex(pthread_self()) << std::endl;

}

return nullptr;

}

int main()

{

pthread_t tid;

pthread_create(&tid, nullptr, routine, (void *)("thread-1"));

while (true)

{

std::cout << "i am process,pid:" << getpid() << " new thread id:" << ToHex(tid) << std::endl;

sleep(1);

}

return 0;

}

运行结果:

在这里插入图片描述

<code>🦔__thread与线程的局部存储

__thread 是 C 和 C++ 中用于声明线程局部存储的一个关键字(或属性,具体取决于编译器和平台)。它告诉编译器或链接器,被 __thread 修饰的变量是每个线程私有的,即每个线程都有该变量的一个独立实例,这些实例之间互不影响。如:__thread int tls_variable = 0; 一般对于全局变量使用。

示例代码:

#include <pthread.h>

#include <stdio.h>

// 声明一个线程局部变量

__thread int tls_variable = 0;

void* thread_function(void* arg)

{

tls_variable = (int)arg; // 每个线程都会修改它自己的 tls_variable 副本

printf("Thread %ld has tls_variable = %d\n", (long)pthread_self(), tls_variable);

return NULL;

}

int main() {

pthread_t threads[2];

// 创建两个线程,每个线程将接收到一个不同的参数

pthread_create(&threads[0], NULL, thread_function, (void*)1);

pthread_create(&threads[1], NULL, thread_function, (void*)2);

// 等待两个线程完成

pthread_join(threads[0], NULL);

pthread_join(threads[1], NULL);

return 0;

}




声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。