• 深入理解操作系统之线程


    目录

    补充进程知识:

    是什么触发了进程的切换?

    进程切换时要做什么?

    切换进程的时机分为主动和被动。

    进程上下文指的是什么?

    线程篇:

    以进程创建子进程为例引入线程:

    线程定义:

    Motivation:

    进程区别:

    多线程优点:

    定义:

    多线程模型:

    多核编程:

    ULT(用户级线程):

    KLT(内核线程):

    组合方式:

    多对一模型:

    一对一模型(主流):

    多对多模型:

    多线程实验:

    实验一代码:

     Monte Carlo技术计算Π值:

    非多线程的计算方法:


    补充进程知识:

    在上篇文章中的一张图提到:

    是什么触发了进程的切换?

    是中断触发了进程的切换

    进程切换时要做什么?

    进程的切换类似于中断,也需要进行模式切换,如:

    p1进程正在执行,在某点要被切换,就会被中断,进而模式切换到操作系统的内核态,调用中断处理程序(switch--专门处理进程切换的中断处理程序)

    切换进程的时机分为主动和被动。

    主动:启动IO设备

    被动:高优先级进程的调度、cpu时间片到期

    进程上下文指的是什么?

    其实就是就进程在内存中所包含的内容:

    线程篇:

    以进程创建子进程为例引入线程:

    通过fork函数创建了子进程,由上一篇文章可知会把父进程的数据以及代码全部进行克隆。

    创建子进程的好处是什么?

    得到了两个不同的通过利用fork在父子进程中的返回值不同使用if分支语句进行区分并发的执行流。

    创建子进程的坏处是什么?

    资源浪费(内存空间)

     有了缺点那本章的问题就来了,就是如何解决坏处,而且保留好处,即两条执行流且不浪费资源,我们可以叫它Thread---线程  

    对线程直观的定义可以认为线程是进程中的执行流

    线程定义:

    Motivation:

    一个应用通常要处理很多任务,比如一个Web浏览器,可能需要同时处理文 字和图片,这些同时执行的任务可称为 “执行流” ,我们不希望它们是顺序执行的。在web中我们不能说让上述任务处理完文字再处理图片吧,这是不符合用户的使用的。因此需要多任务并发执行早期每个执行流就是创建子进程,但是进程的切换是要消耗巨大的时空资源的,所以把一个应用装进进程中,进程内部的执行流就是线程。

    进程区别:

    多线程优点:

    响应性:当一个服务器负责监听进程(Listen process)一个用户发出请求,则在进程中创建一条执行流(线程)来响应请求,当另一个用户发出请求,服务器在进程中新创建一个线程作为响应,两个用户所访问的是同一个数据,如果是在单线程进程中,就需要创建新的进程,这开销是巨大的,引入多线程进程后,只需要消耗小部分资源,从上个线程中复制处理。

    资源共享:进程间的资源共享是靠进程间的通信来完成的,但是线程间的资源共享,同属于一个进程的线程是可以直接访问的。

    经济:相比进程的创建,线程的消耗是很小的。

    可伸缩性:在多处理机系统中,有多个cpu,一个多线程进程可以将多个线程进行分配到多个处理机上,让他们并行执行。可以根据系统的环境来调整所占处理机的数量。

    定义:

    * A thread is a basic unit(单元) of CPU utilization(利用、使用); it comprises(包含) a thread id, a program counter, a register set, and a stack.
    ---- 一个线程是利用CPU的基本单元,它包含一个线程id、一个pc(程序计数器)、一个寄存器、一个栈。(线程拥有CPU的使用权是由OS分配给他的,并不属于它的资源)
    * It shares with other threads belonging to the same process(属于同一个进程) its code section, data section, and other operating-system resources(os资源) , such as open files and signals.
    ----   它和属于同一个进程的其他线程共享代码部分、数据部分和其他的系统资源,例如打开的文件和信号。
    * A traditional (or heavyweight ) process has a single thread of control(控制流). If a process has multiple threads of control, it can perform(运行,表演)  more than one task at a time。

    ---- 一个传统的进程有一个单线程控制流,如果一个进程有多线程控制流,同一时间它能运行不止一个任务。

    多线程模型:

    多核编程:

            多核编程与前边的多线程的优点中的可伸缩性相似,在多处理器系统中,多核编程机制让应用程序可以更有效地将自身的多个执行任务(并发的线程)分散到不同的处理器上运行,以实现并行计算。

    ULT(用户级线程):

    ULT在user mode下运行,它的管理无需内核支持。用户级线程一个系统中可以有很多个,但是设置用户级线程的的系统的调度单位是进程

    优点:

    1)对于同一个进程的线程,线程的切换是不需要切换到内核空间

    2)用户级线程的实现与OS平台无关,对于线程管理的的代码是属于用户程序的一部分,应用程序是共享的。

    缺点:

    1)系统调用引起的阻塞问题,大多系统调用都会使系统阻塞,当用户线程系统调用的时候也会使整个进程阻塞。而内核支持线程,是只有该线程阻塞。

    2)用户级线程是不具有伸缩性的,每个进程还是只会分到一个cpu,则只有一个线程拥有使用权。(设置用户级线程的OS的调度单位是进程)

    KLT(内核线程):

    KLT在kernel mode下运行,由操作系统支持与管理。是在内核空间实现的,内核空间给每个内核线程设置了一个线程控制块(TCB),设置KLT的操作系统的调度单位是线程。

    优点:

    1)在多处理机系统中,可以调度同一个进程中的多个线程同时使用CPU(并行工作)

    2)如果某个进程被阻塞,则内核可以调度该进程的其他线程使用CPU,也可以调度其他进程的其他线程使用CPU。

    3)KTL具有很小的数据结构与堆栈,进程切换较快,开销较小。

    4)内核本身可以采用多线程技术,可以提高运行速度和效率。

    缺点:

    对于用户级线程来说,线程调度的时候需要进行模式切换(用户--内核),开销较大。

    由于上边两者都有优缺点,因此采用他们组合的方式进行互补。

    组合方式:

    多对一模型:

    组合方式即将用户级别线程对内核线程进行绑定,在多对一的模型中这里的多是多个用户线程(一般属于一个进程),一 指的是一个内核线程,虽然多对一,但是每次只能一个用户线程对内核线程进行映射,即同一时刻只有一个用户线程可以访问内核,当发生阻塞时,整个进程都会阻塞。

    一对一模型(主流):

     一对一模型是为每一个用户进程都绑定一个核心进程进行映射,并发性较好,当线程阻塞时,其他线程依然可以访问内核。但是系统开销较大,每设一个用户线程都要创建一个内核线程,因此需要控制线程数量。

    多对多模型:

    多对多模型,即将许多用户线程映射到同样数量或更少数量的内核线程上。内核控制线程的数目可以根据应用进程和系统的不同而变化,可以比用户线程少,也可以与之相同。该模型结合上述两种模型的优点, 它可以像一对一模型那样,使一个进程的多个线程并行地运行在多处理机系统上,也可像多对一模型那样,减少线程的管理开销和提高效率。 节省了内核开销,但是实现复杂,中间需要多了一层分配器。

    多线程实验:

    (1)使用Pthreads库创建多个线程,并观察线程的并发执行现象以及数据共享关系

    (2)Monte Carlo技术计算Π值(多线程)

                    π = 4 *(圆内点数)  /(总的点数)

    创建线程代码及解释:

    1. #include
    2. #include
    3. #include
    4. #include
    5. void* threadFunc(void* arg){ //线程函数
    6. printf("In NEW thread\n");
    7. }
    8. int main()
    9. {
    10. pthread_t tid;
    11. pthread_create(&tid, NULL, threadFunc, NULL);
    12. pthread_join(tid, NULL);
    13. printf("In main thread\n");
    14. return 0;
    15. }

    pthread_create(&tid, NULL, threadFunc, NULL)函数讲解:

    第一个参数:第一个参数是用来存储新创建的线程的线程id的地址

    第二个参数:第二个参数是指定线程的属性地址

    第三个参数:第三个参数是线程执行任务函数的地址(函数执行的起始地址)

    第四个参数:第四个参数是传给线程函数参数的地址

    该函数结束之后会立即返回,继续执行。

    pthread_join(tid, NULL);函数讲解:

    它是等待函数,等待指定线程结束后才结束。

    执行结果:

    将 pthread_join(tid, NULL)注释后执行结果:

    造成这种结果的原因是主线程没有等待子线程结束后再结束,main线程结束后会回收所有资源。

    实验一代码:

     运行结果:

    结果可知两个线程是并发执行的

    上图是解决的线程并发问题,下边是解决的数据共享问题:

     代码:

     在代码中添加了一个全局变量,通过打印全局变量来进行分析

    运行结果:

     这里与父子进程的数据就产生了差别。

     Monte Carlo技术计算Π值:

    intervals变量是随机产生的点的数量

    rand_x是随机产生点的横坐标

    rand_y是随机生成点的纵坐标

    随机生成点是通过rand函数生成,生成的数是在[0,RAND_MAX],每个系统都有自己的RAND_MAX,根据表达式,最后生成的x,y的坐标在单位圆中。

    circle_points是落入圆内的点数

    square_point是总共的点数

    非多线程的计算方法:

    多线程实现:

    通过多线程计算Π值,将线程函数单独实现,在main函数中调用。

    在main函数中设置了两个数组,数组1存储线程号(跟随for循环的i值得变化而变化)、数组2存储得是调用函数得参数(该参数在线程中控制循环得次数,传入得是地址类型,在线程中转换成int*类型,再进行解引用)(值也是随for循环得i值而变化)

    在mian函数中arg+i加的是一个int类型得地址(四个字节)(arg代表的是数组的首地址)。

     运行结果:

     通过时间对比可知,单线程的开销比多线程的开销较小(线程的切换),当然这个是跟你给虚拟机所分配的cpu有关,在多个cpu实现多线程的时候,真正的消耗的时间是减小的,但是用户的时间是不变得,毕竟任务量是一定的。

  • 相关阅读:
    git的实际操作
    php-java-net-python-北京名胜古迹展览网站计算机毕业设计程序
    springboot+vue+mybatis学生日常行为评分管理系统+PPT+论文+讲解+售后
    【OJ每日一练】1017 - 计算蔬菜总价
    设计模式:迭代器模式
    解决SpringBoot项目war部署到tomcat下无法Nacos中注册服务问题
    因特网中的电子邮件系统(SMTP)
    计算机网络之传输层------TCP协议详解
    注意力机制 - 注意力评分函数
    有向图缩点题集
  • 原文地址:https://blog.csdn.net/m0_61469860/article/details/133521443