进程:
在OS看来:程序的⼀一个执行实例。 正在执行的程序,能分配处理器并由处理器执⾏行的实体。
在内核看来:担当分配系统资源(CPU时间,内存)的实体。
进程的两个基本元素:进程代码和进程资源;
所有的进程信息被放在一个叫做进程控制块的数据结构中,可以理解为进程属性 的集合;
进程控制块(PCB)
组成成分:
1)进程标示符
(1)内部标示符 OS赋予,唯一的数字标示符
(2)外部标示符 创建者提供,字母+数字 包含父进程标示符,子进程标示符,还有用户标识
2)处理机状态
(1)通用寄存器 用户程序可访问,暂存信息,一般8~32个通用寄存器
(2)指令计数器 下一条指令的地址
(3)程序状态字PSW 含有状态信息条件码、执行方式、中断屏蔽标志
(4)用户栈指针 存放过程和系统调用参数及调用地址
3)进程调度信息
(1)进程状态 调度和对换的依据
(2)进程优先级
(3)其他信息 已等待CPU时间总和,已执行时间总和
(4)事件 阻塞原因
4)进程控制信息
(1)程序和数据地址
(2)进程同步和通信机制 消息队列指针,信号量
(3)资源清单 所需资源和已分配到的资源
(4)链接指针 进程队列下一个进程PCB的首地址
进程控制块的组织方式:
链接方式:把同一状态的PCB用其中的链接字接成一个队列,有就绪队列,若干个阻塞队列和空白队列
索引方式:根据所有进程的状态建立几张索引表
每个进程在内核中都有一个进程控制块(PCB)来维护进程相关的信息,
Linux内核的 进程控制块是task_struct结构体。
现在我们全面了解一下其中都有哪些信息。
在Linux中,这个结构叫做task_struct。
task_struct是Linux内核的⼀一种数据结构,它会被装载到RAM⾥里并且包含着进程的信息。
每个进程都把它的信息放在 task_struct 这个数据结构里,task_struct 包含了这些内容:
标示符 :描述本进程的唯一标示符,用来区别其他进程。
进程ID : PID 父进程ID PPID
状态 :任务状态,退出代码,退出信号等。
优先级 :相对于其他进程的优先级。
程序计数器:程序中即将被执行的下一条指令的地址。
内存指针:包括程序代码和进程相关数据的指针,还有和其他进程共享的内存块的指针。
上下文数据:进程执行时处理器的寄存器中的数据。
I/O状态信息:包括显示的I/O请求,分配给进程的I/O设备和被进程使用的文件列表。
记账信息:可能包括处理器时间总和,使用的时钟数总和,时间限制,记账号等。
保存进程信息的数据结构叫做 task_struct,并且可以在 include/linux/sched.h 里找到它。
所有运行在系统里的进程都以 task_struct 链表的形式存在内核里。
O(1) 现在操作系统管理进程都是用红黑树;
进程的信息可以通过 /proc 系统文件夹查看。要获取PID为400的进程信息,你需要查看 /
proc/400 这个文件夹。大多数进程信息同样可以使用top和ps这些用户级工具来获取。
代码 + 数据 + PCB组成进程实体;
但是 创建进程实质就是创建 PCB
进程和程序的区别:
1、进程是动态的(可以被调度),程序是静态的(文件,由文件系统管理);
2、
linux进程的几个状态
- Linux进程状态:R run 运行状态 0 (TASK_RUNNING),可执行状态&运行状态(在run_queue队列里的状态)
- Linux进程状态:S 浅度睡眠 1 系统默认sleep (TASK_INTERRUPTIBLE),可中断的睡眠状态, 可处理signal
- Linux进程状态:D 深度睡眠 2 硬件交互I/O (TASK_UNINTERRUPTIBLE),不可中断的睡眠状态, 可处理signal, 有延迟
- Linux进程状态:T stop 状态 4 (TASK_STOPPED or TASK_TRACED),暂停状态或跟踪状态, 不可处理signal, 因为根本没有时间片运行代码
- Linux进程状态:Z 僵死状态 16(TASK_DEAD - EXIT_ZOMBIE),退出状态,进程成为僵尸进程。不可被kill, 即不响应任务信号, 无法用SIGKILL杀死
6 . linux进程状态 : X 死亡状态: 32
微机进程状态:
新建状态:
就绪状态:
阻塞状态;
运行状态:
消亡状态:
挂起状态:
只能由 就绪切换到运行 ; 运行时间片用完切换到 就绪状态; 运行时 中断 请求I/O等 会进入阻塞状态;
请求I/O完成后 会从阻塞状态进入 就绪状态; 就绪状态的进程 形成就绪队列;
并发和并行的区别:
并发是
并行是多个CPU,
ps -l 可显示进程:
UID : 代表执行者的身份
PID : 代表这个进程的代号
PPID :代表这个进程是由哪个进程发展衍生而来的,亦即父进程的代号
在shell上运行一个程序,因此shell程序的ppi的就是 shell ;
PRI :代表这个进程可被执行的优先级,其值越小越早被执行
NI :代表这个进程的nice值
pri+nice最终决定进程的优先级
当进程执行时,它会被装载进虚拟内存,为程序变量分配空间,并把相关信息添到 task_struct里。
进程内存布局分为四个不同的段:
• 文本段,包含程序的源指令。
• 数据段,包含了静态变量。
• 堆,动态内存分区区域。
• 栈,动态增长与收缩的段,保存本地变量。
这里有两种创建进程的方法,fork()和execve()。它们都是系统调用,但它们的运行方式有点
不同。
要创建一个子进程可以执行fork()系统调用。然后子进程会得到父进程中数据段,栈段和
堆区域的一份拷贝。子进程独立可以修改这些内存段。但是文本段是父进程和子进程共享
的内存段,不能被子进程修改。
COW 写时拷贝,尝试写入才开辟新空间,
fork调用的一个奇妙之处就是它仅仅被调用一次,却能够返回两次,它可能有三种不同的返回值:
在父进程中,fork返回新创建子进程的进程PID;
在子进程中,fork返回0;
如果出现错误,fork返回一个负值。
fork完 要进行程序分流,要不然下面的代码会被两个进程执行;
如果使用execve()创建一个新进程。这个系统调用会销毁所有的内存段去重新创建一个新
的内存段。然而,execve( )需要一个可执行文件或者脚本作为参数,这和fork()有所不同。
注意,execve( )和fork( )创建的进程都是运行进程的子进程。
僵尸进程:一个子进程在其父进程没有调用wait()或waitpid()的情况下退出。这个子进程就
是僵尸进程。如果其父进程还存在而一直不调用wait,则该僵尸进程将无法回收,等到其父
进程退出后,该进程将被init回收。
进程位置:
程序转化为进程:
内核将程序读入内存;为程序分配空间;
内核为该进程分配PID; 把进程变成就绪状态,并加入就绪队列;
进程的内存映像 : 内核在内存中如何存放可执行程序文件。
PCB是进程独有的,地址空间也是独有的;
地址空间是一个数据结构,是虚拟地址,通过页表()+mmu(内存管理单元,硬件)映射到物理地址上;
地址空间从低地址到高地址,依次是 代码段,初始化的全局变量区,未初始化的全局变量区,堆(向上增长),栈(像下增长和堆反方向),命令行花环境; 堆一般挺小。栈能大一些 ;最后是命令行参数和环境变量;
进程看来,独立拥有整个内存空间。