来源:CSDN  作者:王文松  转自:Linux公社

Linux下多任务介绍

首先,先简单的介绍一下什么叫多任务系统?任务、进程、线程分别是什么?它们之间的区别是什么?,从而可以宏观的了解一下这三者,然后再针对每一个仔细的讲解。

什么叫多任务系统?多任务系统指可以同一时间内运行多个应用程序,每个应用程序被称作一个任务。

任务定义:任务是一个逻辑概念,指由一个软件完成的任务,或者是一系列共同达到某一目的的操作。

进程定义:进程是指一个具有独立功能的程序在某个数据集上的一次动态执行过程,它是系统进行资源分配和调度的最小单元。

线程定义:线程是进程内独立的一条运行路线,是处理器调度的最小单元,也可以成为轻量级进程。

看了定义,有点晕,还是通俗的说一下它们的区别吧。①通常一个任务是一个程序的一次执行,一个任务包含一个或多个完成独立功能的子任务,这个独立的子任务就是进程或线程。②一个进程可以拥有多个线程,每个线程必须有一个父进程。

任务

任务是一个逻辑概念,指由一个软件完成的任务,或者是一系列共同达到某一目的的操作。通常一个任务是一个程序的一次执行,一个任务包含一个或多个完 成独立功能的子任务,这个独立的子任务就是进程或线程。例如,一个杀毒软件的一次运行是一个任务,目的是从各种病毒的侵害中保护计算机系统,这个任务包含 多个独立功能的子任务(进程或线程),包括实时监控功能、定时查杀功能、防火墙功能及用户交互功能等。任务、进程和线程之间的关系如图1所示

进程

进程的基本概念

进程是指一个具有独立功能的程序在某个数据集上的一次动态执行过程,它是系统进行资源分配和调度的基本单元。一次任务的运行可以并发激活多个进程,这些进程相互合作来完成该任务的一个最终目标。

进程具有并发性、动态性、交互性、独立性和异步性等主要特性。

进程和程序是有本质区别的:程序是静态的一段代码,是一些保存在非易失性存储器的指令的有序集合,没有任何执行的概念;而进程是一个动态的概念,它是程序执行的过程,包括动态创建、调度和消亡的整个过程,它是程序执行和资源管理的最小单位。

Linux下的进程结构

进程不但包括程序的指令和数据,而且包括程序计数器和处理器的所有寄存器及存储临时数据的进程堆栈,因此,正在执行的进程包括处理器当前的一切活动。

因为linux是一个多任务多进程的操作系统,所以其他的进程必须等到系统将处理器使用权分配给自己之后才能运行。当正在运行的进程等待其他的系统 资源时,linux内核将取得处理器的控制权,并将处理器分配给其他正在等待的进程,他按照内核中的调度算法决定将处理器分配给哪一个进程,也就是说,内 核不会让处理器闲着。

内核将所有进程存放在双向循环链表(进程链表)中,其中链表的头是 init_task 描述符。链表的每一项都是类型为 task_struct,称为进程描述符的结构,该结构包含了一个进程相关的所有信息,定义在<include/linux /sched.h>文件中。task_struct内核结构比较大,它能完整的描述一个进程,如进程的状态、进程的基本信息、进程标识符、内存相关 信息、父进程相关信息、与进程相关的终端信息、当前工作目录、打开的文件信息、所接收的信号信息等。

下面详细讲解task_struct结构中最为重要的两个域:state(进程状态)和pid(进程标识符)。如果想具体了解task_struct,请点这里。

(1)进程状态

Linux中的进程有以下几种状态。

● 运行状态(TASK_RUNNING):进程当前正在运行,或者正在运行队列中等待调度。

● 可中断的阻塞状态(TASK_INTERRUPTIBLE):进程处于阻塞(睡眠)状态,正在等待某些事件发生或能够占用某些资源。处在这种状态下的进程 可以被信号中断。接收到信号或被显式的唤醒呼叫(如调用 wake_up 系列宏:wake_up、wake_up_interruptible等)唤醒之后,进程将转变为 TASK_RUNNING 状态。

