[Linux]进程概念

[Linux]进程概念

文章目录

  • [Linux]进程概念
    • 进程的定义
    • 进程和程序的关系
    • Linux下查看进程
    • Linux下通过系统调用获取进程标示符
    • Linux下通过系统调用创建进程-fork函数使用

进程的定义

进程是程序的一个执行实例,是担当分配系统资源(CPU时间,内存)的实体。

进程和程序的关系

由编程语言编写的代码经过编译后形成的二进制程序会存储在硬盘中,当计算机启动一个程序时,会将程序的相关代码和和数据加载到内存中,供CPU来使用:

image-20230803184610548

程序的代码和数据加载到内存后,操作系统就要对程序进行管理,为了更好的管理这些程序,需要对先创建相应的结构来描述这些程序,在操作系统中,用于描述程序的结构叫做进程控制块(Process Control Block,简称 PCB),Linux系统下的PCB名为task_struct,PCB中也会记录相应的代码和数据的地址,为了更好的访问这些PCB使用链式结构将其组织起来:

image-20230803185254037

task_ struct内容分类如下:

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

如果只是将程序的代码和数据加载到内存中,但是操作系统没有为其创建PCB进行管理,操作系统就不会调度它,它就无法完成程序的执行,因此进程的本质是内存中的代码和数据+进程控制块,有了PCB后,操作系统就将对进程的管理转化为了对PCB的管理,比如如果要关闭一个进程就将其PCB删除,然后对应的内存就会清空其在内存中的代码和数据:

image-20230803190524985

Linux下查看进程

为了更好的在Linux操作系统上查看进程,创建源文件myprocess.c和makefile文件来创建二进制程序,

源文件中内容如下:

#include <stdio.h>
#include <unistd.h>

int main()
{
  while(1)
  {
    printf("hello myprocess\n");
    sleep(1);
  }
  return 0;
}

其中makefile的内容如下:

myprocess:myprocess.c
	gcc -o myprocess myprocess.c
.PHONY:clean
clean:
	rm -f myprocess

创建好以上文件并编译得到名为myprocess的二进制程序,然后在Linux下启动两个客户端,其中一个启动程序变成进程:

image-20230803193450976

再另一个客户端输入ps axj | head -1 && ps axj | grep myprocess | grep -v grep查看myprocess进程:

image-20230803193707725

以上为使用指令查看进程,指令如下:

ps axj | head -1 && ps axj | grep 进程名 | grep -v 进程名

另外还可以在/proc目录下看到进程:

image-20230803193931708

/proc目录是一个内存级的目录,不存在于硬盘中,目录中会有命名和pid相同的目录,该目录中会记录对应进程的task_struct,如果进程关闭了对应的目录也就删除了。

Linux下通过系统调用获取进程标示符

Linux操作系统为了唯一标识一个进程,给每个进程设置了一个进程标识符在PCB中,也就是pid。并且也提供了系统接口函数getpid来获取当前进程的pid,其介绍如下:

image-20230803195336806

为了测试getpid函数修改源文件myprocess.c,内容如下:

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>

int main()
{
  while(1)
  {
    printf("hello myprocess, 我的pid是%d\n", getpid());
    sleep(1);
  }
  return 0;
}

用指令查询进程pid和查看进程执行结果:

image-20230803195628555

另外Linux操作系统中还设置了父进程标识符,用于记录当前进程的父进程pid,也就是ppid,同时也提供了getppid函数来获取当前进程的ppid,ppid的介绍如下:

image-20230803200016344

为了测试getppid函数修改源文件myprocess.c,内容如下:

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>

int main()
{
  while(1)
  {
    printf("hello myprocess, 我的pid是%d, 我的ppid为%d\n", getpid(), getppid());
    sleep(1);
  }
  return 0;
}

用指令查询进程pid和查看进程执行结果:

image-20230803201344196

多次利用 ctrl+c关闭进程,然后重新启动进程:

image-20230803201448298

可以看出,无论进程的pid如何变化,进程的ppid都不会变化,我们尝试用指令查看这个父进程:

image-20230803201620342

实际上这个这个父进程就是bash,通过如上现象我们可以得到如下结论:

  • 命令行解释器(bash)本质也是一个进程。
  • 命令行启动的所有程序最终都会变成进程,而该进程对应的父进程都是bash。

