进程概念、PCB及进程查看

文章目录

  • 一.进程的概念
    • 进程控制块(PCB)
  • 二.进程查看
    • 通过指令查看进程
    • 通过proc目录查看
    • 进程的`cwd`和`exe`
    • 获取进程pid和ppid
    • 通过fork()创建子进程

一.进程的概念

进程是一个运行起来的程序,而程序是存放在磁盘的,cpu要想执行程序的指令,需要先将程序加载到内存中。

课本概念:进程是被加载到内存运行的程序
内核观点:担当分配系统资源(CPU时间,内存)的实体。

操作系统中有着大量的进程,操作系统作为管理者,管理的其实是大量进程相关的数据,那么如何管理这些数据呢?

先描述,再组织

当二进制代码直接加载到内存时,操作系统为了更好地管理加载的程序,创建了描述该进程的数据结构。这样,操作系统只用看这个数据结构,不用管各种复杂多样的二进制代码,并且将它们组织起来进行管理

进程控制块(PCB)

这个数据结构叫PCB(process control block),进程信息被放在其中,可以理解为进程属性的集合,在linux的PCB是task_struct

struct task_struct {volatile long state; /* -1 unrunnable, 0 runnable, >0 stopped */struct thread_info *thread_info;atomic_t usage;unsigned long flags; /* per process flags, defined below */unsigned long ptrace;int lock_depth;  /* Lock depth */int prio, static_prio;struct list_head run_list;prio_array_t *array;//.....
}

当有一个程序被加载到内存时,操作系统会为该进程在内存中创建一个task_struct类型的对象,并将该进程放入双链表等其他结构中。这样,操作系统对进程的管理就变为操作系统对PCB的管理,再变为操作系统对双链表等结构的增删查改等操作

由此可以总结:进程 = 内核数据结构(PCB等)+ 可执行程序(代码+数据)

二.进程查看

通过指令查看进程

为了让进程能够一直运行方便观察,写一个死循环程序,让其每隔1秒钟打印一句话。

#include <stdio.h>
#include <unistd.h>int main()
{while(1){printf("It's a process.\n");sleep(1);}return 0;
}

随后运行它,此时该程序变成了一个进程:
在这里插入图片描述

接着就可以用ps指令查看进程信息,同时配合grep进行抓取

ps ajx | grep myprocess

得到以下结果:
在这里插入图片描述

可以看到系统中关于myprocess的进程一共有两个,第一行是我们写的运行的程序,第二行是grep命令进行抓取的进程。展示了各种信息:PPID、PID、PGID等等,这些就是PCB的一部分。
注意:task_struct是内核数据结构,查看进程信息读取该数据,必须要通过系统调用。

通过proc目录查看

proc是一个目录,里面存放当前系统实时的 进程信息
ls /proc
在这里插入图片描述

这里的数字就是进程的PID,由于此时已经将myprocess进程停止,此目录并没有找到名为167647的目录。
但是,仔细看,却有165058,这是刚才myprocess的父进程ID即PPID,通过指令可以知道,该进程其实就是bash
在这里插入图片描述

再次运行myprocess,并且通过指令得到其PID,进入该文件夹,可以发现进程的数据显式存在文件中。

在这里插入图片描述

进程的cwdexe

查看该目录详细信息,有两个文件很瞩目
在这里插入图片描述

cwd: Current Work Directory 指出该进程当前工作路径
exe: 指出该进程可执行程序的磁盘文件

修改程序,添加一个fopen函数

#include <stdio.h>
#include <unistd.h>int main() 
{FILE* fp = fopen("1.txt", "w");  // 若不存在就创建while (1) {printf("It's a process.\n");sleep(1);}
}

在这里插入图片描述

这恰好就是cwd链接的目录,说明fopen使用了查看cwd的系统调用。


再看exe,此时进程运行中,直接删除其链接在磁盘中的文件,发现进程没有终止,停止进程再运行显然就会失败了。
在这里插入图片描述

