程序、进程、线程详解

程序是编译好的二进制文件,在磁盘上,不占用系统资源。
进程是运行的程序,占用系统资源,在内存中执行。
线程是轻量级的进程,本质任是进程(在Linux环境下)
线程和进程的区别和联系:
(1)进程有独立的地址空间,拥有PCB
(2)线程也有自己的PCB,但是没有独立的地址空间
区别:是否共享地址空间,例如,进程a.out有自己的0到4G的地址空间,当它调用pthead_create创建线程的时候,线程的PCB也是存在a.out的0到4G的地址空间中的,此时进程a.out就变成了线程,和它新创建出来的线程共享地址空间,但是有各自的PCB。
Linux下,线程是最小的执行单位,进程是最小的分配资源的单位。
为了更好的解释这句话,先引入以下几个知识点:
单道程序设计:cpu在同一时间只能执行一个任务
多道程序设计:进程分为多个时间片,轮流使用cpu资源
程序、进程、线程详解_第1张图片
**并发:**一个时间段中有多个进程都处于已启动运行到运行完毕之间的状态,但任一个时刻点上仍只有一个进程在运行。可以看成两个或多个事件在同一时间间隔发生,如以正常速度的N倍吃一口饭喝一口水。
**并行:**指两个或者多个事件在同一时刻发生,如吃饭的时候可以边吃饭边打电话。
总之,加入有1万个请求同时过来,很明显不可能真正的同时去处理这1万个请求,如果这台机器的处理器有4个核心,不考虑超线程,那么我们认为同时会有4个线程在跑。也就是说,并发访问数是1万,而底层真实的并行处理的请求数是4。所以,并发只是分时复用,由于计算机执行速度快,可以把它看做宏观上的并行,微观上的串行,就是上面的多道程序设计模型,并行是多核处理器上真正的同时执行。
现在回到上面那句话的理解,如下图所示,当没有红线指向的区域时,三个进程在CPU中轮流执行,当第一个进程变成线程后,也就是红线指向的地方,CPU分配时间的时候,把最左边的当做五份来分,也就是原来CPU把自己分成三份,现在分成七份,最左边的线程占五份,所以它在相同的时间内能获得更多的时间轮片,这也就是为什么多线程效率更高的原因。因此,线程是最小的执行单位。其次,那五个线程共享地址共享,因此分配资源的时候是按照进程划分的。
程序、进程、线程详解_第2张图片
此外,从内核角度看,进程和线程是一样的(因为内核以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 
#include 
#include 
#include 
#include 

void *thrd_func(void *arg){
    int i = (int)arg;
    sleep(i);
    printf("%dth thread: thread id = %lu, pid = %u\n",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
程序、进程、线程详解_第3张图片

线程就是轻量级进程

Linux的线程就是轻量级进程只是从核心态空间的层面来看。在Linux诞生的时候还没有线程的概念,(LINUX是在1991诞生的)。随着多核CPU的诞生,多线程能充分发挥多核CPU的优势。但是修改操作系统去支持线程是一个很大的工程,所以线程的作者采用了库函数来实现线程。但是采用库函数实现线程的话,只是模拟了线程,并没有完全实现线程的理论。比如线程不能分配到多核上,在内核看来只是一个进程。因此进程可以直接调用系统函数,而线程调用的是库函数。
目前Linux内核实现的角度来看,在内核中会通过一个轻量级的进程来管理,线程调度相当于进程调度。当前Linux内核中,无论是创建一个线程还是一个进程都是会调用clone()系统调用,只不过是参数不同,创建线程的话clone()的参数是(CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND),其中CLONE_VM也指定了核心态空间中,此轻量级进程(即线程)和父进程共享地址空间,因此这个线程才可以访问父进程的地址空间。

你可能感兴趣的:(Linux)