Linux下通过系统调用创建进程-fork函数使用

fork函数是Linux系统提供的创建子进程的系统调用。

  • fork函数运行成功后,执行流会变成两个,一个是调用fork函数的父进程,另一个是fork函数创建的子进程。
  • 创建的子进程会和父进程共享父进程代码和数据,子进程会执行父进程fork函数创建子进程之后的代码。
  • fork函数给父进程返回子进程的pid,给创建的子进程返回0,出错返回-1。

为了测试fork函数修改源文件myprocess.c,内容如下:

#include <stdio.h>
#include <assert.h>
#include <unistd.h>

int main()
{
  pid_t id = fork();
  if (id == 0)
  {
    //子进程
    printf("我是子进程,我的pid是%d, 我的ppid是%d\n", getpid(), getppid());
    sleep(2);
  }
  else if (id > 0)
  {
    //父进程
    printf("我是父进程,我的pid是%d, 我的ppid是%d\n", getpid(), getppid());
    sleep(3);
  }
  else 
  {
    //fork函数出错
    assert(1);
  }
  return 0;
}

说明:

  • fork函数所需要的头文件是unistd.h
  • 使用条件判断来控制父子进程执行不同的代码。

用指令查询进程和查看进程执行结果:

image-20230804105943957

fork函数的原理

进程的本质是PCB+内存中的代码和数据,由于fork函数创建的子进程是和父进程共享代码和数据的,因此fork函数创建子进程的原理是创建一个PCB给子进程,该PCB中大部分数据是和父进程相同的,并且指向同一份代码和数据:

image-20230804113115080

进程独立性在fork中的体现

首先给出如下定理:进程之间是相互独立的,一个进程的任何操作都不会影响其他进程。

在使用fork函数创建子进程进程之间的独立性也能得到保证,为了验证独立性修改源文件myprocess.c,内容如下:

#include <stdio.h>
#include <assert.h>
#include <unistd.h>

int main()
{
  pid_t id = fork();
  if (id == 0)
  {
    //子进程
    printf("我是子进程,我的pid是%d, 我的ppid是%d\n", getpid(), getppid());
    sleep(20);
    printf("我是子进程,我的pid是%d, 我的ppid是%d\n", getpid(), getppid());
    printf("我是子进程,我已经关闭了\n");
  }
  else if (id > 0)
  {
    //父进程
    printf("我是父进程,我的pid是%d, 我的ppid是%d\n", getpid(), getppid());
    sleep(3);
    printf("我是父进程,我已经关闭了\n");
  }
  else 
  {
    //fork函数出错
    assert(1);
  }
  return 0;
}

用指令查询进程和查看进程执行结果:

开始时,父子进程一起执行:

image-20230804114253740

父进程关闭,子进程正常运行:

image-20230804114347222

最后子进程关闭:

image-20230804114411668

由以上测试可以看出,父进程的关闭不影响子进程正常执行,保证了一定的独立性。另外由于代码是只读的,父进程无法通过修改代码来影响子进程,而数据的修改会触发写时拷贝机制,保证了一定的独立性。

为了观察写时拷贝现象,修改源文件myprocess.c,内容如下:

#include <stdio.h>
#include <assert.h>
#include <unistd.h>

int main()
{
  int a = 0;
  pid_t id = fork();
  if (id == 0)
  {
    //子进程
    printf("我是子进程,我的pid是%d, 我的ppid是%d, a:%d, &a:%p\n", getpid(), getppid(), a, &a);
    sleep(5);
    printf("我是子进程,我的pid是%d, 我的ppid是%d, a:%d, &a:%p\n", getpid(), getppid(), a, &a);
  }
  else if (id > 0)
  {
    //父进程
    printf("我是父进程,我的pid是%d, 我的ppid是%d, a:%d, &a:%p\n", getpid(), getppid(), a, &a);
    a = 666;
    printf("我是父进程,我的pid是%d, 我的ppid是%d, a:%d, &a:%p\n", getpid(), getppid(), a, &a);
    sleep(3);
    printf("我是父进程,我已经关闭\n");
  }
  else 
  {
    //fork函数出错
    assert(1);
  }
  return 0;
}

查看进程执行结果:

image-20230804120919703

观察现象可以发现,父进程修改a的值后,子进程的a的值并没有改变,但是父进程和子进程的a变量的地址是相同,这就是发生了写时拷贝造成的现象。