运行程序,本质就是将其从磁盘拷贝至内存中,进程与其磁盘上对应程序没有直接关系。

获取进程pid和ppid

可以直接通过系统调用getpid()getppid()得到当前进程的pid和ppid(父进程的pid),返回值为pid_t类型,底层就是整数。

运行以下代码

#include <stdio.h>
#include <unistd.h>int main()
{while (1){printf("It's a process.\t");printf("pid:%d, ppid:%d\n",getpid(), getppid());sleep(1);}return 0;
}

可以看到打印出当前进程的pidppid
在这里插入图片描述

通过ps axj | head -1; ps axj | grep 184670进行验证,当前进程是./myprocess且其父进程是bash

在这里插入图片描述

通过fork()创建子进程

通过man指令查看fork()函数细节
在这里插入图片描述

fork()函数可以创建子进程,创建成功后父子进程代码共享。
若成功创建,子进程的pid返回给父进程,0返回给子进程;
若失败,-1返回给父进程,没有子进程。

代码共享可以通过以下代码得到验证

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

fork()之前的代码只执行了一次,之后的代码执行了两次,这两次分别是两个进程执行的。
在这里插入图片描述


创建父子进程是为了做不同的事情,一般是通过if/else来进行分流达到的,这恰恰用到了fork()有两个返回值的特点,下面的代码若是初见一定会迷惑。

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>int main() 
{pid_t id = fork();// id: 0-子进程 >0-父进程if (id == 0){while(1){printf("child process, pid: %d, ppid: %d", getpid(), getppid());sleep(1);}}else{while(1){printf("father process, pid: %d, ppid: %d", getpid(), getppid());sleep(1);}}
}

利用父子进程fork()返回值不同,达到两个死循环都在不断执行的效果:
在这里插入图片描述

通过指令查看,确实两个进程是父子进程关系:

在这里插入图片描述


下面来简要分析上面的情况,具体细节会在之后进程地址空间部分详谈。

  1. 为什么两个死循环会同时执行❓

上节讲过,进程 = 内核数据结构(PCB等)+ 可执行程序(代码+数据)。通过fork()创建子进程,肯定也要给子进程创建一个独立的task_struct,而其代码和数据指向了父进程接下来的代码和数据。子进程的大部分属性值也是由父进程拷贝而来,修改前地址不会改变。
在CPU角度,它不会管谁是父进程,谁是子进程,会在操作系统的管理下并发执行。在我们的视角下,两个死循环同时执行了。

  1. 为什么fork()返回值如此设计❓

父与子的关系是一对一或者一对多的。这样的关系导致父找子并不容易,所以创建子进程成功后需要把子进程的pid返回给父进程,方便父进程控制子进程。
而子找父是很容易的,通过系统调用getppid()即可。

  1. 为什么fork()会返回两次值❓

fork()之前只有父进程,即只有父进程才能调用fork()fork()内部在return之前肯定已经将子进程创建成功,又子进程和父进程在创建成功后代码共享,那么子进程和父进程都会执行return这条语句,这也就是为什么fork()会返回两次值。

  1. 同一个变量id怎么会既大于0,又等于0❓

进程之间具有独立性,一个进程崩溃了,不会影响另一个进程。这里的id是父子进程的共享数据,若父子进程对共享数据有写操作,这时操作系统会将该数据拷贝两份,这就是写时拷贝。那么此时,虽然这是同一个变量名,但实际上表示的是不同的值,那么id出现两种情况也就不足为奇了,实际在底层的空间根本就不是一个。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

极客大学 java 进阶训练营怎么样,图文详解

Spring 思维导图 Spring 源码学习笔记 有关微服务的面试题&#xff1a; Dubbo中zookeeper做注册中心&#xff0c;如果注册中心集群都挂掉&#xff0c;发布者和订阅者之间还能通信么&#xff1f;微服务学习笔记 有关分布式的面试题&#xff1a; 消息幂等:如何保证消息不被重复…

