Linux系列-进程的属性

🌈个人主页:羽晨同学 

💫个人格言:“成为自己未来的主人~”  

我们上一篇文章当中讲到了进程的概念,我们知道进程信息是放到一个tast_struct的结构体当中的,这个结构体也叫做PCB。

PCB是内存级的操作,我们把这个结构体放到CPU当中。

首先,我们来说一下应该如何查看进程呢?

比如说,我们创建一个code的进程。

#include<stdio.h>
int main()
{while(1){printf("hello world\n");}return 0;
}
~                                                                        
~              

然后我们将这个代码运行起来,就创建了一个进程,接下来,让我们来查看我们的进程。

ps ajx | head -1 && ps ajx | grep code

通过这个命令,我们就可以查看到下面的进程列表。 

 其实这个命令本身就是好几个进程。

ps是一个,head是一个,grep本身就是一个,然后查看到的code也是一个,当我们想要取消掉grep进程的时候,可以后面加一个grep -v grep,就可以过滤掉grep进程

这个时候,就没有grep进程了。

其实把程序运行起来,本身就是在系统上启动一个进程。

像linux当中的指令,如:ls who am i pwd等都是系统对应的指令,都是系统对应的文件,相对应的,这种也都是启动的进程不过这种进程是瞬时的,一跑就跑完了。

还有一种不是瞬时的,就比如各种应用程序。

所以,进程分为两种:

执行完就推出的。

除非用户主动关闭,否则一直不退的(常驻进程:杀毒软件)

为了识别进程,所以进程是有编号的,也就是进程的pid。

同一个程序,在不同的时间启动,pid是会发生变化的,背后的原理是用了一个累加的计数器。

如果连续创建,pid是会连续的。

如果是我们刚才创建的进程,这个时候,他的PID是17293,当我们重新启动这个进程。

这个时候他的PID是19421

 此外,进程是具有唯一性的,体现到PID上就是,每个进程的PID都是不同的。

但是进程又是可以知道自己的PID的

pid_t  pid getpid();

通过这条指令,我们就可以获得进程的id值

#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>int main()
{pid_t id = getpid();while(1){printf("hello bit,i am a process pid: %d\n",id);sleep(1);}return 0;
}
~      

你看,这样子的话,我们就可以获得进程的PID值了 

我们取消进程的方式一般有两种,一种是ctrl c 还有一种是使用kill命令,其实两个方式的本质相同,都是杀进程。

比如说,这个时候我们启动一个进程,我们可以使用下面的这个命令来删除进程:

kill -9 21858

这个时候,这个进程就被删除了

在Linux中,在根目录下面有一个/proc(目录),全称叫做process,把进程的很多信息以文件的形式呈现出来。

这里面的一个个数字都是代表着进程的PID,每一个目录都是代表的一个进程。 

而每一个目录里面都存放着这个进程的所有属性。

只要启动了一个进程,就会瞬间在/proc里面创建一个这个PID的文件夹。

比如说,我刚才创建一个22877的进程,就可以在里面直接打开这个进程。

再次把这个进程干掉之后,就找不到这个文件夹了,也就没有22877这个东西了。

当我们进入这个目录的时候,有两个东西是值得我们关注的。

一个是exe,一个是cwd,

首先,我们说一下exe。

当我们这个时候终止这个进程,那就是删除了这个可执行文件。

但是这个时候代码还在运行,这是因为这个时候已经加载到内存当中了,删除的是磁盘当中的可执行文件。

 而对于那个cwd来说,cwd指的是current work dir,是当前的工作目录。

我们创建程序或者文件的时候,一般都是默认在当前目录下面创建的。,而这个当前目录,就是cwd所指向的路径。

比如说,这个时候我们新建一个程序:

#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>int main()
{FILE *fp=fopen("log.txt","w");if(fp==NULL){}pid_t id = getpid();while(1){printf("hello bit,i am a process pid: %d\n",id);sleep(1);}return 0;
}

当这个代码运行之后,我们发现在当前目录下面创建一个log.txt的文件夹

怎么说呢,其实就是自己写的代码,当开始跑的时候,从上到下开始执行的时候,就是将cwd表示的路径拼接到log.txt的前面。所以会在当前路径下面新建文件。

