Linux进程初识:OS基础、fork函数创建进程、进程排队和进程状态讲解

2024-09-07 03:12

本文主要是介绍Linux进程初识:OS基础、fork函数创建进程、进程排队和进程状态讲解,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

1、冯诺伊曼体系结构

问题一:为什么在体系结构中存在存储器(内存)?

存储单元总结:

问题二:为什么程序在运行的时候,必须把程序先加载到内存?

问题三:请解释,从你登录上qq开始和某位朋友聊天开始,数据的流动过程。

2、操作系统

2.1操作系统的概念:

我们首先要明白什么是管理:

2.2为什么要有操作系统?

2.3操作系统如何保证稳定和安全呢?(利用系统调用函数解决)

库函数和系统调用的关系:

3、那到底什么是进程呢?(重要!)

3.1、什么程序加载到内存,变成一个进程之后,我们要给每一个进程形成一个PCB对象呢?

3.2、PCB进程标识符:

4、fork函数初识

4.1、fork函数的作用:

4.2、使用方法(一般都用if分流)

4.3、关于fork函数的灵魂三问:

1、为什么给父进程返回子进程的pid,给子进程返回0?

2、fork函数为什么会返回两次?

3、这里的id为什么会同时满足 == 0 又满足>0呢?

问题:为什么我们在磁盘上将一个程序删了,这个程序还能运行呢?

5、进程排队:

6、进程状态:

6、1那什么是挂起状态呢?

6.2、Linux下具体的进程状态:

D状态深度睡眠又是什么呢?

6.3、为什么要有Z状态?

6.4、什么是僵尸Z状态?

6.5、僵尸进程危害:

7、什么是孤儿进程?

1、冯诺伊曼体系结构

截至目前,我们所认识的计算机,都是由一个个的硬件组件组成

  • 输入单元:包括键盘, 鼠标,扫描仪, 写板等
  • 中央处理器(CPU):含有运算器和控制器
  • 输出单元:显示器,打印机等
  • 存储器:指的是内存(掉电易失)
  • 运算器主要进行算术运算(加减乘除)和逻辑运算(判断真假)

