【Linux探索学习】进程的概念及详细解释和一些简单的相关操作

目录

一.什么是进程?

1.1查看进程

1.2进程的cwd与exe

二.认识fork-系统调用创建进程

[什么是 fork()?](#什么是 fork()?)

[fork() 的返回值](#fork() 的返回值)

[独立的实现机制:写时复制(Copy-on-Write, COW)](#独立的实现机制:写时复制(Copy-on-Write, COW))


一.什么是进程?

从用户的视角来看,进程是一个程序的运行实例;从操作系统的视角来看,进程是一个拥有资源分配能力的实体。

进程 = 内核数据结构对象(PCB) + 自己的代码和数据

在Linux中进程可以看做是PCB(task struct)和自己的代码和数据 组成的。PCB中包含该进程的所有属性,与代码以及数据共同组成进程,PCB中存在指向其他进程的指针,通过指针的指向,进程通过双向链表 的数据结构来进行链接,而进程的管理就是对链表的增删查改。并且每个进程都有独立的地址空间,以避免相互干扰。

PCB主要内容(PCB 被实现为一个名为task_struct的结构体)

  • 标识符:如进程 ID (PID),用于唯一标识进程。
  • 状态:包括进程当前的运行状态(运行、就绪、阻塞等)。
  • 优先级:用于调度时比较不同进程的重要性。(CPU计算的优先级)
  • 程序计数器:存储下一条将要执行的指令地址。
  • 内存指针:指向进程的代码段、数据段以及共享内存块。
  • 上下文数据:包括处理器寄存器中的数据。
  • I/O 状态信息:描述进程使用的文件和 I/O 设备。
  • 记账信息:记录进程使用的资源总量和时间。

所有进程的 PCB 以链表形式组织。通过 task_struct 中的 nextprev 指针,形成一个双向链表,对进程进行遍历和管理。

1.1查看进程

我们可以通过查看/proc/文件,来查看我们目前正在执行的全部进程

上面蓝色的数字就是进程的PID,每个进程都会有一个对应的PID,PID就是我们上面所说的进程ID,也叫做进程标识符,我们可以通过这些进程标识符来查看每个进程具体的信息,比如查看1号进程

除了上面的这个方法外,我们还可以通过ps axj这个指令,不仅可以看到所有的进程,还可以看到它们的进程的属性信息

我们可以只打印出一行来看一下进程属性的内容(利用管道 | 和打印行数head)

这里要注意的点就是PPID指的是父进程标识符,PID知道是当前进程标识符、

我们可以查看下我们自己创建的这个进程的相关信息(注意只有当我们的程序在跑着的时候它才叫进程,所以我们可以将我们的程序写成一个死循环,然后让它执行起来)

复制代码
ps axj | head -1 && ps axj | grep mytest

1.2进程的cwdexe

cwd是一个符号链接,指向进程的当前工作目录。当前工作目录是指进程在执行过程中,其相对路径的基准目录。就好比你在终端中切换到某个目录,然后运行一个程序,这个被切换到的目录就是程序的当前工作目录。

例如,假设你在/home/user/projects目录下启动了一个名为my_app的程序,那么/proc/[PID]/cwd就会指向/home/user/projects目录。

exe是一个符号链接,指向启动该进程的可执行文件的路径。这个可执行文件是进程运行的主体,包含了程序的机器代码和资源。

例如,如果你使用命令/usr/bin/my_app启动了一个程序,那么/proc/[PID]/exe就会指向/usr/bin/my_app。

二.认识fork-系统调用创建进程

  • 回值为pid_t类型
  • 包含在头文件<unistd.h>

可以通过以下代码获取进程的 PID 和其父进程的 PPID:

cpp 复制代码
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>

int main() {
    printf("pid: %d\n", getpid());
    printf("ppid: %d\n", getppid());
    return 0;
}

fork() 是 Linux 中用于创建新进程的函数,我们先来看下面的一个小程序:

cpp 复制代码
  1 #include<stdio.h>
  2 #include<unistd.h>
  3 #include<sys/types.h>
  4 int main()
  5    {
  6 
  7 |      printf("我是父进程!!,我的pid:%d\n",getpid());
  8 |      fork();
  9 |      printf("我是子进程!!,我的pid:%d\n",getpid());
 10        return 0;
 11    }

可以看出,在fork();执行后出现了两个进程,其中一个进程的pid是fork前的进程的pid,一个是新进程的pid。

cpp 复制代码
1 #include<stdio.h>
  2 #include<unistd.h>
  3 #include<sys/types.h>
  4  int main()
  5 {
  6 pid_t id = fork(); // 父子进程的独立过程是在调用 fork() 函数时完成,之后父子进程独立
  7 
  8 if(id < 0)
  9 {
 10 |   perror("fork");
 11 |   return 1;
 12 }|  
 13 else if(id == 0)
 14 {
 15 |   // child 
 16 |   
 17 |   
 18 |   
 19 |    printf("我是一个子进程!我的pid:%d,我的父进程id:%d\n",getpid(), getppid());
 20 |   
 21 }
 22 else
 23 {
 24 |   // father
 25 |  
 26 |   
 27 |   
 28 |     printf("我是父进程!我的pid:%d,我的父进程id:%d\n",getpid(), getppid());
 29 |   
 30 }
 31                                                                                                                                                                                                                                  
 32 printf("返回结束\n");
 33 
 34 return 0;
 35 }
~        

子进程和父进程的执行流从 fork() 的返回值处分叉:

父进程 继续运行时,fork() 返回子进程的 PID。子进程 继续运行时,fork() 返回 0。

什么是 fork()

fork()是用于创建进程的系统调用。

它会从当前运行的进程(称为父进程)中复制出一个几乎完全相同的新进程(称为子进程)。

父子进程几乎完全独立,但共享相同的代码段

父子进程拥有不同的内存空间,彼此之间不影响。

fork() 的返回值
  1. 在父进程中fork() 返回子进程的 PID(进程 ID),这是一个正整数(> 0)。
  2. 在子进程中fork() 返回 0
  3. 创建子进程失败返回-1。
独立的实现机制:写时复制(Copy-on-Write, COW)

现代操作系统使用了一种优化机制,叫做 写时复制(COW),以减少不必要的资源浪费:

  • **fork()** 刚返回时,父子进程共享相同的物理内存页(只读),因此复制过程很快。
  • 当父进程或子进程试图修改内存时
    • 操作系统会为需要修改的部分分配新的物理内存。
    • 修改后的内存空间对父子进程来说是独立的。

因此,只有在需要时,内存的独立性才真正实现,也就是需要对对内存中数据进行修改的时候,但逻辑上,父子进程从 fork() 返回后就已经被视为完全独立了。

相关推荐
条tiao条2 小时前
鸿蒙 ArkTS 学习入门
学习·华为·harmonyos
雨霁初曦2 小时前
学习通解除粘贴限制完整详细教程(电脑网页版+手机版,含截图示例)
学习·学习通
Hugh-Yu-1301232 小时前
WSL --Ubuntu-24.04-LTS子系统安装教程
linux·windows·ubuntu
EnglishJun2 小时前
ARM嵌入式学习(二十四)--- 库移植(移植到开发板)
arm开发·学习
HalvmånEver2 小时前
MySQL数据库表(table)操作
linux·数据库·学习·mysql
月光下的麦克2 小时前
Ubuntu 与 Windows 之间文件互传
linux·运维·ubuntu
爱学习的小囧2 小时前
ESXi 升级 8.0 后 Realtek 网卡无法识别解决教程
运维·网络·esxi·realtek 网卡
宇钶宇夕2 小时前
西门子S7-1200 OB1主程序构架-填料天车1
运维·自动化
wanhengidc2 小时前
服务器该如何防范网络攻击?
运维·服务器·网络·网络协议·安全·web安全·智能手机