操作系统——多线程

每个线程是CPU使用的一个基本单元:它包括线程ID,PC,寄存器,堆栈。
它与同一进程的其他线程共享代码段,数据段,操作系统资源,但是不共享寄存器,pc以及堆栈,同一进程下的不同线程的寄存器,pc,堆栈是不同的地址空间。

并发编程

数据并行和任务并行

数据并行是一种并行计算的模式,其中数据被分割成多个部分,每个部分在独立的处理单元上同时处理。

任务并行可以是多个线程操作一个数据,主要区别是将任务划分开了,但是操作的是一个数据。

多线程模型

用户态和内核态
大多数应用程序和用户任务在用户态下运行,用户态下运行的程序只能访问自己的地址空间,同时只有在经过操作系统授权后才能使用硬件。
而内核态可以全局访问硬件和内核空间,拥有更高的权限

内核空间通常是操作系统的一部分,它包含了内核代码和数据结构。与内核空间相对应的是用户空间(User Space),用户空间包含运行在操作系统之上的用户程序的代码和数据。

用户线程与内核线程
主要区别:两者使用的内存空间位置不同,用户线程的内存由用户程序管理,而内核线程由操作系统管理。

使用线程库创建的就是用户线程,而使用操作系统函数创建的是内核线程。
用户进程可以访问其自身的内存空间,包括代码段、数据段和堆栈。这是因为操作系统为每个用户进程提供了独立的虚拟内存空间,以确保进程之间的隔离。

内核进程通常具有更高的特权级别,因此它们可以访问所有的内存空间,包括用户空间和内核空间。

内核线程由操作系统直接管理。在这种多对一的映射模型中,多个用户进程共享同一个内核进程的地址空间,内核空间就是一些数据结构和操作硬件的代码。

用户,线程为什么要依靠内核线程(为什么不直接使用用户线程)
当用户线程被阻塞(比如进行IO调用),整个进程被阻塞,操作系统无法感知进程内部被阻塞,操作系统无法直接切换另一个进程,因为控制权在用户空间内。所以用户线程无法完成并发。

常见的线程库,比如java的线程库和POSIX,win32线程库都是使用用户线程映射到内核线程的模型的。当创建用户线程时,内核线程也被创建。其中模型包括:一对一,一对多,多对多模型。

用户线程是如何映射到内核线程的?
当用户线程被创建时,对应的内核线程也被创建,并由内核线程调度器进行管理。用户线程负责在用户空间管理线程的创建,编辑等状态操作以及和内核线程的联系,而内核线程调度器负责在内核空间进行实际的线程调度(中断,多线程并发,切换,抢占)。

  • 多对一模型

映射多个用户线程到一个内核线程,不过,如果一个任务阻塞,那么整个模型都会阻塞,并且不能跑在多处理器上。不常用。

  • 一对一模型

每个用户线程映射到一个内核线程。在一个线程执行阻塞,其他线程仍然能运行。

但是每个用户线程都会创建一个内核线程,创建内核线程会影响性能。

Linux,windows在用。

  • 多对多模型

用户线程想执行,就调用一个内核线程,有一些内核线程放在那里供用户线程选择。

线程库

就是对线程的操作做了一些封装,不同操作系统不同语言都有不同的线程库。

常见的有:C++线程库,java线程库,windows线程库

目前主要使用的三个线程库:

(1)Pthreads:

提供用户级或者内核级操作,是UNIX和Linux操作系统使用的线程库。

(2)Windows:

用于windows操作系统的内核级线程库。

(3)Java:

java线程使用虚拟机的线程,而虚拟机的线程使用操作系统的线程库,windows使用windows线程,Linux使用Pthreads线程库。

多线程创建的两者方式:

(1)异步线程

父线程创建子线程后,父线程就恢复自身的执行,父线程就不知道子线程的执行情况了,他们是独立的。

(2)同步线程

父线程在创建子线程后,会等待子线程返回。

Pthreads线程

使用c的pthread.h开发

windows线程

使用windows.h开发。

多线程问题

(1)信号是由内核进程生成并发送给用户进程的。这可以是由于多种原因,包括以下情况:

  1. 外部中断:例如,当硬件设备报告错误、设备准备好接收数据、定时器到期等情况时,内核可以生成相应的信号并发送给受影响的用户进程。

  2. 用户请求:用户进程可以通过执行系统调用(例如,kill)请求操作系统向其他进程发送信号。

  3. 内核事件:内核可能会在某些情况下生成信号,例如,进程访问了非法内存区域(SIGSEGV信号)、子进程状态发生变化(SIGCHLD信号)等。

内核通过向进程的进程控制块(Process Control Block)或其他相关数据结构中的信号队列发送信号,然后将信号传递给相应的用户进程。用户进程可以注册信号处理函数来响应这些信号,或者使用默认操作来处理它们。

总之,信号通常是由操作系统的内核进程生成并发送给用户进程的,用于通知进程发生了特定事件或条件,以便进程可以采取适当的行动。

(2) 信号处理

信号是由特定事件产生,发送给进程,进程收到信号就会中断,然后执行信号函数一收到就处理。

非法访问内存信号,被除0信号,中断信号。

也有用户自定义的信号。

有对应的信号处理程序。

(3)线程本地存储

就是给一个线程独立的存储区域,就比如javaThread的Local

(4)子线程共享进程内存空间

在一般情况下,当在一个进程中创建线程时,新创建的线程会继承父进程的内存空间,包括全局变量、堆内存、和栈内存。这意味着新线程可以访问和共享父进程中的相同内存区域。这是因为线程是进程内的执行单元,它们通常共享相同的地址空间。
例如,c++创建子线程,子线程可以直接使用全局变量。

你可能感兴趣的:(linux,网络,系统架构,windows,ubuntu,java)