您的位置:首页 > 健康 > 养生 > 能打开国家禁止网站的浏览器_婚纱摄影网站策划书_保温杯软文营销300字_seo搜索引擎优化案例

能打开国家禁止网站的浏览器_婚纱摄影网站策划书_保温杯软文营销300字_seo搜索引擎优化案例

2025/5/8 1:09:34 来源:https://blog.csdn.net/klausur31/article/details/147209014  浏览:    关键词:能打开国家禁止网站的浏览器_婚纱摄影网站策划书_保温杯软文营销300字_seo搜索引擎优化案例
能打开国家禁止网站的浏览器_婚纱摄影网站策划书_保温杯软文营销300字_seo搜索引擎优化案例

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 10vector<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 10vector<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位改变,因此线程推出时并不会涉及到信号,因为当线程异常时,如果有信号,那么会杀死整个进程。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com