● 不可中断的阻塞状态(TASK_UNINTERRUPTIBLE):此进程状态类似于可中断的阻塞状态(TASK_INTERRUPTIBLE),只是它 不会处理信号,把信号传递到这种状态下的进程不能改变它的状态。在一些特定的情况下(进程必须等待,直到某些不能被中断的事件发生),这种状态是很有用 的。只有在它所等待的事件发生时,进程才被显示的唤醒呼叫唤醒。

● 可终止的阻塞状态(TASK_KILLABLE):该状态的运行机制类似于TASK_UNINTERRUPTIBLE,只不过处在该状态下的进程可以响应 致命信号。它可以替代有效但可能无法终止的不可中断的阻塞状态(TASK_UNINTERRUPTIBLE),以及易于唤醒但安全性欠佳的可中断的阻塞状 态TASK_INTERRUPTIBLE)。

● 暂停状态(TASK_STOPPED):进程的执行被暂停,当进程收到 SIGSTOP、SIGSTP、SIGTTIN、SIGTTOU等信号时,就会进入暂停状态。

● 跟踪状态(TASK_TRACED):进程的执行被调试器暂停。当一个进程被另一个监控时(如调试器使用ptrace()系统调用监控测试程序),任何信号都可以把这个进程置于跟踪状态。

● 僵尸状态(EXIT_ZOMBIE):进程运行结束,父进程尚未使用 wait 函数族(如调用 waitpid()函数)等系统调用来“收尸”,即等待父进程销毁它。处在该状态下的进程“尸体”已经放弃了几乎所有的内存空间,没有任何可执行代码,也 不能被调度,仅仅在进程列表中保留一个位置,记载该进程的推出状态等信息供其他进程收集。

● 僵尸撤销状态(EXIT_DEAD):这是最终状态,父进程调用 wait 函数族“收尸”后,进程彻底由系统删除。

它们之间的转换关系如图2所示:

进程可以使用 set_task_state 和 set_current_state 宏来改变指定进程的状态信息和当前进程的状态。

(2)进程标识符

Linux内核通过唯一的进程标识符 PID 来标识每个进程(就和文件描述符一样)。PID存放在进程描述符的 pid 字段中,新创建的 PID 通常是前一个进程的 PID 加1,不过PID的值有上限(最大值=PID_MAX_DEFAULT-1,通常为32767),读者可以查看/proc/sys/kernel /pid_max 来确定该系统的进程数上限。

当系统启动后,内核通常作为某一个进程的代表。一个指向task_struct的宏current用来记录正在运行的进程。current经常作为 进程描述符结构指针的形式出现在内核代码中,例如,current->pid 表示处理器正在执行的进程的PID。当系统需要查看所有的进程时,则调用for_each_process()宏,这将比系统搜索数组的速度要快的多。

在Linux中获得当前进程号的(PID)和父进程号(PPID)的系统调用函数分别为 getpid() 和 getppid()。

进程的创建、执行、终止

(1)进程的创建和执行

咱们首先得知道啥是创建,啥是执行哈!我刚开始看的时候没懂。创建进程就是产生一个新的进程,这个大家都知道。而进程的执行,前边讲进程的的定义的时候,就说了正在运行的子任务,说白了,进程执行也就是让产生的这个进程干点什么事,别占着那啥不拉那啥。

许多操作系统提供的都是产生进程的机制,也就是说,首先在新的地址空间里创建进程、读入可执行文件,最后再开始执行。Linux 中进程的创建很特别,它把上述的步骤分解到两个单独的函数中去执行:fork()函数和exec函数族。首先,fork()函数通过复制当前进程创建一个子进程(注意此时资源还没有被复制过来,去了解一下写时复制页技术吧),子进程于父进程的区别仅仅在于不同的PID、PPID和某些资源及统计量。exec函数族负责读取可执行文件并将其载入地址空间开始运行。

(2)进程的终止

进程终结也需要很多繁琐的工作,系统必须保证回收进程所占用的资源,并通知父进程。Linux首先把终止的进程设置为僵尸状态,这时,进程无法投入 运行,它的存在只为父进程提供信息,申请死亡。父进程得到信息后,开始调用 wait 函数族,最后终止子进程,子进程占用的所有资源被全部释放。

进程的内存结构