如何手动设置u-boot的以太网的IP地址、子网掩码、网关信息、TFTP的服务器地址,并进行测试

设置IP地址 运行下面这条命令设置u-boot的以太网的IP地址&#xff1a; setenv ipaddr 192.168.5.9设置子网掩码 运行下面这条命令设置u-boot的以太网的子网掩码&#xff1a; setenv netmask 255.255.255.0设置网关信息 运行下面这条命令设置u-boot的网关信息&#xff1a; …

使用大语言模型对接OA系统,实现会议室预定功能

随着人工智能技术的不断进步&#xff0c;越来越多的企业开始借助 AI 助手来提高工作效率&#xff0c;尤其是在日常事务的自动化处理中。比如&#xff0c;在许多公司里&#xff0c;会议室的预定是一个常见且频繁的需求&#xff0c;通常需要员工手动检查空闲时间并做出选择。而通…

单链表:数据结构中的灵活“链条”

目录 &#x1f680;前言&#x1f914;单链表是什么&#xff1f;&#x1f4af;单链表的结构特点&#x1f4af;单链表的用途 ✍️单链表的实现与接口解释&#x1f4af;打印链表&#x1f4af;尾插操作&#x1f4af;头插操作&#x1f4af;头删操作&#x1f4af;尾删操作&#x1f4a…

Redis面试宝典【刷题系列】

文章目录 一、什么是Redis&#xff1f;二、Redis相比Memcached有哪些优势&#xff1f;三、Redis支持的数据类型有哪些&#xff1f;四、Redis的主要消耗的物理资源是什么&#xff1f;五、Redis的全称是什么&#xff1f;六、Redis有哪些数据淘汰策略&#xff1f;七、为什么Redis需…

uni-app集成sqlite

Sqlite SQLite 是一种轻量级的关系型数据库管理系统&#xff08;RDBMS&#xff09;&#xff0c;广泛应用于各种应用程序中&#xff0c;特别是那些需要嵌入式数据库解决方案的场景。它不需要单独的服务器进程或系统配置&#xff0c;所有数据都存储在一个单一的普通磁盘文件中&am…

pytest-html

首先安装pytest-html库 #执行命令 pytest --htmlreport.html ./pytest-html.pyimport pytest import logging def test_pass():"""用例通过"""assert Truedef test_fail():"""用例失败"""assert Falsedef test_e…

kafka为什么这么快?

前言 Kafka的高效有几个关键点&#xff0c;首先是顺序读写。磁盘的顺序访问速度其实很快&#xff0c;甚至比内存的随机访问还要快。Kafka在设计上利用了这一点&#xff0c;将消息顺序写入日志文件&#xff0c;这样减少了磁盘寻道的时间&#xff0c;提高了吞吐量。与传统数据库的…

从DeepSeek的爆火来看大模型微调技术的发展方向

“深度人工智能”是成都深度智谷科技旗下的人工智能教育机构订阅号&#xff0c;主要分享人工智能的基础知识、技术发展、学习经验等。此外&#xff0c;订阅号还为大家提供了人工智能的培训学习服务和人工智能证书的报考服务&#xff0c;欢迎大家前来咨询&#xff0c;实现自己的…

Dify使用教程(创建应用)

Dify的安装部署我已经写过了&#xff0c;简单的模型配置我也在前面进行了讲解&#xff0c;今天我们主要来讲讲如何使用Dify。 一、创建应用 我们可以通过三种方式在Dify的工作室内创建应用 01 基于应用模板创建&#xff08;新手推荐&#xff09;02 创建一个空白应用03 通过D…

system verilog的流操作符

流操作符&#xff0c;有分为操作对象是一整个数组和单独的数据两种&#xff0c;例如bit [7:0] a[4]和bit [31:0] b&#xff0c;前者操作对象是数组&#xff0c;后者是单独一个较大位宽的数。 流操作符有<<和>>&#xff0c;代表从右向左打包和从左向右打包。 打包的…

