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使用fdisk进行磁盘的相关操作

《Linux使用fdisk进行磁盘的相关操作》fdisk命令是Linux中用于管理磁盘分区的强大文本实用程序,这篇文章主要为大家详细介绍了如何使用fdisk进行磁盘的相关操作,需要的可以了解下... 目录简介基本语法示例用法列出所有分区查看指定磁盘的区分管理指定的磁盘进入交互式模式创建一个新的分区删除一个存

Window Server2016 AD域的创建的方法步骤

《WindowServer2016AD域的创建的方法步骤》本文主要介绍了WindowServer2016AD域的创建的方法步骤,文中通过图文介绍的非常详细,对大家的学习或者工作具有一定的参考学习价... 目录一、准备条件二、在ServerA服务器中常见AD域管理器:三、创建AD域,域地址为“test.ly”

Linux使用dd命令来复制和转换数据的操作方法

《Linux使用dd命令来复制和转换数据的操作方法》Linux中的dd命令是一个功能强大的数据复制和转换实用程序,它以较低级别运行,通常用于创建可启动的USB驱动器、克隆磁盘和生成随机数据等任务,本文... 目录简介功能和能力语法常用选项示例用法基础用法创建可启动www.chinasem.cn的 USB 驱动

高效管理你的Linux系统: Debian操作系统常用命令指南

《高效管理你的Linux系统:Debian操作系统常用命令指南》在Debian操作系统中,了解和掌握常用命令对于提高工作效率和系统管理至关重要,本文将详细介绍Debian的常用命令,帮助读者更好地使... Debian是一个流行的linux发行版,它以其稳定性、强大的软件包管理和丰富的社区资源而闻名。在使用

龙蜥操作系统Anolis OS-23.x安装配置图解教程(保姆级)

《龙蜥操作系统AnolisOS-23.x安装配置图解教程(保姆级)》:本文主要介绍了安装和配置AnolisOS23.2系统,包括分区、软件选择、设置root密码、网络配置、主机名设置和禁用SELinux的步骤,详细内容请阅读本文,希望能对你有所帮助... ‌AnolisOS‌是由阿里云推出的开源操作系统,旨

python中os.stat().st_size、os.path.getsize()获取文件大小

《python中os.stat().st_size、os.path.getsize()获取文件大小》本文介绍了使用os.stat()和os.path.getsize()函数获取文件大小,文中通过示例代... 目录一、os.stat().st_size二、os.path.getsize()三、函数封装一、os

Linux Mint Xia 22.1重磅发布: 重要更新一览

《LinuxMintXia22.1重磅发布:重要更新一览》Beta版LinuxMint“Xia”22.1发布,新版本基于Ubuntu24.04,内核版本为Linux6.8,这... linux Mint 22.1「Xia」正式发布啦!这次更新带来了诸多优化和改进,进一步巩固了 Mint 在 Linux 桌面

LinuxMint怎么安装? Linux Mint22下载安装图文教程

《LinuxMint怎么安装?LinuxMint22下载安装图文教程》LinuxMint22发布以后,有很多新功能,很多朋友想要下载并安装,该怎么操作呢?下面我们就来看看详细安装指南... linux Mint 是一款基于 Ubuntu 的流行发行版,凭借其现代、精致、易于使用的特性,深受小伙伴们所喜爱。对

Python在固定文件夹批量创建固定后缀的文件(方法详解)

《Python在固定文件夹批量创建固定后缀的文件(方法详解)》文章讲述了如何使用Python批量创建后缀为.md的文件夹,生成100个,代码中需要修改的路径、前缀和后缀名,并提供了注意事项和代码示例,... 目录1. python需求的任务2. Python代码的实现3. 代码修改的位置4. 运行结果5.

使用IntelliJ IDEA创建简单的Java Web项目完整步骤

《使用IntelliJIDEA创建简单的JavaWeb项目完整步骤》:本文主要介绍如何使用IntelliJIDEA创建一个简单的JavaWeb项目,实现登录、注册和查看用户列表功能,使用Se... 目录前置准备项目功能实现步骤1. 创建项目2. 配置 Tomcat3. 项目文件结构4. 创建数据库和表5.