如果想要改变工作路径的话,我们可以使用 int chdir,参数就是一个新的路径

比如说

chdir("/");

这个时候我们将默认的工作路径改到了根目录下面。

这个时候我们启动进程,看一下当前的cwd的指向。

看,这个时候的cwd就指向了根目录。

但是我们打开根目录却看到下面什么都没有,这是因为创建失败了(系统没有这个权限)

换到其他路径下面就可以了。 

对于我们前面的ps来说,ps的作用就是打来ls /proc 并且进行文本分析

/proc 目录不是磁盘级的文件,所以当系统重启的时候,这个目录下面也会刷新,进程相关的属性以文件的形式展现出来,不同担心影响系统的效率。

接下来,我们说一下ppid。

在linux系统重新启动之后,创建任何进程的时候,新创建的进程都是由父进程创建的。

我们通过下面这个指令来获取父进程:

int getppid();

比如说,我们创建下面的这个代码并形成进程。

#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>int main()
{/*chdir("/");FILE *fp=fopen("log.txt","w");if(fp==NULL){}*/pid_t ppid =getppid();pid_t id = getpid();while(1){printf("hello bit,i am a process pid: %d,ppid: %d\n",id,ppid);sleep(1);}return 0;
}

我们可以很明显的发现的是每一次pid都会发生变化,但是ppid却一直不发生变化。

在命令行当中,执行命令,执行程序,本质就是bash的进程,创建的子进程,执行我们的代码,这个bash,也叫做命令行解释器,shell,是所有命令行解释器的统称。在LInux当中,一般使用的是bash。

那么一个bash是怎么创建子进程的呢?

使用系统调用来创建进程,比如说,我们执行./程序,其实就是相当于我们告诉系统,我们要执行这个程序。

接下来,我们来见一下子进程的创建。

接口:

fork()

作用是创建一个子进程。

返回-1则失败了,成功的话,给父进程返回子进程的pid,给子进程返回0 。

接下来,我们创建一个程序来创造子进程:

#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>int main()
{/*chdir("/");FILE *fp=fopen("log.txt","w");if(fp==NULL){}*/printf("I am a process pid %d ppid %d\n",getpid(),getppid());pid_t id = fork();(void)id;printf("I am a 分支 pid %d ppid %d \n",getpid(),getppid());/*pid_t ppid =getppid();pid_t id = getpid();while(1){printf("hello bit,i am a process pid: %d,ppid: %d\n",id,ppid);sleep(1);}*/return 0;
}

这个是代码的运行结果:

 经过fork之后,有了两个执行分支,一个调用printf,另外一个也调用printf,这两个分支之间也是父子关系,一个是父进程(自己),一个子进程(fork创建的),一个父进程是可以创建出多个子进程的,所以进程其实也是树形结构。

我们接下来看下面的这段代码:

#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
int gval=0;int main()
{printf("i am a process, pid: %d,ppid: %d\n",getpid(),getppid());pid_t id=fork();if(id > 0){while(1){printf("我是父进程,pid : %d,ppid:%d,ret id:%d %d\n",getpid(),getppid(),id,gval);gval+=1;sleep(1);}}else if(id==0){while(1){printf("我是子进程,pid : %d,ppid:%d,ret id:%d %d\n",getpid(),getppid(),id,gval);sleep(1);}}return 0;
}

我们可以看到,这两个死循环同时再跑->两个执行流同时在跑,if 和else if同时成立,从fork之后就会有两个进程,分布进行,fork有两个返回值,给父进程返回子进程的pid,给子进程返回0.

一个父进程有多个子进程,但是一个子进程只会有一个父进程

 fork函数

fork创建了一个进程,使得原本的一个进程变成了两个呈现父子关系的进程,一般而言,这两个进程之间的代码是会共享的,但是数据不会,数据会各自私有一份。

所以,进程=内核数据结构+代码和数据

我们每次给新进程创建PCB,可以子进程和父进程相比,并没有数据和代码从磁盘继承,只有内核数据结构,所以,系统要求子进程和父进程共享代码,数据是各自私有一份的。

为什么呢?

因为代码具有很强的独立性,多个进程之间运行互不影响,即便是父子,两个进程之间也有很强的独立性。

所以,代码虽然是共享的,但是是只读的,而数据是各自私有一份的。

