1.线程是进程内的一个执行分支,比进程的划分更加细节,线程是进程内的一个控制序列
2.一个进程至少有一个执行线程
3.线程本质在进程内部运行,本质是在进程地址空间中运行
4.在linux中,cpu看到的PCB比传统进程更加轻量化
5.将进程资源合理高效的分给每个执行流,也就是线程执行流
6.linux中的线程执行进程代码的一部分
7.cpu不知道调度的是进程还是线程,因为它只在意调度执行流
8.线程是操作系统调度的基本单位
9.原来,进程分为PCB(内核数据结构)和代码数据,现在,进程可以进一步划分为PCB,进程地址空间和页表,进程是承担分配系统资源的基本实体,进程分配一块进程地址空间给线程,线程是进程内部的执行流资源
10.重新理解进程:操作系统以进程为单位分配系统资源,可以理解为进程内部只有一个执行流(特殊情况),部分系统如windows还存在对线程先描述再组织的队列,属于是进程套进程,比较复杂
11.linux基于windows系统改进了线程与进程的关系,线程会复用进程的数据结构和管理算法(多的不执行即可),维护成本大量降低
12.struct task_struct模拟线程,linux没有真正意义上的线程(没有为它创建真正意义上的PCB)而用进程的数据结构模拟的线程
在cpu中,线程<=执行流<=进程linux中的执行流,轻量级进程
13.页表
虚拟地址是如何转化为物理地址的?以32位虚拟地址为例
页表对虚拟地址进行拆分,10+10+12进行分级作用,虚拟地址查一级,查二级,查页表,二级页表大部分是用不完的,任何一个进程都必须有页目录,二级页表可以残缺
如果MMU发现请求的虚拟页面不在物理内存中,则触发缺页中断(页面加载到内存)。操作系统内核响应这个中断,将所需的页面从磁盘或其他存储设备加载到物理内存中,并更新相应的页面表条目
如起始地址+虚拟地址的后12位(即页框中的偏移量)=物理地址
任何一个类型取地址都只有一个地址,多字节就取开辟空间的第一个字节的地址
虽然已经对进程进行了轻量化优化,但创建进程仍然是一项艰苦的工作
寻址:起始地址+类型 =起始地址+偏移量(X86 的特点)
CR2:记录引起缺页中断或者异常的地址
如何理解资源分配给每个执行流?线程目前分配资源,本质就是分配地址空间范围
怎么让每个线程执行不同的代码?代码有地址(是虚拟地址)
14.linux线程周边的概念
线程比进程更加轻量化的体现:
1.创建和释放更加轻量化
2.切换更加轻量化(运行),对应的页表和进程地址空间都不会切换
调度线程那还要调用进程吗?
线程的执行本质上就是进程在执行,线程是进程的一个分支,线程在执行,线程内的切换不需要重新 cache 数据
线程完成之后要实现进程的切换,就需要 cache 缓存数据的切换
对于时间片的管理,主线程记录
15.线程的优点
1.创建一个新线程的代价比创建一个新进程小的多
2.与进程切换相比,线程切换带来的操作系统工作量少很多
3.线程占用的资源比进程要小很多
4.充分利用多处理器的可并行数量
5.在等待慢速 I/O 操作结束的同时,程序可执行其他的计算任务
6.计算密集型应用,为了能在多处理器系统上运行,将计算分配到多个线程中实现
7.I/O密集型应用,为了提高性能,将 I/O 操作重叠。线程可以同时等待不同的 I/O 操作
16.线程的缺点
1.性能损失:一个很少被外部事件阻塞的计算密集型线程往往无法与其他线程共享一个处理器。如果计算密集型线程的数量比可用的处理器多,那么可能会有较大 的性能损失,这里的性能损失指的是增加了额外的同步和调度开销,而可用的 资源不变
2.健壮性降低:编写多线程需要更全面更深入的考虑,在一个多线程程序里,因时间分配 上的细微偏差或者因共享了不该共享的变量而造成不良影响的可能性是很大 的,换句话说线程之间是缺乏保护的
3.缺乏访问控制:进程是访问控制的基本粒度,在一个线程中调用某些 OS 函数会对整个进 程造成影响
4.编程难度提高:编写与调试一个多线程程序比单线程程序困难得多
17.线程异常:单个线程出现除零,野指针问题导致线程崩溃,进程也随之崩溃,线程是进程的分支,线程出异常,就类似进程出异常,进而触发信号机 制,终止进程,进程终止,该进程内的所有线程也就随即退出
18.线程用途
1.合理的使用多线程,能提高 CPU 密集型程序的执行效率
2.合理的使用多线程,能提高 IO 密集型程序的用户体验(例如视频可以边下边看)
19.linux进程vs线程
进程和线程:
1.进程是资源分配的基本单位
2.线程是调度的基本单位
3.线程共享进程数据,但也拥有自己的一部分数据:线程ID,一组寄存器(独立的上下文),栈(执行流不会错乱),errno,信号屏蔽字,调度优先级,线程上下文和栈确保了独立性
20.进程的多个线程共享
同一地址空间,因此 Text Segment、Data Segment 都是共享的,如果定义一个函数,在 各线程中都可以调用,如果定义一个全局变量,在各线程中都可以访问到,除此之外,各线 程还共享以下进程资源和环境:文件描述符表,每种信号的处理方式(SIG_ IGN、SIG_ DFL 或者自定义的信号处理函数),当前工作目录,用户ID和组ID
测试
1: c1.cc ? ? ?? buffers
1 #include <iostream>
2 #include <pthread.h>
3 #include <unistd.h>
4
W> 5 void *threadRun(void* args)
6 {
7 while(1)
8 {
9 std::cout << "new thread: " << getpid() << std::endl;
10 sleep(1);
11 }
12 return NULL;
13 }
14
15 int main()
16 {
17 pthread_t tid;
18 pthread_create(&tid, NULL, threadRun, NULL);
19
20 int t=10;
21 while(t--)
22 {
23 std::cout << "main thread: " << getpid() << std::endl;
24 sleep(1);
25 }
26 }
NORMAL ??
看看豆哥的分析:该代码演示了如何在C++程序中使用POSIX线程(pthread)库来创建一个新的线程,并使该线程与主线程并行执行。主线程和新创建的线程都会周期性地打印出它们的“身份”
threadRun:每隔一秒打印进程PID
main:tid去存储线程的标识符
然后打印十次主线程的标识符
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg);
参数分析:
1.thread:存储线程标识符的指针
2.attr:存储线程属性,非必要可以写为NULL
3.中间的void*函数:线程运行函数,当线程启动时会执行这个函数直到它返回
4.arg:用于向线程运行函数传递数据,不需要则写完NULL