Linux操作系统采用虚拟内存管理技术,使得每个进程都有各自互不干涉的进程地址空间。该地址空间是大小为 4GB的线性虚拟空间,用户所看到和接触到的都是该虚拟地址,无法看到实际的物理内存地址。利用这种虚拟地址不但能起到保护操作系统的效果(用户不能直接 访问物理地址),而且,更重要的是,用户程序可以使用比实际物理内存更大的地址空间。

4GB的进程地址空间会被分成两个部分:用户空间与内核空间。用户地址空间是从0到3GB(0xC000 0000),内核地址空间占据3GB到4GB。用户进程通常情况下只能访问用户空间的虚拟地址,不能访问内核空间的虚拟地址。只有用户进程使用系统调用 (代表用户进程在内核态执行)时可以访问到内核空间。每当进程切换时,用户空间就跟着变化;而内核空间由内核负责映射,它不会跟着进程改变,是固定的。内 核空间地址有自己对应的页表,用户进程各自有不同的页表。每个进程的用户空间都是完全独立、互不相干的。进程的虚拟内存空间如图3所示,其中用户空间包括 以下几个功能区域:

● 只读段: 包含程序代码(.init和.text)和只读数据(.rodata)。

● 数据段: 存放的是全局变量和静态变量。其中可读可写数据段(.data)存放已初始化的全局变量和静态变量,BSS数据段(.bss)存放未初始化的��局变量和静态变量。

● 堆: 由系统自动分配释放,存放函数的参数值、局部变量的值、返回地址等。

● 堆栈: 存放动态分配的数据,一般由程序员动态分配和释放。若程序员不释放,程序结束时可能由操作系统回收。

● 共享库的内存映射区域: 这是Linux动态链接器和其他共享代码库代码的映射区域。

由于在Linux系统中每一个进程都会有/proc文件系统下与之对应的一个目录(如将init进程的相关信息在/proc/1 目录下的文件中描述),因此通过 proc 文件系统可以查看某个进程的地址空间的映射情况。例如,运行一个应用程序,如果它的进程号为13703,则输入“ cat /proc/13703/maps”命令,可以查看该进程的内存映射情况。

线程

前面已经讲到,进程是系统中程序执行和资源分配的基本单位。每个进程都拥有自己的数据段、代码段和堆栈段,这就造成了进程在进行切换等动作时需要较 复杂的上下文切换等动作。为了进一步减少处理机的空闲时间,支持多处理器及减少上下文切换开销,进程在演化中出现了另一个概念---线程。它是进程内独立 的一条运行路线,是处理器调度的最小单元,也可以称为轻量级线程。线程可以对进程的内存空间和资源分配进行访问,并与同一进程中的其他线程共享。因此,线程的上下文切换的开销比创建进程小得多。

一个进程可以拥有多个线程,每个线程必须有一个父进程。线程不拥有系统资源,它只具有运行时所必须的一些数据结构,如堆栈/寄存器与线程控制块 (TCB),线程与其父进程的其他进程共享该进程所拥有的全部资源。要注意的是,由于线程共享了进程的资源和地址空间,因此,任何线程对系统资源的操作都 会给其他进程带来影响。由此可知,多线程中的同步是一个非常重要的问题。在多线程系统中,进程与线程的关系如图4所示

在Linux系统中,线程分为3种:①用户线程 ②轻量级线程 ③内核线程

