当前位置: 首页 > news >正文

进程的查看与属性

一、进程的存储与查看

1.1为什么要有PCB

OS要管理进程,就要遵循“先描述,后组织”的原则,组织的结果就是PCB

1.2Linux中task_struct的存储与查看

1.2.1task_struct的存储

task_struct是一个内存级的,是Linux内核中一种数据结构的单位,在内存(RAM)中创建

1.2.2task_struct的查看(结合示例)

首先准备一个无限循环的code.c

将其编译运行后用来进行进程的查看

code.c:

  1 #include<stdio.h>2 #include<unistd.h>3 int main()4 {                                                                                                                                                        5     int n=0;6     while(1)7     {8 9         printf("%d\n",n);10         n++;11         sleep(1);12     }13     return 0;14 }

此时再打开一个shell,可以选择

ps ajx    查看的所有当前进行的程序

ps ajx结果:

这并不容易观察,我们可以:

利用head -1来提取第一行标题信息,利用grep来搜索需要的内容(一行执行多条指令,可以用;或者&&来连接)

为什么会有这一行呢?

这是因为grep查询mycode本身也是一个进程

我们可以选择用-v选项结合[忽视内容]来屏蔽grep的进程

1.3进程的创建方式:瞬时和常驻

实际上,windows中的双击或者运行可执行程序本质上就是在系统中启动了一个进程

瞬时进程:执行完就退出,如ls,pwd等

常驻进程:一直不退,只有用户关闭才会退出,例如刚刚写的mycode

1.4进程的结束方式

①使用kill指令的-9选项

第九选项SIGKILL可以结束进程 

使用方式:

kill -9 +[进程PID]

②ctrl+C杀进程

二、进程中的属性

2.1进程中的唯一标识符:PID

2.1.1PID是什么

PID是每个进程都会有的一个“唯一标识符

