每个线程是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)同步线程
父线程在创建子线程后,会等待子线程返回。
使用c的pthread.h开发
使用windows.h开发。
(1)信号是由内核进程生成并发送给用户进程的。这可以是由于多种原因,包括以下情况:
外部中断:例如,当硬件设备报告错误、设备准备好接收数据、定时器到期等情况时,内核可以生成相应的信号并发送给受影响的用户进程。
用户请求:用户进程可以通过执行系统调用(例如,kill)请求操作系统向其他进程发送信号。
内核事件:内核可能会在某些情况下生成信号,例如,进程访问了非法内存区域(SIGSEGV信号)、子进程状态发生变化(SIGCHLD信号)等。
内核通过向进程的进程控制块(Process Control Block)或其他相关数据结构中的信号队列发送信号,然后将信号传递给相应的用户进程。用户进程可以注册信号处理函数来响应这些信号,或者使用默认操作来处理它们。
总之,信号通常是由操作系统的内核进程生成并发送给用户进程的,用于通知进程发生了特定事件或条件,以便进程可以采取适当的行动。
(2) 信号处理
信号是由特定事件产生,发送给进程,进程收到信号就会中断,然后执行信号函数一收到就处理。
非法访问内存信号,被除0信号,中断信号。
也有用户自定义的信号。
有对应的信号处理程序。
(3)线程本地存储
就是给一个线程独立的存储区域,就比如javaThread的Local
(4)子线程共享进程内存空间
在一般情况下,当在一个进程中创建线程时,新创建的线程会继承父进程的内存空间,包括全局变量、堆内存、和栈内存。这意味着新线程可以访问和共享父进程中的相同内存区域。这是因为线程是进程内的执行单元,它们通常共享相同的地址空间。
例如,c++创建子线程,子线程可以直接使用全局变量。