zoukankan      html  css  js  c++  java
  • 程序、进程、线程详解

    转至:https://blog.csdn.net/IT_10/article/details/90180949?utm_medium=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.control&dist_request_id=2902e361-fc4d-4303-bc1e-68466b404474&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.control

    程序是编译好的二进制文件,在磁盘上,不占用系统资源。
    进程是运行的程序,占用系统资源,在内存中执行。
    线程是轻量级的进程,本质任是进程(在Linux环境下)
    线程和进程的区别和联系:
    (1)进程有独立的地址空间,拥有PCB
    (2)线程也有自己的PCB,但是没有独立的地址空间
    区别:是否共享地址空间,例如,进程a.out有自己的0到4G的地址空间,当它调用pthead_create创建线程的时候,线程的PCB也是存在a.out的0到4G的地址空间中的,此时进程a.out就变成了线程,和它新创建出来的线程共享地址空间,但是有各自的PCB。

    服务注册到系统接受系统统一管理的程序,一般没有界面,运行在后台。
    Linux下,线程是最小的执行单位,进程是最小的分配资源的单位。
    为了更好的解释这句话,先引入以下几个知识点:
    单道程序设计:cpu在同一时间只能执行一个任务
    多道程序设计:进程分为多个时间片,轮流使用cpu资源

    **并发:**一个时间段中有多个进程都处于已启动运行到运行完毕之间的状态,但任一个时刻点上仍只有一个进程在运行。可以看成两个或多个事件在同一时间间隔发生,如以正常速度的N倍吃一口饭喝一口水。
    **并行:**指两个或者多个事件在同一时刻发生,如吃饭的时候可以边吃饭边打电话。
    总之,加入有1万个请求同时过来,很明显不可能真正的同时去处理这1万个请求,如果这台机器的处理器有4个核心,不考虑超线程,那么我们认为同时会有4个线程在跑。也就是说,并发访问数是1万,而底层真实的并行处理的请求数是4。所以,并发只是分时复用,由于计算机执行速度快,可以把它看做宏观上的并行,微观上的串行,就是上面的多道程序设计模型,并行是多核处理器上真正的同时执行。
    现在回到上面那句话的理解,如下图所示,当没有红线指向的区域时,三个进程在CPU中轮流执行,当第一个进程变成线程后,也就是红线指向的地方,CPU分配时间的时候,把最左边的当做五份来分,也就是原来CPU把自己分成三份,现在分成七份,最左边的线程占五份,所以它在相同的时间内能获得更多的时间轮片,这也就是为什么多线程效率更高的原因。因此,线程是最小的执行单位。其次,那五个线程共享地址共享,因此分配资源的时候是按照进程划分的。

    此外,从内核角度看,进程和线程是一样的(因为内核以PCB作为区分),都有各自不同的PCB。
    ps -Lf PID可以查看指定程序的线程数,下图LWP是线程号,是CPU分配时间片的依据,不是线程ID。
    ps -aux 查看所有运行的程序的PID等信息。

    要创建一个线程,必须先有进程,那么,同一个虚拟地址空间中的线程之间共享那些资源,独享那些资源?
    共享:
    (1)文件描述符表、打开的文件
    (2)每种信号的处理方式
    (3)当前工作目录
    (4)用户ID和组ID
    (5)内存地址空间(.text .data .bss heap 除栈空间)
    独享:
    (1)线程id
    (2)处理器现场和栈指针(内核栈)
    (3)独立的栈空间(用户空间栈,如函数调用栈帧)
    (4)error变量
    (5)信号屏蔽字
    (6)调度优先级
    线程优缺点:
    优点:提高程序并发性,开销小,数据通信共享数据方便
    缺点:库函数不稳定(进程中用的函数都是系统调用,而线程中使用的大部分都是库函数,稳定性相对进程低),调试编写困难(gdb不支持),对信号支持不好
    创建n个线程实例

    #include <stdlib.h>
    #include <stdio.h>
    #include <unistd.h>
    #include <string.h>
    #include <pthread.h>
    
    void *thrd_func(void *arg){
    int i = (int)arg;
    sleep(i);
    printf("%dth thread: thread id = %lu, pid = %u
    ",i,pthread_self(),getpid());
    return NULL;
    }
    
    int main(void){
    pthread_t tid;
    int ret,i;
    
    for(i = 0; i < 5; i++){
    //区别于进程,线程创建成功后会去执行自己的代码,即main函数里面的代码都
    是属于主控线程的,所以不会出现创建出来的线程又会创建线程的循环
    ret = pthread_create(&tid,NULL,thrd_func,(void *)i);
    if(ret != 0){
    fprintf(stderr,"pthread_create error:%s",strerror(ret));
    exit(1);
    }
    }
    
    sleep(i);//防止进程在线程之前退出
    return 0;
    }

    注意,gcc编译的时候需要带上参数-lpthread


    线程就是轻量级进程

    Linux的线程就是轻量级进程只是从核心态空间的层面来看。在Linux诞生的时候还没有线程的概念,(LINUX是在1991诞生的)。随着多核CPU的诞生,多线程能充分发挥多核CPU的优势。但是修改操作系统去支持线程是一个很大的工程,所以线程的作者采用了库函数来实现线程。但是采用库函数实现线程的话,只是模拟了线程,并没有完全实现线程的理论。比如线程不能分配到多核上,在内核看来只是一个进程。因此进程可以直接调用系统函数,而线程调用的是库函数。
    目前Linux内核实现的角度来看,在内核中会通过一个轻量级的进程来管理,线程调度相当于进程调度。当前Linux内核中,无论是创建一个线程还是一个进程都是会调用clone()系统调用,只不过是参数不同,创建线程的话clone()的参数是(CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND),其中CLONE_VM也指定了核心态空间中,此轻量级进程(即线程)和父进程共享地址空间,因此这个线程才可以访问父进程的地址空间。
    ————————————————
    版权声明:本文为CSDN博主「IT_10-」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    原文链接:https://blog.csdn.net/IT_10/article/details/90180949

  • 相关阅读:
    [React Native] Target both iPhone and iPad with React Native
    [Angular] Increasing Performance by using Pipe
    [Angular] Angular CDK Intro
    [React] Refactor componentWillReceiveProps() to getDerivedStateFromProps() in React 16.3
    [Python] Create a minimal website in Python using the Flask Microframework
    [GraphQL] Apollo React Mutation Component
    [Angular] Introduce to NGXS
    《火球——UML大战需求分析》(第2章 耗尽脑汁的需求分析工作)——2.4 UML助力需求分析
    《火球——UML大战需求分析》(第2章 耗尽脑汁的需求分析工作)——2.5 小结与练习
    [Django实战] 第4篇
  • 原文地址:https://www.cnblogs.com/my-first-blog-lgz/p/14451129.html
Copyright © 2011-2022 走看看