• 进程线程及其状态


    进程线程及其状态

    进程

    进程的概念

    • 进程就是执行中的程序。

    进程的状态

    进程有五种状态,分别是:

    • 新建:进程正在被创建
    • 运行:进程正在被执行
    • 阻塞:进程等待某个事件(如I/O操作)
    • 就绪:进程等待分配处理器
    • 终止:进程完成执行

    进程调度流程图

    线程

    线程的概念

    • 线程是程序执行流的最小单元,线程早期也有轻量级进程之称。一个进程中可能包含多个线程。一个进程至少包含一个线程,否则没有存在的意义。多线程里不宜再创建子进程。在系统内核层面,进程与线程并无本质的不同。进程与线程最大的不同点是资源分配。

    线程与进程的比较

    • 线程与进程都可以实现多任务。
    • 线程是CPU调度的基本单元,进程是系统资源分配的基本单元。
    • windows下进程线程是泾渭分明,区别明显的。在Linux中它们有很多共同特性。
    • 在早期Linux的内核结构中:进程和线程的区别只是创建子进程和子线程时,是否设置为共享内存,二者在内核中的存储结构并无区别,系统调度的单位也是轻量级进程。2.6以后的Linux内核版本才将线程和进程完全独立开来。
    • 线程的状态改变只代表了CPU执行过程的改变,线程操作的资源仍然是进程的。除了 CPU外,计算机内的软硬件资源的分配都是以进程为单位的。进程拥有一个完整的虚拟地址空间,不依赖于线程而独立存在,而线程只是进程的一部分,与进程内的其他线程一起共享分配给该进程的所有资源。

    线程的状态

    • 同进程的实现原理类似,线程也可主要概括为五种状态(实际上Linux将线程状态细分为十几种):
      • 新建,由于不需要进行必要的内存复制等工作,新建线程要比新建进程更快。
      • 就绪
      • 运行
      • 阻塞
      • 死亡,线程死亡后,也需要回收处理。
    • 调度的过程参考进程。

    线程的内核调度

    • 多线程编程具有响应度高、资源共享、经济和多处理器体系结构的利用四个优点。用户线程是映射到内核线程池进行CPU调度的,映射关系模型包含有:

      • 多对一
      • 一对一
      • 多对多

    内核调度图

    • 这里为什么没有一对多?因为线程是CPU资源调度的最小单位,即:单线程在一个时间点上只能利用到一个核心(进行一个原子操作),一个原子操作不能再分开由不同核心执行。而多核CPU在执行单线程任务时,可能会切换多个核心轮流来执行这个任务(每个原子操作的CPU核心可能并不相同),例如在执行循环时,这次循环和下次循环可能并不是同一个核心来执行的(这跟你的系统有关,但可以看到单线程最多只能占用到 (1/CPU核心数) 的CPU资源(超线程CPU占用1/(CPU线程数))。
    • 而资源上,多线程调用同一资源时,X86架构可能会使用总线锁,对该资源进行锁定,保证原子操作执行完整不被打断。当操作完成时,会解锁并通知其他线程,我操作完了,你们可以来操作了(实际上,此方法效率很低,仅作为最后一道保险)。
    • 因此确定一个操作是原子操作时,没有必要浪费外围昂贵的开销再来给他加锁,原子操作本身就是一道互斥锁。互斥锁的目的,也正是将一系列操作变为原子操作。

    进程的诞生与消亡

    • 进程的诞生
      • (1)fork函数:子进程拷贝父进程的数据(具体实现是读时共享,写时复制)
      • (2)vfork函数:子进程与父进程共享数据
      • vfork是一个过时的函数,虽然与fork相比有那么一点性能优势,但其带来一连串的坑并不那么好填,不建议使用,除非你对性能追求到极致。
    • 进程的消亡
      • 正常结束和异常终止;
      • 进程结束时的资源问题回收:linux系统设计时规定:每个进程结束时,系统会自动回收open但没有close的文件资源,malloc但没有free的资源等,但并不会回收进程本身占用的资源(即进程的尸体,主要包括进程本身的文件描述符对应的资源(task_struct结构体)和进程的栈空间),这需要由进程的父进程来完成回收(收尸)。
    • 僵尸进程
      • 在子进程消亡后,如果父进程没有结束,而且也不回收已结束的子进程(收尸),已经结束的子进程,就变成了僵尸进程。
      • 父进程可以使用wait或waitpid,显式地回收子进程(剩余待回收)的内存资源并且获取子进程退出状态。
      • 父进程结束时也会自动回收僵尸进程,但应避免这种不严谨的方式。
    • 孤儿进程
      • 子进程还在执行,而父进程先结束了,子进程就成为了孤儿进程,托管到系统了。
      • 此时子进程的父进程变为了系统的init进程(该进程PID为1),init进程会在孤儿进程结束后自动回收孤儿进程的资源。
    • 进程回收
      • 可以使用wait或waitpid,阻塞回收进程资源,阻塞回收具有明显的劣势,会导致阻塞的父进程不能干别的事情了。
      • Linux生产中更多的是采用信号机制,父进程注册信号,收到SIGCHLD信号才调用回收函数回收子进程资源,这样就不会导致父进程阻塞了。

    线程的诞生与消亡

    • 线程标识(线程ID)

      • 进程ID在整个系统中是唯一的。
      • 线程ID(pthread_t类型)只在它所属的进程中有效。
      • pthread_t(Linux中为unsigned int类型,OS中为结构体指针,Windows中为handle句柄)用于声明线程ID。
      • 函数:pthread_self取得自身线程ID。
    • 创建线程

      • 使用函数pthread_create,线程创建后,就开始运行相关的线程函数。
    • 退出线程。

      • 线程执行完毕。可以return,不能exit(exit是退出进程)。
      • 使用函数pthread_exit,主动退出线程。主线程使用该函数时,进程并不会结束,而是等待其他线程结束。
      • 进程结束时,线程也结束(线程依赖于其所在的进程)。
    • 线程回收

      • 由于线程使用的资源是属于进程的,退出线程而进程仍然运行时资源并未完全释放,形成僵尸线程。
      • pthread_join(tid)函数类似wait/waitpid函数,用于阻塞等待线程tid结束,调用它的线程一直等待到tid线程结束时,tid线程资源就被回收。
      • pthread_detach(tid)函数线程分离,让系统自动回收tid线程。
      • 设置线程属性进行回收。可按以下步骤回收:
        • pthread_attr_t attr;//线程创建前,定义线程属性
        • pthread_attr_init(&attr);//进行初始化线程属性
        • pthread_attr_getdetachsate(&attr,&status);//获取分离状态
        • pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);//设置线程分离状态.
        • pthread_create(&tid, &attr,func,NULL);//创建线程
        • pthread_attr_destroy(&attr);//线程结束时,调用回收函数
      • 线程回收代码示例:
      void * func(void *p)
      {
      	printf("我是子线程
      ");
      }
      int main(int argc, char *argv[])
      {
      	pthread_attr_t attr; //定义一个变量
      	pthread_t tid;
      	pthread_attr_init(&attr);//初始化
      	pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);//设置分离
      	pthread_create(&tid, &attr, func, NULL);//创建线程
      	sleep(1);//等1秒让子线程执行完
      	pthread_attr_destroy(&attr);//释放
      	return 0;
      }
      
  • 相关阅读:
    LabelImg 图像图像标注工具
    周杰伦的2000w个故事
    ROS 订阅图像节点(1)
    ROS 订阅图像节点
    ROS 双目标定
    书籍
    Z30云台PC控制问题
    大疆M600组装和试飞
    M100 X3云台安装
    M100 组装教程
  • 原文地址:https://www.cnblogs.com/linkyip/p/8168034.html
Copyright © 2020-2023  润新知