设备是互相连接的,目的是让设备之间数据流动,本质是让设备之间进行数据的来回拷贝(拷贝的整体速度,是决定计算机效率的重要指标

问题一:为什么在体系结构中存在存储器(内存)?

输入输出设备的运行速度就会拖累cpu的运行速度,跟木桶原理一个道理,那存储器并没有解决短板的问题,那怎么提高运行速度呢?

我们把内存看做一个非常大的缓存,介于设备和CPU之间
利用预存和缓存的机制,将计算机的效率最终变成了内存效率为主

存储单元总结:

距离CPU越近的存储单元,效率越高,造价贵,单体容量越小

距离CPU越远的存储单元,效率越低,造价便宜,单体容量大

所以内存的引入,可以让我们的计算机效率不错,且价格比较便宜,这样我们才能买的起电脑

问题二:为什么程序在运行的时候,必须把程序先加载到内存?

因为冯诺依曼体系决定的!

在数据层面,CPU只和内存打交道;外设只和内存打交道

关于冯诺依曼,必须强调几点:
这里的存储器指的是内存
不考虑缓存情况,这里的CPU能且只能对内存进行读写,不能访问外设(输入或输出设备)
外设(输入或输出设备)要输入或者输出数据,也只能写入内存或者从内存中读取。
一句话,所有设备都只能直接和内存打交道

对冯诺依曼的理解,不能停留在概念上,要深入到对软件数据流理解上,

问题三:请解释,从你登录上qq开始和某位朋友聊天开始,数据的流动过程。

我们先通过键盘输入消息,然后加载到内存,CPU从内存中读取数据,进行加密和计算后再放到内存,显示器(显示消息到屏幕上)和网卡(发送数据到网络)再从内存中读取数据。(忽略网络部分处理细节)

        朋友电脑的网卡从网络上获取到了我们发送的数据,然后加载到内存,CPU从内存中读取数据,进行解密和计算后再放到内存,显示器再从内存读取相关的数据,显示到屏幕上。

总的来说,就是外设->内存->CPU->内存->外设

2、操作系统

2.1操作系统的概念:

操作系统是一个进行软硬件资源管理软件

我们首先要明白什么是管理

管理就是做决策+做执行。

管理者和被管理者,并不需要见面,管理的本质是对人的信息(数据)做管理。

管理者的核心工作是做决策,根据数据做决策。

总结为六字真言:

先描述,再组织

语言的本质就是对数据的管理

2.2为什么要有操作系统?

对下管理好软硬件资源(手段),对上提供一个良好(稳定、高效、安全)的运行环境(目的)

2.3操作系统如何保证稳定和安全呢?(利用系统调用函数解决)

任何人不得访问操作系统中的任何数据,需要利用系统调用的函数

不能直接越过操作系统!系统调用接口很重要

所谓的系统调用接口,其实就是函数,用C语言设计的函数,由操作系统提供:系统调用函数

库函数和系统调用的关系:

只要库函数调用了系统调用,他们两个就必然是上下层的关系,库函数在上,系统调用在下

3、那到底什么是进程呢?(重要!)

进程 = 内核PCB对象(内核的数据结构) + 可执行程序

这里的PCB到底是何方神圣呢?进程控制块(process control block)

在Linux环境下,PCB就是task_struct,存储进程的所有属性,操作系统内部的数据

未来,所有对进程的控制和操作,都只和进程的PCB有关,和进程的可执行程序没有关系

我们所说的让一个进程去排队,本质上是让PCB去排队,而不是让可执行程序去排队

对进程的管理,转换为对PCB对象的管理

3.1、什么程序加载到内存,变成一个进程之后,我们要给每一个进程形成一个PCB对象呢?

因为操作系统要进行管理!(先描述,再组织)

PCB这些内容都是在操作系统内部的数据

task_ struct内容分类

标示符: 描述本进程的唯一标示符,用来区别其他进程。
状态: 任务状态,退出代码,退出信号等。
优先级: 相对于其他进程的优先级。
程序计数器: 程序中即将被执行的下一条指令的地址。
内存指针: 包括程序代码和进程相关数据的指针,还有和其他进程共享的内存块的指针
上下文数据: 进程执行时处理器的寄存器中的数据[休学例子,要加图CPU,寄存器]。
I/O状态信息: 包括显示的I/O请求,分配给进程的I/O设备和被进程使用的文件列表。
记账信息: 可能包括处理器时间总和,使用的时钟数总和,时间限制,记账号等。
其他信息

3.2、PCB进程标识符:

下面主要讲解标识符,描述进程的唯一标识符,用来区别其他进程

getpid是我们第一个学习的系统调用函数,用来获得该进程的标识符。

每一次启动进程的pid几乎都会变化,因为我的进程是一个新的进程!

一般在Linux中,普通进程,都有他的父进程!

通过系统调用获取进程标示符
进程id(PID)
父进程id(PPID)

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
printf("pid: %d\n", getpid());
printf("ppid: %d\n", getppid());
return 0;
}

4、fork函数初识

4.1、fork函数的作用:

通过系统调用创建进程。它的作用是从已经存在的进程中创建一个子进程,而原进程称为父进程

4.2、使用方法(一般都用if分流)

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
int ret = fork();
if(ret < 0){
perror("fork");
return 1;
}
else if(ret == 0){ //child
printf("I am child : %d!, ret: %d\n", getpid(), ret);
}else{ //father
printf("I am father : %d!, ret: %d\n", getpid(), ret);
}
sleep(1);
return 0;
}

4.3、关于fork函数的灵魂三问:

1、为什么给父进程返回子进程的pid,给子进程返回0?

  • 在‌父进程中‌,fork函数返回新创建子进程的进程ID。这是因为父进程需要通过这个返回值来跟踪和管理其创建的子进程。
  • 在‌子进程中‌,fork函数返回0。这是因为子进程可以通过这个返回值来判断它是否成功创建,并且由于子进程只有一个父进程,它的ID可以通过getppid()获得父进程的ID,而子进程的ID(虽然与父进程ID不同)在fork函数返回时已经被系统内部记录下来。

父进程会有多个子进程,但是子进程只会有一个父进程,一对多的关系

2、fork函数为什么会返回两次?

当fork函数被调用时,它会创建一个新的子进程,这个子进程是父进程的一个复制品,它们共享相同的代码段和部分数据段。由于子进程是父进程的一个副本,因此它们都会执行fork函数之后的代码。这就导致了fork函数在父进程和子进程中都会“返回”,但返回的值不同

fork之后,我们的父和子都会进行,代码共享,一般而言,我们想让父子做不同的事情。调用fork函数,会返回不同的值分别给父子进程

父子进程执行不同的代码块

3、这里的id为什么会同时满足 == 0 又满足>0呢?

这是因为有两个进程,进程之间是具有独立性,互相不能影响!

问题:为什么我们在磁盘上将一个程序删了,这个程序还能运行呢?