Pid_t id =fork();

在这个函数当中,

  • id是一个变量
  • 返回的本质,就是向指定变量进行写入返回值数据
  • 打印的本质,就是读取 

 创建多进程

那么其实有一个很奇怪的点,就是fork怎么会有两个返回值呢?

其实就是因为多了一个进程,也就是多了一个task_struct,一般而言,子进程的task_struct是从父进程拷贝下来的,调整了新的task_struct的部分属性。

所以有一部分属性,父子进程是相同的,代码要指向同一块,但是pid不同。

当fork的时候,fork是一个系统调用,当我们return的时候,它们的核心工作已经做完了,父子进程都已经开始运行了。

return本身就是一行代码

而fork之后代码是会共享的,所以父进程执行一次return,子进程执行一次return。

那么fork之后,是哪个进程先运行呢? 这个其实是不确定的(有OS调度器自主决定)。

但是时间片可能是一个方面的影响因素。

 好了,本次的文章就到这里了,我们下次再见。 

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

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

PLC单键启停控制的多种写法

题目&#xff1a;编写程序&#xff0c;实现当按下SB1按钮奇数次&#xff0c;灯亮&#xff1b;当按下SB1按钮偶数次&#xff0c;灯灭&#xff0c;即单键启停控制&#xff0c;设计梯形图。 解法一&#xff1a;使用标志位进行自锁互锁 &#xff08;1&#xff09;刚上电&#xff0c…

PH热榜 | 2024-11-06

DevNow 是一个精简的开源技术博客项目模版&#xff0c;支持 Vercel 一键部署&#xff0c;支持评论、搜索等功能&#xff0c;欢迎大家体验。 Github&#xff1a;https://github.com/LaughingZhu/DevNow 1. MindOne Builder 标语&#xff1a;这是一个“设计优先”的应用构建工具…

11.6 校内模拟赛总结

打的很顺的一场 复盘 7:40 开题&#xff0c;看到题目名很interesting T1 看起来很典&#xff0c;中位数显然考虑二分&#xff0c;然后就是最大子段和&#xff1b;T2 构造&#xff1f;一看数据范围这么小&#xff0c;感觉不是很难做&#xff1b;T3 神秘数据结构&#xff1b;T…

SpringBoot健康监控

文章目录 1_监控-健康监控服务2_监控-Admin可视化3_使用介绍 1_监控-健康监控服务 目的&#xff1a;能够理解健康监控actuator的作用 讲解&#xff1a; 每一个微服务在云上部署以后&#xff0c;我们都需要对其进行监控、追踪、审计、控制等。SpringBoot就抽取了Actuator场景…

时间序列算法---ARIMA

时间序列其他相关参考文章&#xff1a; 时间序列分类任务—tsfresh库 时间序列预测—Prophet python时间序列处理 有季节效应的非平稳序列分析   现代时间序列分析方法主要有两个不同的方向&#xff1a;一个方向是由外向内的分析视角产生的方法是与确定性因素分解相关的方法&…

Java内存模型

Java内存模型 JMM即java memory model,它定义了主存、工作内存抽象概念,底层对应着CPU寄存器、缓存、硬件内存、CPU指令优化等。 JMM体现在以下几个方面 原子性 - 保证指令不受到线程上下文切换的影响(之前的synchornized原理文章有介绍过) 可见性 - 保证指令不会受CPU缓…

软考高级架构 - 8.2 - 系统架构评估 - 超详细讲解+精简总结

8.2-系统架构评估 系统架构评估就是对系统架构的质量进行分析&#xff0c;以便帮助设计者作出架构决策&#xff0c;确保系统能够符合需求。评估方法大致分为三种&#xff1a; 基于问卷或检查表&#xff1a;通过设计好的问卷或清单&#xff0c;收集开发人员和相关人员的…

【LLM】Generative Agents和代码实践

note Generative Agents是2023年斯坦福提出的agent小镇&#xff0c;通过memory->reflection->planning的框架提高NPC的行为目标性&#xff0c;给游戏NPC的灵活设计带来了可能。Generative Agents是一种多智能体交互的框架&#xff0c;它模拟现实中的人类行为。这些Agent…

K. Farm Management 【CCPC2024哈尔滨站】