Linux多任务编程之一:任务、进程、线程(转)的更多相关文章

  1. Linux多任务编程之七:Linux守护进程及其基础实验(转)

    来源:CSDN  作者:王文松  转自Linux公社 ------------------------------------------------------------------------- ...

  2. linux高级编程基础系列:线程间通信

    linux高级编程基础系列:线程间通信 转载:原文地址http://blog.163.com/jimking_2010/blog/static/1716015352013102510748824/ 线 ...

  3. Linux多任务编程之六:编写多进程程序及其代码(转)

    来源:CSDN  作者:王文松  转自Linux公社 ------------------------------------------------------------------------- ...

  4. Linux多任务编程之五:exit()和_exit()函数(转)

    来源:CSDN  作者:王文松   转自:Linux公社 ----------------------------------------------------------------------- ...

  5. Linux多任务编程——线程

    线程基础 △ 由于进程的地址空间是私有的,因此在进行上下文切换时,系统开销比较大 △ 在同一个进程中创建的线程共享该进程的地址空间 △ 通常线程值得是共享相同地址空间的多个任务 △ 每个线程的私有这些 ...

  6. Linux多任务编程——进程

    进程编程常用函数 1--- fork pitd_t fork(void); 创建一个新的子进程,其父进程为调用 fork() 函数的进程: 返回值:成功:子进程返回 0,父进程返回 子进程 PID:失 ...

  7. linux c编程:初识进程与线程

    p { margin-bottom: 0.25cm; line-height: 120% } (一) 认识进程 在Linux系统中,每一个进程都有自己的ID,就如同人的身份证一样.linux中有一个数 ...

  8. Linux编程之《进程/线程绑定CPU》

    Intro----- 通常我们在编写服务器代码时,可以通过将当前进程绑定到固定的CPU核心或者线程绑定到固定的CPU核心来提高系统调度程序的效率来提高程序执行的效率,下面将完整代码贴上. /***** ...

  9. Linux多线程编程,为什么要使用线程,使用线程的理由和优点等

    线程?为什么有了进程还需要线程呢,他们有什么区别?使用线程有什么优势呢?还有多线程编程的一些细节问题,(http://www.0830120.com)如线程之间怎样同步.互斥,这些东西将在本文中介绍. ...

随机推荐

  1. Centos7 搭建KVM并创建Linux Windows虚拟机

    一.安装KVM   查看系统版本 cat /etc/redhat-release     关闭防火墙及selinux   systemctl disable firewalld.service 查看防 ...

  2. Jmeter(八) - 从入门到精通 - JMeter配置元件(详解教程)

    1.简介 JMeter配置元件可以用来初始化默认值和变量,读取文件数据,设置公共请求参数,赋予变量值等,以便后续采样器使用.将在其作用域的初始化阶段处理.配置元件(Config Element)提供对 ...

  3. 【Spring Cloud 系列】 二、Spring Cloud Eureka 的第一印象

    Eureka : 翻译翻译,找到了!(惊讶语气) Spring CLoud 中的 Spring Cloud Eureka,用于 分布式项目中的服务治理.是对Netflix 套件中的Eureka 的二次 ...

  4. 用Springboot干掉IBM的WAS-为公司省点钱

    1 那一夜,你伤害了我 今夜的雨下得凉快,小南睡得正香,突然收到远洋运维小周的电话:Hello, Are you OK? WAS有issue,快起来help me! 只见小南登陆WAS机,查看了机器日 ...

  5. 我要穿越,干翻 “烂语言” JavaScript!

    更多精彩文章,尽在码农翻身 我是一个线程 TCP/IP之大明邮差 一个故事讲完Https CPU 阿甘 Javascript: 一个屌丝的逆袭 微服务把我坑了 如何降低程序员的工资? 程序员,你得选准 ...

  6. selenium(5)-解读强制等待,隐式等待,显式等待的区别

    背景 为什么要设置元素等待 因为,目前大多数Web应用程序都是使用Ajax和Javascript开发的:每次加载一个网页,就会加载各种HTML标签.JS文件 但是,加载肯定有加载顺序,大型网站很难说一 ...

  7. Beta冲刺<8/10>

    这个作业属于哪个课程 软件工程 (福州大学至诚学院 - 计算机工程系) 这个作业要求在哪里 Beta冲刺 这个作业的目标 Beta冲刺--第八天(05.26) 作业正文 如下 其他参考文献 ... B ...

  8. elasticserach数据库深度分页查询的原理

    深度分页存在的问题 https://segmentfault.com/a/1190000019004316?utm_source=tag-newest 在实际应用中,分页是必不可少的,例如,前端页面展 ...

  9. Eureka心跳健康检查机制和Spring boot admin 节点状态一直为DOWN的排查(忽略某一个节点的健康检查)

    https://www.jdon.com/springcloud/eureka-health-monitoring.html 运行阶段执行健康检查的目的是为了从Eureka服务器注册表中识别并删除不可 ...

  10. Python3-算法-递归

    递归 递归算法是指一般通过函数或子进程来实现,在函数或子进程的内部,直接或间接地调用自己的算法,说白了就是自己调自己 注: 1.在使用递归的时候,必须要有一个递归出口,即一个明确的结束条件,否则就是死 ...