fork函数返回两个返回值的原理

由于fork创建的子进程和父进程共享代码和数据,并且fork函数也是父进程的代码的一部分,因此父进程完成子进程的创建后,子进程也会执行fork函数创建子进程后续的剩余代码,其中就包括fork函数中return返回的部分,因此父进程执行了return部分,子进程也执行了return部分,造成fork函数返回两个返回值的现象:

image-20230804121340511

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/90872.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

HAProxy+nginx搭建负载均衡群集

目录 一、常见的Web集群调度器 二、HAProxy群集介绍 1、Haproxy的特性 : 2、Haproxy常用的调度算法 ① 轮询调度&#xff08;Round Robin&#xff09; ② 最小连接数&#xff08;Least Connections&#xff09; ③ 基于来源访问调度算法&#xff08;Source Hashing&am…

Java“牵手”天猫店铺所有商品API接口数据,通过店铺ID获取整店商品详情数据,天猫API申请指南

天猫商城是一个网上购物平台&#xff0c;售卖各类商品&#xff0c;包括服装、鞋类、家居用品、美妆产品、电子产品等。天猫商品详情可以帮助消费者更好的了解宝贝信息&#xff0c;从而做出购买决策。同时&#xff0c;消费者也可以通过商品详情了解其他买家对宝贝的评价&#xf…

神经网络入门

前言 本文主要介绍最基础的神经网络&#xff0c;包括其结构&#xff0c;学习方法&#xff0c; C \texttt{C} C 的实现代码。 Python \texttt{Python} Python 的代码可以搜索互联网得到。 前排提示&#xff1a;本人涉及一丁点数学知识。 神经网络的结构 神经网络包括多个层…

Linux--进程地址空间

1.线程地址空间 所谓进程地址空间&#xff08;process address space&#xff09;&#xff0c;就是从进程的视角看到的地址空间&#xff0c;是进程运行时所用到的虚拟地址的集合。 简单地说&#xff0c;进程就是内核数据结构和代码和本身的代码和数据&#xff0c;进程本身不能…

构建智慧停车场:4G DTU实现无线数据高速传输

物联网技术的快速发展使得各种设备能够实现互联互通&#xff0c;无线网络技术给我们的日常生活带来了极大的便利。其中的网络技术如无线WiFi及4G网络已经成为了物联网应用中不可或缺的组成部分。而在工业领域中对4G无线路由器的应用是非常广泛的&#xff0c;人们通过4G工业路由…

eclipse中设置按backspace键、或者delete键,一次删除代码中多个空格

选择菜单Window->Preferences&#xff1a; 在弹出窗口中&#xff0c;找到General->Text Editors&#xff0c;在右面的选项中勾选Insert spaces for tabs和Remove multiple spaces on backspace/delete&#xff0c;然后点击窗口下面的Applay and Close按钮&#xff1a; …

【Linux】进程通信 — 信号(上篇)

文章目录 &#x1f4d6; 前言1. 什么是信号1.1 认识信号&#xff1a;1.2 信号的产生&#xff1a;1.3 信号的异步&#xff1a;1.4 信号的处理&#xff1a; 2. 前后台进程3. 系统接口3.1 signal&#xff1a;3.1 - 1 不能被捕捉的信号 3.2 kill&#xff1a;3.2 - 1 killall 3.3 ra…

Vue3.0极速入门- 目录和文件说明

目录结构 以下文件均为npm create helloworld自动生成的文件目录结构 目录截图 目录说明 目录/文件说明node_modulesnpm 加载的项目依赖模块src这里是我们要开发的目录&#xff0c;基本上要做的事情都在这个目录里assets放置一些图片&#xff0c;如logo等。componentsvue组件…

巨人互动|Facebook海外户Facebook游戏全球发布实用策略

Facebook是全球最大的社交媒体平台之一&#xff0c;拥有庞大的用户基数和广阔的市场。对于游戏开发商而言&#xff0c;利用Facebook进行全球发布是一项重要的策略。下面小编将介绍一些实用的策略帮助开发商在Facebook上进行游戏全球发布。 巨人互动|Facebook海外户&Faceboo…

Maven的超级POM