项目实战--网页五子棋(匹配模块)(4)

上期我们完成了游戏大厅的前端部分内容&#xff0c;今天我们实现后端部分内容 1. 维护在线用户 在用户登录成功后&#xff0c;我们可以维护好用户的websocket会话&#xff0c;把用户表示为在线状态&#xff0c;方便获取到用户的websocket会话 package org.ting.j20250110_g…

hot100_108. 将有序数组转换为二叉搜索树

hot100_108. 将有序数组转换为二叉搜索树 思路 给你一个整数数组 nums &#xff0c;其中元素已经按 升序 排列&#xff0c;请你将其转换为一棵 平衡 二叉搜索树。 示例 1&#xff1a; 输入&#xff1a;nums [-10,-3,0,5,9] 输出&#xff1a;[0,-3,9,-10,null,5] 解释&#…

Win11更新系统c盘爆满处理

1.打开磁盘管理 2.右击c盘选择属性&#xff0c;进行磁盘管理&#xff0c;选择详细信息。 3.选择以前安装的文件删除即可释放c盘空间。

深入理解 JSP 与 Servlet:原理、交互及实战应用

一、引言 在 Java Web 开发领域,JSP(JavaServer Pages)和 Servlet 是两个至关重要的技术,它们共同构成了动态网页开发的基础。Servlet 作为服务器端的 Java 程序,负责处理客户端请求并生成响应;而 JSP 则是一种简化的 Servlet 开发方式,允许开发者在 HTML 页面中嵌入 J…

[通俗易懂C++]:指针和const

之前的文章有说过,使用指针我们可以改变指针指向的内容(通过给指针赋一个新的地址)或者改变被保存地址的值(通过给解引用指针赋一个新值): int main() {int x { 5 }; // 创建一个整数变量 x&#xff0c;初始值为 5int* ptr { &x }; // 创建一个指针 ptr&#xff0c;指向 …

DL/CV领域常见指标术语(FLOPS/mIoU/混淆矩阵/F1-measure)------一篇入门

1. FLOPS、FLOPs和GFLOPs FLOPS: floating-point operations per second&#xff0c;每秒浮点运算次数&#xff0c;用来衡量硬件性能。 FLOPs&#xff1a;floating point of operations&#xff0c;是浮点运算次数&#xff0c;用来衡量算法、模型的复杂度。 GFLOPS&#xff…

被裁20240927 --- WSL-Ubuntu20.04安装cuda、cuDNN、tensorRT

cuda、cuDNN、tensorRT的使用场景 1. CUDA&#xff08;Compute Unified Device Architecture&#xff09; 作用&#xff1a; GPU 通用计算&#xff1a;CUDA 是 NVIDIA 的并行计算平台和编程模型&#xff0c;允许开发者直接利用 GPU 的并行计算能力&#xff0c;加速通用计算任…

DeepSeek vs ChatGPT:AI对决中的赢家是……人类吗?

DeepSeek vs ChatGPT&#xff1a;AI对决中的赢家是……人类吗&#xff1f; 文章目录 DeepSeek vs ChatGPT&#xff1a;AI对决中的赢家是……人类吗&#xff1f;一、引言1. 背景2. 问题 二、DeepSeek vs ChatGPT&#xff1a;谁更胜一筹&#xff1f;2.1 语言生成能力评测对比场景…

旧手机热点无法提供ipv6解决方法(emui 8 热点提供ipv6)

旧手机热点无法提供ipv6解决方法 手机&#xff1a;荣耀8x 系统版本: EMUI 8 网络&#xff1a;移动流量卡 解决方案 设置-》无线和网络-》移动网络-》接入点名称(APN)-》cmiot 修改 APN协议: IPv4/IPv6 修改 APN漫游协议: IPv4/IPv6