我们在运行一个程序时,本质是把程序从磁盘拷贝到内存,换句话来说就是你把这个程序在磁盘上删了,但是这个程序比较小,已经拷贝到内存,在内存上运行了,成为一个进程,就与磁盘上的程序没有关系了

5、进程排队:

进程为什么会排队呢?一定是在等待某种“资源”,比如下面的scanf就需要等待键盘给他传输资源。

只要是排队,一定是进程的task_struct进行排队。

6、进程状态:

教材上关于进程状态的表述:运行、阻塞、挂起

所谓的状态,本质就是一个整形变量,在task_struct中的一个整型变量

状态决定了什么?决定了你的后续动作,Linux中可能会存在多个进程都要根据它的状态执行后续动作(进程开始排队了!)

当我们的进程在进行等待软硬件资源的时候,资源如果没有就绪,我们的进程task_struct只能1、将自己设置为阻塞状态2、将自己的PCB连入等待的资源提供的等待队列。

状态的变迁,引起的是PCB会被OS变迁到不同的队列当中。

当我们的软硬件资源准备就绪后,进程状态就会从阻塞状态调整到运行状态!

6、1那什么是挂起状态呢?

我们这里主要讲的是阻塞挂起,这个状态的前提是计算机资源比较吃紧了

挂起状态就是将数据从内存,换出到磁盘上面 ,当计算机资源恢复后,数据会从外设转入到内存中

6.2、Linux下具体的进程状态:

为了弄明白正在运行的进程是什么意思,我们需要知道进程的不同状态。一个进程可以有几个状态下面代码是状态在kernel源代码里定义:

static const char * const task_state_array[] = {
"R (running)", /* 0 */
"S (sleeping)", /* 1 */
"D (disk sleep)", /* 2 */
"T (stopped)", /* 4 */
"t (tracing stop)", /* 8 */
"X (dead)", /* 16 */
"Z (zombie)", /* 32 */
};
  • R运行状态(running): 并不意味着进程一定在运行中,它表明进程要么是在运行中要么在运行队列里。
  • S睡眠状态(sleeping): 意味着进程在等待事件完成(这里的睡眠有时候也叫做可中断睡眠(interruptible sleep))。
  • D磁盘休眠状态(Disk sleep)有时候也叫不可中断睡眠状态(uninterruptible sleep),在这个状态的进程通常会等待IO的结束。
  • T停止状态(stopped): 可以通过发送 SIGSTOP 信号给进程来停止(T)进程。这个被暂停的程序可以通过发送 SIGCONT 信号让进程继续运行。
  • X死亡状态(dead):这个状态只是一个返回状态,你不会在任务列表里看到这个状态

S状态其实与操作系统中的阻塞状态(等待某种资源)是一致的!!!

D状态深度睡眠又是什么呢?和S睡眠有什么区别?

深度睡眠,不可中断睡眠。

相当于给进程一个免死金牌,我们要知道在Linux环境下,在操作系统逼急的时候,是会杀掉进程!如果这个进程很重要,就会造成很大的损失,因此这个D状态就相当于给了进程一个免死金牌,不会被进程所杀掉。

注意D状态依旧是阻塞状态

kill -19 pid 就会让一个进程进入T状态,也就是暂停状态

继续运行就是 kill -18 pid

子进程最终的状态都是Z

注意状态标识后面有+,就代表是前台进程,可以直接^C杀死,但是如果没有+,就说明这个进程变成了后台进程,不能用^C杀死,只能kill-9方法

后台进程,我们用普通的^C是停止不掉的,那怎么停止呢?

我们可以使用这样的命令:kill -9 pid(该进程的pid)

6.3、为什么要有Z状态?

创建进程是希望这个进程给用户完成工作的,子进程必须得有结果数据返回给父进程

6.4、什么是僵尸Z状态?

僵死状态(Zombies)是一个比较特殊的状态。当进程退出并且父进程(使用wait()系统调用,后面讲)
没有读取到子进程退出的返回代码时就会产生僵死(尸)进程
僵死进程会以终止状态保持在进程表中,并且会一直在等待父进程读取退出状态代码。
所以,只要子进程退出,父进程还在运行,但父进程没有读取子进程状态,子进程进入Z状态

6.5、僵尸进程危害:

进程的退出状态必须被维持下去,因为他要告诉关心它的进程(父进程),你交给我的任务,我办的怎
么样了。可父进程如果一直不读取,那子进程就一直处于Z状态?是的!
维护退出状态本身就是要用数据维护,也属于进程基本信息,所以保存在task_struct(PCB)中,换句话
说,Z状态一直不退出,PCB一直都要维护?是的!
那一个父进程创建了很多子进程,就是不回收,是不是就会造成内存资源的浪费?也就会造成内存的泄露!

7、什么是孤儿进程?

父进程如果提前退出,那么子进程后退出,进入Z之后,那该如何处理呢?
父进程先退出,子进程就称之为“孤儿进程”

这篇关于Linux进程初识:OS基础、fork函数创建进程、进程排队和进程状态讲解的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1143896

相关文章

linux-基础知识3

打包和压缩 zip 安装zip软件包 yum -y install zip unzip 压缩打包命令: zip -q -r -d -u 压缩包文件名 目录和文件名列表 -q:不显示命令执行过程-r:递归处理,打包各级子目录和文件-u:把文件增加/替换到压缩包中-d:从压缩包中删除指定的文件 解压:unzip 压缩包名 打包文件 把压缩包从服务器下载到本地 把压缩包上传到服务器(zip

hdu1043(八数码问题,广搜 + hash(实现状态压缩) )

利用康拓展开将一个排列映射成一个自然数,然后就变成了普通的广搜题。 #include<iostream>#include<algorithm>#include<string>#include<stack>#include<queue>#include<map>#include<stdio.h>#include<stdlib.h>#include<ctype.h>#inclu

hdu1565(状态压缩)

本人第一道ac的状态压缩dp,这题的数据非常水,很容易过 题意:在n*n的矩阵中选数字使得不存在任意两个数字相邻,求最大值 解题思路: 一、因为在1<<20中有很多状态是无效的,所以第一步是选择有效状态,存到cnt[]数组中 二、dp[i][j]表示到第i行的状态cnt[j]所能得到的最大值,状态转移方程dp[i][j] = max(dp[i][j],dp[i-1][k]) ,其中k满足c

hdu1171(母函数或多重背包)

题意:把物品分成两份,使得价值最接近 可以用背包,或者是母函数来解,母函数(1 + x^v+x^2v+.....+x^num*v)(1 + x^v+x^2v+.....+x^num*v)(1 + x^v+x^2v+.....+x^num*v) 其中指数为价值,每一项的数目为(该物品数+1)个 代码如下: #include<iostream>#include<algorithm>

Linux 网络编程 --- 应用层

一、自定义协议和序列化反序列化 代码: 序列化反序列化实现网络版本计算器 二、HTTP协议 1、谈两个简单的预备知识 https://www.baidu.com/ --- 域名 --- 域名解析 --- IP地址 http的端口号为80端口,https的端口号为443 url为统一资源定位符。CSDNhttps://mp.csdn.net/mp_blog/creation/editor

【Python编程】Linux创建虚拟环境并配置与notebook相连接

1.创建 使用 venv 创建虚拟环境。例如,在当前目录下创建一个名为 myenv 的虚拟环境: python3 -m venv myenv 2.激活 激活虚拟环境使其成为当前终端会话的活动环境。运行: source myenv/bin/activate 3.与notebook连接 在虚拟环境中,使用 pip 安装 Jupyter 和 ipykernel: pip instal

零基础学习Redis(10) -- zset类型命令使用

zset是有序集合,内部除了存储元素外,还会存储一个score,存储在zset中的元素会按照score的大小升序排列,不同元素的score可以重复,score相同的元素会按照元素的字典序排列。 1. zset常用命令 1.1 zadd  zadd key [NX | XX] [GT | LT]   [CH] [INCR] score member [score member ...]

在cscode中通过maven创建java项目

在cscode中创建java项目 可以通过博客完成maven的导入 建立maven项目 使用快捷键 Ctrl + Shift + P 建立一个 Maven 项目 1 Ctrl + Shift + P 打开输入框2 输入 "> java create"3 选择 maven4 选择 No Archetype5 输入 域名6 输入项目名称7 建立一个文件目录存放项目,文件名一般为项目名8 确定

Java 创建图形用户界面(GUI)入门指南(Swing库 JFrame 类)概述

概述 基本概念 Java Swing 的架构 Java Swing 是一个为 Java 设计的 GUI 工具包,是 JAVA 基础类的一部分,基于 Java AWT 构建,提供了一系列轻量级、可定制的图形用户界面(GUI)组件。 与 AWT 相比,Swing 提供了许多比 AWT 更好的屏幕显示元素,更加灵活和可定制,具有更好的跨平台性能。 组件和容器 Java Swing 提供了许多