K. Farm Management 思路: 很容易想到的策略&#xff1a; 给每个作物都安排最短的时长 l l l&#xff0c;剩下的时间作为可支配时间。选择删除收益最高的作物&#xff0c;然后将尽可能多的可支配时间用于这个作物。或者选择删除收益低时间还长的作物&#xff0c;然后将时间解…

智能 ODN 系统研究与设计

智能 ODN 系统研究与设计 摘 要&#xff1a;为了解决ODN面临的光纤错综复杂&#xff0c;故障定位低效等问题&#xff0c;引入电子标签&#xff0c;智能OTDR技术&#xff0c;提出了一种基于电子标签、光纤检测笔和智能OTDR故障监测的智能 ODN 解决方案,并重点讲述了智能ODN系统的…

Openlayers实现长度测量

概述 在 Openlayers 中,计算两点之间的距离,通常会用到ol/sphere模块。ol/sphere模块主要用于处理与球体(特别是地球球体)相关的数学和几何计算。而长度测量主要用到ol/sphere中的getDistance函数。 getDistance函数用于计算地球表面两点之间的距离,通常用于经纬度坐标。…

xftp连接中不成功 + sudo vim 修改sshd_config不成功的解决方法

我们使用sudo vim不成功&#xff0c;但是我们使用sudo su就可以 了&#xff01; root用户权利更大&#xff01; 喵的&#xff0c;终于成功了&#xff0c;一个xftp连接半天不成功。&#xff08;添加上面的内容就可以连接成功了↑&#xff09;

常用的c++特性-->day02

可调用对象 案例 #include <iostream> #include <string> #include <vector> using namespace std;using funcptr void(*)(int, string);int print(int a, double b) {cout << a << ", " << b << endl;return 0; } // …

应急车道占用检测算法的技术方案与应用

应急车道是高速公路上的生命通道&#xff0c;专门用于救护车、消防车、警车等紧急车辆通行&#xff0c;帮助应对突发状况。然而&#xff0c;一些驾驶员出于各种原因违规占用应急车道&#xff0c;阻碍救援车辆的正常通行&#xff0c;导致交通救援效率大幅下降&#xff0c;甚至加…

卡尔曼滤波算法Kalman filter algorithm

一、假如 状态向量服从高斯分布&#xff1a; 而且状态转移是线性的&#xff1a; 测量是状态的线性关系&#xff08;带噪声&#xff09; 初始状态的置信度也是正态分布 二、卡尔曼滤波的算法流程为 疑问&#xff1a;多测量融合&#xff0c;是不是用不同的传感器测量值去重复5…

java_继承

1.为啥用 继承? Pupil类 package com.hspedu.extend;// 小学生->模拟小学生考试的情况 public class Pupil {public String name;public int age;private double score;public void setScore(double score) {this.score score;}public void testing() {System.out.printl…

Redis 缓存击穿

目录 缓存击穿 什么是缓存击穿&#xff1f; 有哪些解决办法&#xff1f; 缓存穿透和缓存击穿有什么区别&#xff1f; 缓存雪崩 什么是缓存雪崩&#xff1f; 有哪些解决办法&#xff1f; 缓存预热如何实现&#xff1f; 缓存雪崩和缓存击穿有什么区别&#xff1f; 如何保…

【Redis的安装以及主从复制的搭建】配置Redis的哨兵模式

文章目录 一、安装Redis1、上传、解压、重命名2、安装GCC环境3、编译源码4、进行安装5、修改配置文件redis.conf 二、Redis主从复制搭建1、创建文件夹用来实现主从复制 三、配置Redis的哨兵模式1、创建文件夹2、修改sentinel.conf配置文件3、启动 一、安装Redis 1、上传、解压…

WPF中的转换器

单值转换器 1.创建项目后下载两个NuGet程序包 2.先定义一个转换器实现IValueConverter接口 using System; using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; usin…

GEE Ui——批量查询Sentinel-2 图像无云区域的可视化应用(提供缩略图)

目录 简介 功能选项 函数 Map.clear() No arguments. Returns: ui.Map Map.onClick(callback) Arguments: Returns: String reduceRegion(reducer, geometry, scale, crs, crsTransform, bestEffort, maxPixels, tileScale) Arguments: Returns: Dictionary toLis…