对于我们创建的一个maven工程&#xff0c;即便我们自己的pom.xm文件中没有明确指定一个父工程&#xff08;父POM&#xff09;&#xff0c;其实也默认继承了超级POM&#xff0c;就好比JAVA类继承Object类一样。 maven官网关于超级POM的介绍&#xff1a; https://maven.apache.o…

3.BGP状态机和路由注入方式

BGP状态机 BGP路由的生成 不同于IGP路由协议,BGP自身并不会发现并计算产生路由,BGP将GP路由表中的路由注入到BGP路由表中,并通过Update报文传递给BGP对等体。 BGP注入路由的方式有两种: Networkimport-route与IGP协议相同,BGP支持根据已有的路由条目进行聚合,生成聚合路由…

关于css 的选择器和 css变量

css 选择器 常用的选择器 1. 后代选择器&#xff1a;也就是我们常见的空格选择器&#xff0c;选择的对象为该元素下的所有子元素 。例如&#xff0c;选择所有 元素下的 元素 div p{font-size:14px}2. 子元素选择器 ‘>’ 选择某元素下的直接子元素。例如&#xff0c;选择所…

stm32之8.中断

&#xff08;Exceptions&#xff09;异常是导致程序流更改的事件&#xff0c;发生这种情况&#xff0c;处理器将挂起当前执行的任务&#xff0c;并执行程序的一部分&#xff0c;称之为异常处理函数。在完成异常处理程序的执行之后&#xff0c;处理器将恢复正常的程序执行&#…

PyQt open3d 加载 显示点云

PyQt加载 显示点云&#xff0c;已经有三种方式&#xff0c;使用 open3d; 使用 vtk; 使用 pcl; 下面是使用 open3d: import sys import open3d as o3d import numpy as np import pyqtgraph.opengl as gl from PyQt5.QtWidgets import QApplication, QVBoxLayout, QWidget, QFi…

LeetCode 138.复制带随机指针的链表

文章目录 &#x1f4a1;题目分析&#x1f4a1;解题思路&#x1f6a9;步骤一&#xff1a;拷贝节点插入到原节点的后面&#x1f369;步骤一代码 &#x1f6a9;步骤二&#xff1a;控制拷贝节点的random进行连接&#x1f369;步骤二代码 &#x1f6a9;步骤三&#xff1a;拷贝节点解…

Linux 内核page migration设计文档

概述 page migration设计之初是在numa system的各个node之间迁移physical pages&#xff0c;意味着进程页面的虚拟地址不会变化&#xff0c;物理地址发生改变&#xff0c;migration的目的将page迁移到临近的cpu上降低内存访问延迟。 页面迁移粗略步骤 A. In kernel use of m…

4.14 tcp_tw_reuse 为什么默认是关闭的?

开启 tcp_tw_reuse 参数可以快速复用处于 TIME_WAIT 状态的 TCP 连接时&#xff0c;相当于缩短了 TIME_WAIT 状态的持续时间。 tcp_tw_reuse 是什么&#xff1f; TIME_WAIT 状态的持续时间是 60 秒&#xff0c;这意味着这 60 秒内&#xff0c;客户端一直会占用着这个端口。端…

桃子叶片病害识别(Python代码,pyTorch框架,深度卷积网络模型,很容易替换为其它模型,带有GUI识别界面)

1.分为三类 健康的桃子叶片 &#xff0c;251张 桃疮痂病一般&#xff0c;857张 桃疮痂病严重&#xff0c;770 张 2. GUI界面识别效果和predict.py识别效果如视频所示桃子叶片病害识别&#xff08;Python代码&#xff0c;pyTorch框架&#xff0c;深度卷积网络模型&#xff0…

探索图结构:从基础到算法应用

文章目录 理解图的基本概念学习图的遍历算法学习最短路径算法案例分析&#xff1a;使用 Dijkstra 算法找出最短路径结论 &#x1f389;欢迎来到数据结构学习专栏~探索图结构&#xff1a;从基础到算法应用 ☆* o(≧▽≦)o *☆嗨~我是IT陈寒&#x1f379;✨博客主页&#xff1a;I…

光伏+旅游景区

传统化石燃料可开发量逐渐减少&#xff0c;并且对环境造成的危害日益突出。全世界都把目光投向了可再生能源&#xff0c;希望可再生能源能够改变人类的能源结构。丰富的太阳能取之不尽、用之不竭&#xff0c;同时对环境没有影响&#xff0c;光伏发电是近些年来发展最快&#xf…