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

【Linux】Linux进程基础

1.进程介绍与概念

进程的本质是在计算机内存中运⾏的程序,但是这⼀个概念太过于⼴泛

每个应用程序运行于现代操作系统之上时,操作系统会提供一种抽象,好像系统上只有这个程序在运行,所有的硬件资源都被这个程序在使用。这种假象是通过抽象了一个进程的概念来完成的,进程可以说是计算机科学中最重要和最成功的概念之一。

进程是操作系统对一个正在运行的程序的一种抽象,换言之,可以把进程看做程序的一次运行过程;同时,在操作系统内部,进程又是操作系统进行资源分配的基本单位。

要了解进程,⾸先采⽤前⾯在操作系统基础中介绍的思想「先描述,再组织」,每⼀ 个进程本质也是⼀个结构(⼀般称为PCB(Process Control Block,进程控制 块),在Linux下的PCB称为 task_struct ),计算机将程序加载到内存,就会在 内存中添加⼀个进程,那么此时就会形成⼀个结构体,该结构体中存在进程的⼀些描 述,例如进程的在内存中的位置、进程的编号、对应数据和代码的地址等,在程序执 ⾏前,进程会被排列到⼀个数据结构,通过操作系统调度器从数据结构中取出进程就 代表对应的进程获取到CPU的使⽤权,此时就会通过对应进程的结构体中的相关数据 加载代码和数据进⾏运算、逻辑处理,从⽽到达执⾏程序的效果

这⼀过程可以看出,进程=程序代码和数据+进程结构体 进程可以粗略得分为两种:

1. 执⾏完对应代码就退出

2. ⼀直运⾏在后台,也被称为「常驻进程」

2.Linux下查看进程

⾸先,通过下⾯的C语⾔代码创建⼀个进程:

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

对应的Makefile代码如下:

TARGET=process 
SRC=process.c
 
$(TARGET):$(SRC)
    gcc -o $@ $^
 
.PHONY:clean
clean:
    rm -rf $(TARGET)

编译运⾏前⾯的C语⾔代码,并在Linux下查看对应进程

可以使⽤下⾯的⽅式:

ps ajx | head -1 && ps ajx | grep process
 
# 如果存在部分干扰信息,可以使用结合管道和grep的-v指令对部分干扰信息的关键字进行过滤

上⾯的指令中的 ps ajx代表查看当前系统运⾏时所有的进程以及部分信息,直接执 ⾏就可以看到进程信息

可以看到对应的进程为:

在Linux下实际上进程都被保存在⼀个名为 proc 的⽬录下,这个⽬录不是存在于磁 盘中的⽬录,⽽是操作系统运⾏时进程出现创建的,使⽤下⾯的命令查看该⽬录:

ls /proc

proc ⽬录在根⽬录下,与 home ⽬录同级

再次运⾏前⾯的C语⾔代码,使⽤下⾯的代码查看程序进程信息以及在 proc下的位置

3.结束进程的方式

在Linux下,可以使⽤两种⽅式结束进程:

1. 快捷键 ctrl+c

2. 使⽤ kill 指令: Kill -9 进程PID

4.进程对应程序文件位置

在前⾯C语⾔程序对应的进程⽬录中除了可以看到 cwd 以外,还可以看到⼀个名为 exe 的软链接,该链接指向着⼀个路径,如下:

该路径对应的即为加载到内存中的代码和数据(进程组成的⼀部分),如果在运⾏时 将该路径下的 process ⽂件删除,此时已经运⾏的进程不会受到影响,因为对应的 代码和数据已经加载进内存,受影响的只是下⼀次⽆法直接使⽤运⾏程序的⽅式创建 进程

删除后再次查看结果如下:

5.进程PID与 PPID

PID 是Linux系统下每⼀个进程对应的⼀个编号,该编号会因为进程产⽣的时间不同 ⽽不同,例如每⼀次运⾏前⾯的C语⾔代码都会得到⼀个不同的PID

如果想获取到当前进程的 PID 可以使⽤getpid()函数 该函数原型如下:

 pid_t getpid(void);

其中p id_t 类似于 long 类型,是⼀个⻓整型,该函数不需要传递形参

在前⾯的C语⾔代码中添加该函数并打印该进程的 PID 对⽐使⽤指令查看进程信息下的PID

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

可以看到PID⼀致

⼀般连续创建的进程,PID 是连续的     PID 是进程的编号    PPID 是指定进程的⽗进程的编号,当前进程也被称为对应⽗进程的⼦进程

同样,在代码中想获取到当前进程的⽗进程对应的 PPID ,可以使⽤ getppid() 函 数,原型如下:

pid_t getppid(void);

对前⾯C语⾔的代码进⾏修改,如下:

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
 
int main() 
{   
    pid_t id = getpid();
    pid_t pid = getppid();
    while(1) {
        printf("hello world, pid = %d ppid = %d\n", id, pid);
        sleep(1);
    }
 
    return 0;
}

多次运⾏代码后结果如下:

可以发现,每⼀次运⾏代码, PID 都会改变,但是 PPID 始终不变,因为其⽗进程始 终是同⼀个进程,通过查看进程的指令可以找到 PID 为14901对应的⽗进程:

bash 是Linux下的指令解释器,每⼀次执⾏指令时,实际上就是将对应的指令交给 了bash,由 bash 再交给操作系统进⾏执⾏,当执⾏上⾯程序的进程时,实际上是 创建了⼀个 bash 的⼦进程,再由⼦进程执⾏对应的程序代码和数据

6.创建子进程 fork

6.1简单了解 fork函数

fork 函数原型如下:

pid_t fork(void);

在Linux编程⼿册中对 fork 的解释如下:

对应的函数返回值的解释如下:

⼤意为:成功的情况下,该函数返回⼦进程的 PID 给⽗进程,返回0给⼦进程。失败 的情况下,该函数返回-1给⽗进程,不创建任何⼦进程,并恰当设置错误代码

6.2简单使用 fork函数

根据前⾯对 fork 函数的介绍,修改前⾯的C语⾔代码如下:

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
 
int main() 
{   
    pid_t id = fork();
    if(id > 0) {
        printf("I am prarent process, my pid = %d my ppid = %d\n", getpid(), getppid());
    } else if(id == 0) { 
        printf("I am child process, my pid = %d my ppid = %d\n", getpid(), getppid());
    }
    return 0;
}

运行结果如下:

可以看到,⼦进程的 PPID 即为⽗进程 PID ,与前⾯的思想对应

7.简单了解 fork细节

上⾯的代码中⼀共存在着三个待解决的问题:

1. 使⽤ fork 创建⼦进程,为什么可以通过if 和 else if区分⼦进程和⽗进程

对于第⼀个问题来说,根据 fork 函数的返回值描述,当函数运⾏成功的情况下,会 返回⼦进程的 PID 给⽗进程, PID ⼀般都是⼤于0的,⽽⼦进程会收到 返回值为0,所以当上⾯代码中的id变量⼤于0时,就会⾛ fork 函数的 if 语句,⽽唯⼀⼀个接 收到的id⼤于0的就是⽗进程;同理,⼦进程的id变量等于0,就会⾛ else if 语句。

2. 为什么 fork 函数出现了两个返回值

对于第⼆个问题来说,实际上执⾏ fork 函数时,在返回 pid 之前就已经执⾏完了创 建⼦进程逻辑,此时⽗进程和⼦进程共享代码,但是各⾃独⽴数据,⽗进程拥有⾃⼰ 的返回值,⼦进程也拥有⾃⼰的返回值,所以此时 return 的时候是每个进程返回各 ⾃的返回值

3. 为什么两个分⽀语句if和 else if都执行了

对于第三个问题来说,当代码执⾏到 fork 函数时,会执⾏ fork 函数的内部逻辑, 再执⾏完其主逻辑代码后,就已经创建出了⼦进程(对应存在⼀个⼦进程的 task_struct ),此时⼦进程会与⽗进程共享同⼀块代码,但是⼦进程拥有⾃⼰的 数据,并使⽤⾃⼰的数据执⾏代码。⽽之所以需要共享同⼀块代码,是因为⽗进程的 代码是从硬盘中加载的,但是⼦进程的代码并不存在与硬盘,从⽽也就⽆法加载,⽽ 之所以不同的进程有着不同的数据,这是进程独⽴性的主要体现,可以⽤下图先简单 理解这个过程,具体过程会在进程地址空间讲解:

此处,⼦进程的 task_struct 实际上先是对⽗进程的task_struct 的拷⻉,再对其中的内容进⾏⼀定的修改;


http://www.kler.cn/a/349969.html

相关文章:

  • 2024 自主创业事业小结和2025展望
  • 深入理解三高架构:高可用性、高性能、高扩展性的最佳实践
  • Linux应用编程(五)USB应用开发-libusb库
  • 实战经验:使用 Python 的 PyPDF 进行 PDF 操作
  • 数据结构:二叉树
  • ChatGPT 摘要,以 ESS 作为你的私有数据存储
  • pdf文件怎样一张纸打印四页
  • 多模态大语言模型(MLLM)-InstructBlip深度解读
  • 【自动驾驶汽车通讯协议】GMSL通信技术以及加串器(Serializer)解串器(Deserializer)介绍
  • 中小型医院网站:Spring Boot框架详解
  • kali chrome 安装 hackbar
  • 16年408计算机网络
  • 【动手学深度学习】6.5 汇聚层(个人向笔记)
  • 阿里云数据库导出 | 数据管理(兼容数据库备份)
  • Java->排序
  • 影刀RPA接口_查询应用主流程参数结构
  • 上半年净利下滑85%,光峰科技能否靠“上车”扭转局面?
  • oneAPI学习-使用oneAPI 实现矩阵乘法并分析性能瓶颈
  • DINODINO v2:颠覆自监督视觉特征表示学习
  • Docker 环境下 GPU 监控实战:使用 Prometheus 实现 DCGM Exporter 部署与 GPU 性能监控
  • 力扣3158.求出出现两次数字的XOR值 python
  • tcl/perl 脚本命令学习
  • 基于SpringBoot+Vue的益农智慧服务平台【提供源码+答辩PPT+参考文档+项目部署】
  • 2017年-2021年 软件工程程序设计题(算法题)实战_c语言程序设计数据结构程序设计分析
  • 12- Cesium 中动态处理与四个圆形渐变过渡材质相关的属性
  • 【微信小程序_4_小程序view组件和swiper组件】