2.1 进程的概念
进程是操作系统结构的基础;是一个正在执行的程序;计算机中正在运行的程序实例;可以分配给处理器并由处理器执行的一个实体;由单一顺序的执行显示,一个当前状态和一组相关的系统资源所描述的活动单元。
第一,进程是一个实体。每一个进程都有它自己的地址空间,一般情况下,包括文本区域(text region)、数据区域(data region)和堆栈(stack region)。文本区域存储处理器执行的代码;数据区域存储变量和进程执行期间使用的动态分
配的内存;堆栈区域存储着活动过程调用的指令和本地变量。
第二,进程是一个“执行中的程序”。程序是一个没有生命的实体,只有处理器赋予程序生命时,它才能成为一个活动的实体,我们称其为进程。
1、用户角度: 进程是程序的一次动态执行过程
2、操作系统: 进程是操作系统分配资源的基本单位,也是最小单位
2.2 进程的特征
动态性:进程的实质是程序在多道程序系统中的一次执行过程,进程是动态产生,动态消亡的。
并发性:任何进程都可以同其他进程一起并发执行
独立性:进程是一个能独立运行的基本单位,同时也是系统分配资源和调度的独立单位;
异步性:由于进程间的相互制约,使进程具有执行的间断性,即进程按各自独立的、不可预知的速度向前推进
结构特征:进程由程序、数据和进程控制块三部分组成。
多个不同的进程可以包含相同的程序:一个程序在不同的数据集里就构成不同的进程,能得到不同的结果;但是执行过程中,程序不能发生改变。
2.3 进程的状态
创建状态:进程在创建时需要申请一个空白PCB,向其中填写控制和管理进程的信息,完成资源分配。如果创建工作无法完成,比如资源无法满足,就无法被调度运行,把此时进程所处状态称为创建状态
就绪状态:进程已经准备好,已分配到所需资源,只要分配到CPU就能够立即运行
执行状态:进程处于就绪状态被调度后,进程进入执行状态
阻塞状态:正在执行的进程由于某些事件(I/O请求,申请缓存区失败)而暂时无法运行,进程受到阻塞。在满足请求时进入就绪状态等待系统调用
终止状态:进程结束,或出现错误,或被系统终止,进入终止状态。无法再执行
2.4 操作系统与进程的关系
为啥会有进程
CPU一次只能处理一个程序,CPU速度很快,而内存很慢,所以CPU会有大量的时间都是空闲的。而CPU又是很昂贵的,为了解决浪费CPU的情况,就出现了中断处理,将程序分成一小片一小片的,这个进程执行一点,那个进程执行一点。
虽然在内部进程的执行是一段一段的,但是CPU的速度很快的(速度都是纳秒级别的),所以我们是感受不到进程执行过程中的停顿的。
CPU执行程序的过程
进程的标识号:
系统为每一个进程分配一个唯一的标识号(正整数),这个标识号就称为进程ID(progress identifer),即PID。
最早的时候有一个0号进程,是写死在内核里边的,所以操作系统在启用的时候,0号进程就启用了。
0号进程的作用:启动1号进程,将1号进程启动了之后就没事干了
1号进程是系统启动的第一个用户态进程,其他进程就相当于1号进程的子孙。
每个进程也应该要有属于自己的地址空间。这个空间给多大呢?4G,Linux下有3G在用户空间,1G在内核。Windows下,有2G在内核,2G在用户空间。
每个进程都有4G,同一时间内可能会有成百上千乃至上万个进程,累加起来,所需的内存是非常庞大的,然而我们的计算机内存并没有那么多的内存
(内存可是很贵的),所以,进程的地址空间其实是虚拟地址,是操作系统给它们画的一个大饼。进程是一小片一小片的执行的,所以,即使进程认为自
己拥有4G空间,但是并不会在同一时刻使用4G空间,那么,操作系统只需要在你需要使用的时候给你分配一点空间就行了。你不用的时候,操作系统就可以
将这块空间给别的进程使用。那怎么通过虚拟地址找到对应的物理地址(在内存中真实存在的地址)呢?操作系统为每个进程提供了一个MMU(内存管理单
元),里边有一张表,里边将虚拟内存映射到物理内存的情况记录着,然后每个进程里边都有一个指针(在mm_struct 里边的pgd指针)指向这张表
PCB(process control block)即,进程控制块。既然是控制进程的,进程的相关信息都应该存放在里边
task_struct(PCB)内容分类:
标识符:就是进程的PID。
状态:任务状态、退出代码,退出信号等。
优先级:相对于其他进程的优先级。
程序计数器:程序中即将被执行的下一条指令的地址。
内存指针:包括程序代码和进程相关数据的指针,还有和其他进程共享的内存块的指针。
上下文数据:进程执行时处理器的寄存器中的数据。
I/O状态信息:包括显示的I/O请求,分配给进程的I/O设备和被进程使用的文件列表。
记账信息:可能包括处理器时间总和,使用的时钟数总和,时间限制,记账号等。
操作系统控制一个进程分两步:
1、通过PCB将一个进程描述起来
2、将这些PCB组织起来。
PCB是一个结构体,那么创建一个链表,每个节点都存放一个PCB,来一个进程,就把它的PCB挂到链表上,来一个挂一个,那么,操作系统只要拎着这个链表的头,后边所有的PCB都可以获取到了,PCB获取到了,整个进程的控制权也就拿到了进程有不同的状态,如果不同状态的进程都放在一个链表上的话,如果进程很多,链表就会很长,这就降低了操作系统调度在对进程进行调度时的查找效率。
为了解决这个问题,操作系统创建了索引表。让同一状态的进程归入一个索引表再由索引指向PCB,多个状态对应多个不同的索引表,各状态的进程形成不同的索引表,就绪索引表、阻塞索引表等
2.5 进程的创建
JAVA提供了两种方法用来启动进程或其它程序:
(1)使用Runtime的exec()方法
(2)使用ProcessBuilder的start()方法
ProcessBuilder与Runtime.exec()的区别?
ProcessBuilder.start() 和 Runtime.exec() 方法都被用来创建一个操作系统进程(执行命令行操作),
并返回 Process 子类的一个实例,该实例可用来控制进程状态并获得相关信息。
ProcessBuilder.start() 和 Runtime.exec()传递的参数有所不同,Runtime.exec()可接受一个单独的字符串,这
个字符串是通过空格来分隔可执行命令程序和参数的;也可以接受字符串数组参数。而ProcessBuilder的构造
函数是一个字符串列表或者数组。列表中第一个参数是可执行命令程序,其他的是命令行执行是需要的参数。
ProcessBuilder的优点
配置环境变量时更优雅
对当前目录的控制也更合理
错误流重定向特别方便
进程控制更简洁
ProcessBuilder类
每个进程生成器(即ProcessBuilder对象)管理这些进程属性:
命令 command
是一个字符串列表,它表示要调用的外部程序文件及其参数(如果有)。在此,表示有效的操作系统命令的字符串列表是依赖于系统的。例如,每一个总体变量,通常都要成为此列表中的元素,但有一些操作系统,
希望程序能自己标记命令行字符串——在这种系统中,Java 实现可能需要命令确切地包含这两个元素。
环境 environment
是从变量到值得依赖于系统的映射。初始值是当前进程环境的一个副本(请参阅 System.getenv())。
工作目录 working directory
默认值是当前进程的当前工作目录,通常根据系统属性 user.dir 来命名。
redirectErrorStream属性
最初,此属性为 false,意思是子进程的标准输出和错误输出被发送给两个独立的流,这些流可以通过 Process.getInputStream() 和 Process.getErrorStream() 方法来访问。
如果将值设置为 true,标准错误将与标准输出合并。这使得关联错误消息和相应的输出变得更容易。在此情况下,合并的数据可从 Process.getInputStream() 返回的流读取,
而从 Process.getErrorStream() 返回的流读取将直接到达文件尾。
方法
构造方法摘要 ProcessBuilder(List
Runtime 类
每个 Java 应用程序都有一个 Runtime 类实例,使应用程序能够与其运行的环境相连接。可以通过 getRuntime 方法获取当前运行时。
应用程序不能创建自己的 Runtime 类实例。但可以通过 getRuntime 方法获取当前Runtime运行时对象的引用。
一旦得到了一个当前的Runtime对象的引用,就可以调用Runtime对象的方法去控制Java虚拟机的状态和行为。
方法
void addShutdownHook(Thread hook) 注册新的虚拟机来关闭挂钩。 int availableProcessors() 向 Java 虚拟机返回可用处理器的数目。 Process exec(String command) 在单独的进程中执行指定的字符串命令。 Process exec(String[] cmdarray) 在单独的进程中执行指定命令和变量。 Process exec(String[] cmdarray, String[] envp) 在指定环境的独立进程中执行指定命令和变量。 Process exec(String[] cmdarray, String[] envp, File dir) 在指定环境和工作目录的独立进程中执行指定的命令和变量。 Process exec(String command, String[] envp) 在指定环境的单独进程中执行指定的字符串命令。 Process exec(String command, String[] envp, File dir) 在有指定环境和工作目录的独立进程中执行指定的字符串命令。 void exit(int status) 通过启动虚拟机的关闭序列,终止当前正在运行的 Java 虚拟机。 long freeMemory() 返回 Java 虚拟机中的空闲内存量。 void gc() 运行垃圾回收器。 InputStream getLocalizedInputStream(InputStream in) 已过时。 从 JDK 1.1 开始,将本地编码字节流转换为 Unicode 字符流的首选方法是使用 InputStreamReader 和 BufferedReader 类。 OutputStream getLocalizedOutputStream(OutputStream out) 已过时。 从 JDK 1.1 开始,将 Unicode 字符流转换为本地编码字节流的首选方法是使用 OutputStreamWriter、BufferedWriter 和 PrintWriter 类。 static Runtime getRuntime() 返回与当前 Java 应用程序相关的运行时对象。 void halt(int status) 强行终止目前正在运行的 Java 虚拟机。 void load(String filename) 加载作为动态库的指定文件名。 void loadLibrary(String libname) 加载具有指定库名的动态库。 long maxMemory() 返回 Java 虚拟机试图使用的最大内存量。 boolean removeShutdownHook(Thread hook) 取消注册某个先前已注册的虚拟机关闭挂钩。 void runFinalization() 运行挂起 finalization 的所有对象的终止方法。 static void runFinalizersOnExit(boolean value) 已过时。 此方法本身具有不安全性。它可能对正在使用的对象调用终结方法,而其他线程正在操作这些对象,从而导致不正确的行为或死锁。 long totalMemory() 返回 Java 虚拟机中的内存总量。 void traceInstructions(boolean on) 启用/禁用指令跟踪。 void traceMethodCalls(boolean on) 启用/禁用方法调用跟踪。
Process 类
不管通过哪种方法启动进程后,都会返回一个Process类的实例代表启动的进程,该实例可用来控制进程并获得相关信息。
Process 类提供了执行从进程输入、执行输出到进程、等待进程完成、检查进程的退出状态以及销毁(*掉)进程的方法:
方法
void destroy() *掉子进程。 一般情况下,该方法并不能*掉已经启动的进程,不用为好。 int exitValue() 返回子进程的出口值。 只有启动的进程执行完成、或者由于异常退出后,exitValue()方法才会有正常的返回值,否则抛出异常。 InputStream getErrorStream() 获取子进程的错误流。 如果错误输出被重定向,则不能从该流中读取错误输出。 InputStream getInputStream() 获取子进程的输入流。 可以从该流中读取进程的标准输出。 OutputStream getOutputStream() 获取子进程的输出流。 写入到该流中的数据作为进程的标准输入。 int waitFor() 导致当前线程等待,如有必要,一直要等到由该 Process 对象表示的进程已经终止。
3 简单用例
3.1 通过Runtime中的exec方创建进程
public static void main(String[] args) { Runtime run = Runtime.getRuntime(); Process p = run.exec("java test1"); BufferedInputStream in = new BufferedInputStream(p.getInputStream()); BufferedReader br = new BufferedReader(new InputStreamReader(in)); String s; while ((s = br.readLine()) != null) System.out.println(s); }
3.2 使用ProcessBuilder创建继承
ProcessBuilder pb = new ProcessBuilder("Command", "arg2", "arg2", '''); // 设置环境变量 Map
4 进程容易出现的问题
4.1 JAVA进程突然消失问题以及解决方案 4.2 Java进程cpu占用过高问题以及解决方案 4.3 java 进程是如何在Linux服务器上进行内存分配的 4.4 OutOfMemoryError,内存不足
容易出现的问题会在后面讨论,想一起讨论学习的朋友可以点下关注,会持续更新,文章有帮助的话可以收藏,转发,有什么补充可以在下面评论,谢谢!!