我们在ps axj查看进城的时候,可以发现每个进程都有一个PID(实际上PID随机分配底层类似累加器,而且一个进程结束以后再次运行,它的PID也是不一样的

mycode原PID:

mycode新PID:

2.1.2PID在代码中的获取方式

我们可以使用getpid函数来获取当前进程的PID

 使用时需要包含两个头文件:

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


它的返回值pid_t本质上就是一个int类型的typedef

  1 #include<stdio.h>2 #include<unistd.h>3 #include <sys/types.h>4 int main()5 {6     int n=0;7     while(1)8     {9 10         printf("%d\n",n);11         printf("PID:%d\n",getpid());                                                                                                                     12         n++;13         sleep(1);14     }15     return 0;16 }

运行结果:

2.2进程中的两个常用属性:exe和cwd

2.2.1根目录下储存进程中属性的文件夹proc

进程中的属性其实都存在根目录下的文件夹proc中,它是process的简称

在这个文件中,我们可以通过PID查找进程

进程中的属性

2.2.1补:ps底层就是/proc

ps 指令可以直接查看部分当前进程的属性,本质上也是调用/proc并整理其内容

它们的区别是:ps是封装的结果,/proc是系统调用接口

2.2.2exe属性

 指向的是对应进程源头可执行程序的路径

此时如果去该路径下删除可执行程序,运行中的程序不会立即停止,为什么?

 我们删除的是磁盘中的可执行程序,但是该可执行程序在此之前是通过被加载到了内存中来实现运行的,所以内存中的可执行程序依旧存在,不受影响

2.2.3cwd属性

全称是current work dir:当前工作目录

其展示的内容与pwd的结果基本一致

①作用:其作用的体现可以通过fopen来说明,例如在code.c中添加这样一行代码

FILE* fp=fopen("text.txt","w");

本质上就是将cwd的内容拼接到第一个参数列表之前,以此形成完整的访问路径

②cwd的主动修改:

 可以通过代码中的chdir函数来实现cwd的主动修改

(chdir函数:修改成功返回0,否则返回-1)

加入此时将code.c代码修改为

再次查看属性

2.2.4特殊的proc:内存在磁盘中的映射

实际上,/proc并不是磁盘级文件,他只是内存中对应对象(一个task_struct对象)的属性在磁盘中的“挂名”,属于内存级文件

2.3父进程标识:PPID

2.3.1什么是PPID

ppid中的第一个p指的是parent,即父进程的PID

2.3.2PPID在代码中的获取方式

可以在代码中使用getppid函数来获取

更改code.c代码

  1 #include<stdio.h>2 #include<unistd.h>3 #include <sys/types.h>4 int main()5 {6     int n=0;7     chdir("/home/my_normal/test_2_20");8     while(1)9     {10 11         printf("%d ",n);12         printf("PID:%d ",getpid());13         printf("PPID:%d\n",getppid());                                                                                                                   14         n++;15         sleep(1);16     }17     return 0;18 }

 打印结果

2.4bash进程

在上一个例子中,我们看到mycode的PPID值为25428,那么这到底是什么呢?

从结果可以看出:这正是bash

我们在命令行中执行命令/程序,本质是bash进程创建的子进程,由子进程来执行我们的代码

bash其实就是命令行解释器shell(属于统称)在Linux下的特化

bash什么时候创建呢?

我们每次登录时都会创建一个-bash

例如此时我进行了三次登录

三、子进程的创建

3.1fork函数进行子进程的创建

3.1.1fork函数的功能介绍

fork函数是一个系统调用接口

可以用来创建一个子进程,返回值分三种:

 ①创建成功,在父进程中返回子进程的PID

②创建成功,在子进程中返回0

③创建失败:返回-1

3.1.2fork函数使用举例

  1 #include<stdio.h>2 #include<unistd.h>3 4 int main()5 6 {                                                                                                                                                        7     printf("下面展示创建子进程后的情况:\n");8 9     pid_t pd=fork();10     (void)pd;11     printf("I am a process ,PID:%d ,PPID:%d\n",getpid(),getppid());12     sleep(1);13 14     return 0;15 }

运行结果:

分析结果我们可以看出:

第一条结果的PID是第二条结果的PPID,也就是父进程

对应的,第二条结果无疑就是子进程了

所以,使用fork函数可以让我们以原来的进程为父进程,继续创建新的子进程

3.1.2补:当变量定义但未使用时发出警告的处理方案

在使用vim中,有一个设置会让没有使用的变量上被标记一个警告,如果要正常编译运行我们需要将这一警告解除,此时可以选择

(void)+[变量]

3.1.3返回值的设置根据

为什么fork的返回值会这么设置呢?

实际上在Linux中,进程的整体是一个树形结构的

父 :子 = 1 :n

所以需要区分的是”父亲的孩子“,而不是”孩子的父亲“

3.2父子进程间代码的共享与数据的私有

3.2.1结合例子观察现象

代码逻辑:

  1 #include<stdio.h>2 #include<unistd.h>3 #include <sys/types.h>4 5 int tmp=0;6 int main()7 {8    printf("I am a process, pid: %d, ppid: %d\n", getpid(), getppid());9    pid_t id = fork();10    if(id > 0)11    {12        while(1)13        {   14            printf("我是父进程, pid: %d, ppid:%d, ret id: %d, gval: %d\n", getpid(), getppid(), id, tmp);                                                 15            tmp+=2;16            sleep(1);17        }18    }19    else if(id == 0)20    {21        while(1)22        {23            printf("我是子进程, pid: %d, ppid:%d, ret id: %d, gval: %d\n",                               getpid(), getppid(), id, tmp);24            tmp++;25            sleep(1);26        }27    }28    return 0;29    }

①当返回pid_t的值为子进程的PID时,说明当前进程为父进程,我们让tmp为偶数

②当返回pid_t的值为0时,说明当前进程为子进程,我们让tmp为奇数

运行结果

 综上,fork()的运行结果会呈现出两个进程(二者间是父子关系)

他们之间:代码是会共享的,但是数据各自私有一份

3.2.2为什么会出现代码共享

 我们知道:进程=内核数据结构+代码和数据

磁盘当中的代码和数据仅有一份加载到内存中,我们创建子进程时虽然为它创建了task_struct,但并没有对代码和数据做二次加载,默认指向与父进程同一份

3.2.3为什么数据各自私有一份

进程之间具有很强的独立性,多个进程之间运行时互不影响,就算是父子进程也一样

这种独立性在代码和数据二者中都有体现:①代码是只读的,不受多进程影响数据各自私有一份

3.2.4选择语句if-else同时得以执行的原因

我们例子中的if-else语句的内容都被执行了,这是因为在fork创建子进程后,这两个进程同时运行

第一个进程中走if的逻辑;第二个进程中走else的逻辑

但我们看到的只是他们同时运行的结果:if与else都被执行

对于pid_t id = fork();返回的本质就是向指定变量写入返回值数据,因此父/子进程有着不同的id变量

3.3多进程的创建

3.3.1C++的编译方式

创建C++文件:后缀为.cc  .cxx  .cpp都可以

需要先安装g++

sudo yum install -y gcc-c++

调用

g++ -o $@ $^ -std=c++11

3.3.2多进程创建的原理

多进程创建其实就是循环调用fork函数,每次调用都会形成当前父进程下一个新的的子进程

3.4对于子进程创建的理解

创建子进程使用的函数fork是一个系统调用接口

在fork内部:创建子进程意味着多了一个task_struct:先从父进程拷贝,然后调整一部分属性;

之后将这个新的task_struct连入到进程列表中,完成这些,父子进程就都已经开始运行了

为什么fork函数会有两个返回值呢?

其实是因为fork函数在父子进程中分别进行了返回,fork中俄return语句也属于代码,也是被父子进程共享的,但他们对应的数据并不一样

3.4补:fork运行结束后,父子进程谁先开始执行

这是不确定的,需要由OS调度器自行决定

相关文章:

  • java分页实例
  • Python + Playwright:编写自动化测试的避坑策略
  • Typora更改快捷键 markdown语法大全
  • 关闭谷歌浏览器(Google Chrome)的自动更新可以通过以下方法实现。具体操作步骤取决于你的操作系统。
  • C#VIN 码识别接口:汽车后市场的智能数据管理利器
  • 微服务即时通信系统---(四)框架学习
  • 体系结构论文(七十一):Quantifying the Impact of Data Encoding on DNN Fault Tolerance
  • 从JSON到SQL:基于业务场景的SQL生成器实战
  • Kubernetes(K8S)内部功能总结
  • 迁移python环境
  • 在 Redis 中存储对象类型的数据时,选择hash还是string?
  • 企业级Active Directory架构设计与运维管理白皮书
  • AI在代码Review中的应用试验与推广策略
  • word中插入图片显示不完整,怎么处理让其显示完整?
  • 智能系统:中钧科技的秘密武器
  • css hover 实现鼠标放上去后略微放大的效果
  • Kotlin学习记录2
  • ShenNiusModularity项目源码学习(17:ShenNius.Admin.Mvc项目分析-2)
  • selenium自动化测试实战案例
  • 【机器学习】如何正确下载sklearn包
  • 我国与沙特签署《核能发展安全与安保合作谅解备忘录》
  • 大国重器飞天背后,有一位上海航天的“老法师”
  • 数智时代出版专业技能人才培养研讨会在沪举行
  • 马拉松夺冠机器人将小批量量产:价格与一台入门级小轿车差不多
  • 人民日报和音:书写周边命运共同体建设新篇章
  • 海南开展药品安全“清源”行动,严查非法渠道购药等违法行为