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

【Linux】进程概念(PCB)与进程创建(fork)

🔥个人主页🔥:孤寂大仙V
🌈收录专栏🌈:Linux
🌹往期回顾🌹:【Linux】进程铺垫——冯诺依曼体系与操作系统概念
🔖流水不争,争的是滔滔不


  • 一、进程是什么
  • 二、task_struct
  • 三、查看进程
    • 1. getpid和getppid
    • 2. ps axj
    • 3. ps axj | grep proess
    • 4. ps axj | head -1
    • 5. 进程的信息可以通过 /proc 系统文件夹查看
  • 三、创建进程(fork)

一、进程是什么

在操作系统中,进程是资源分配和独立运行的基本单位。它是程序在一个数据集合上运行的过程,它是系统进行资源分配和调度的一个独立单位。单纯的只看一个定义很难理解什么是进程,下面用一张图进行描述。
在这里插入图片描述
我们磁盘中的可执行程序,CPU要想拿到并且执行,代码和数据要先放在内存中。对于没有了解过的人可能以为放在内存中的代码和数据就是进程,其实不然。操作系统是一个软件在内存中,当磁盘中的可执行程序被内存拿到,可执行程序的代码和数据会被内存拿到,内存中的操作系统会对代码和数据进行描述然后组织为数据结构(先组织在描述)形成内核数据结构对象,对进程的管理就变成了对数据结构对象的增删查改。 内核数据结构对象可以称为PCB,也叫做进程控制块
内核数据结构对象通过指针指向本身的代码和数据也指向下一个结构体,进而形成真正的进程。进程=内核数据结构对象+自己的代码和数据。 这个数据结构就是进程列表。CPU对进程列表进行调度


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

在具体的操纵系统中,Linux的PCB是task_struct。Linux的进程=tack_struct+自己的代码和数据。进程的所有属性都可以通过PCB(task_struct)直接或者间接的找到。

二、task_struct

在Linux中描述进程的结构体叫做task_struct。
task_struct是Linux内核的⼀种数据结构,它会被装载到RAM(内存)里并且包含着进程的信息。

  1. 标示符: 描述本进程的唯⼀标示符,用来区别其他进程。
  2. 状态: 任务状态,退出代码,退出信号等。
  3. 优先级: 相对于其他进程的优先级。
  4. 程序计数器: 程序中即将被执行的下⼀条指令的地址。
  5. 内存指针: 包括程序代码和进程相关数据的指针,还有和其他进程共享的内存块的指针
  6. 上下⽂数据: 进程执行时处理器的寄存器中的数据[休学例⼦,要加图CPU,寄存器。
  7. I∕O状态信息: 包括显示的I/O请求,分配给进程的I∕O设备和被进程使用的文件列表。
  8. 记账信息: 可能包括处理器时间总和,使用的时钟数总和,时间限制,记账号等。
    具体的功能在今后的文章中会进行讲解。

三、查看进程

我们执行的所有指令,工具,自己的程序运行起来,全都是进程。

1. getpid和getppid

查看系统调用进程的进程标识符,getppid是查看父进程的进程标识符。
在这里插入图片描述
gitpid和girppid是系统调用。

2. ps axj

通过指令查看系统进程
在这里插入图片描述

3. ps axj | grep proess

查看具体的进程
在这里插入图片描述

4. ps axj | head -1

打印进程的属性,就是显示的是什么的信息。只有进程的id也看不懂,所有得打印头更好看都是哪些信息。
在这里插入图片描述


所有平时我们查询进程的时候就用

ps axj | head -1; ps axj | grep process

在这里插入图片描述
也可以用下面指令,效果是一样的

 ps axj | head -1 && ps axj | grep process

在这里插入图片描述

5. 进程的信息可以通过 /proc 系统文件夹查看

在这里插入图片描述
要获取PID为1的进程信息,你需要查看 /proc/1 这个文件夹。

查看具体进程

ls /proc/1406525 -dl

在这里插入图片描述


ls /proc/1406525 -l

在这里插入图片描述
这条指令下exe,表示进程所对应的可执行文件。如果在这个文件下删除可执行文件,正在运行的进程不会崩,因为可执行文件已经拷贝在了内存当中。
这条指令下cwd,进程启动的时候,记录下来当前进程所处的文件。

bash是Linux的命令行解释器,操作系统没登陆一个用户就会分配一个bash。我们命令行所写的命令都是先给bash。

三、创建进程(fork)

fork是一个系统调用,用于创建一个新的进程。当一个进程调用fork时,操作系统会创建一个与原进程几乎完全相同的新进程。这个新进程被称为子进程,而调用fork的进程被称为父进程。
fork函数在父进程和子进程中返回不同的值:在父进程中,fork返回子进程的进程 ID(PID);在子进程中,fork返回 0。这个返回值可以用来区分父进程和子进程。
在这里插入图片描述

#include<stdio.h>
#include<unistd.h>
int main()
{
        printf("这是一个父进程 %d\n",getpid());
        fork();
        printf("这是一个进程 %d\n",getpid());
}

在这里插入图片描述
fork前的执行:在调用fork之前,程序会执行printf(“i am a process, my pid: %d\n”, getpid());,这会打印出当前进程的 PID。此时只有一个进程在运行,即主进程。
fork的调用与进程复制:当执行fork()时,操作系统会创建一个与父进程几乎完全相同的子进程。这个复制过程包括复制父进程的整个地址空间,如代码段、数据段、堆和栈等。子进程会继承父进程打开的文件描述符、信号处理设置、当前工作目录等资源
fork后的执行
父进程:在父进程中,fork返回新创建子进程的 PID。然后继续执行printf(“i am a process, my pid is: %d\n”, getpid());,此时打印出的 PID 与fork之前打印的 PID 相同,因为这还是在父进程中。
子进程:在子进程中,fork返回 0。子进程从fork调用的下一条语句开始执行,也就是同样执行printf(“i am a process, my pid is: %d\n”, getpid());,但此时打印出的 PID 是子进程自己的 PID,与父进程的 PID 不同。由于父子进程是独立运行的,所以这两个printf语句的执行顺序是不确定的,取决于操作系统的调度。在代码中添加sleep(1)是为了让子进程有足够的时间执行到这一步,以便更清晰地观察到两个进程的执行结果。


#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
int main()
{
        printf("父进程开始运行, pid:%d\n",getpid());
        pid_t id=fork();
        if(id<0)
        {
                perror("fork");
                return 1;
        }
        else if(id==0)
        {
                //child
                while(1)
                {
                        sleep(1);
                        printf("我是一个子进程,我的pid:%d,我的父进程id:%d\n",getpid(),getppid());
                }
        }
        else
        {
                //father
                while(1)
                {
                        sleep(1);
                        printf("我是一个父进程,我的pid:%d,我的父进程id:%d\n",getpid(),getppid());
                }
        }
        printf("进程开始运行, %d\n",getpid());
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
父进程的返回值是子进程的PID,子进程的返回值是0。

那么fork的两个返回值,给父进程返回子进程PID给子进程返回0的原因是什么?
父进程与子进程是一对多的关系,父进程:子进程=1:N。对于子进程来说它只有一个父进程,对于父进程来说他有多个父进程。为了管理这些进程,所有父进程返回子进程的PID具有唯一性。子进程要想找到父进程肯定更方便。

为什么fork会有两个返回值?
执行fork函数,会为子进程创建一个和父进程一样的PCB。但是他们所指向的代码都是一样的,所以他们的代码共享。也就是说父进程和子进程共享代码中return,都执行return。

为什么一个变量会有多个不同的值
比如上图中的id又是等于0又是大于0。其实进程之间是相互对立的,子进程挂了不会影响父进程,父进程挂了也不会影响子进程。虽然创建fork进程的时候,子进程会创建一个和父进程一样的PCB共享数据和代码,但是代码是只读的,也就是说互不影响。当想要对一个进程进行修改,各个进程都会拷贝一份,叫做写时拷贝。
所以不同进程执行代码不同,所获取的id就不同。


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

相关文章:

  • 【和春笋一起学C++】文本输入与读取(二)
  • 将txt转成excel正则化公式的调整
  • Java SpringBoot使用EasyExcel导入导出Excel文件
  • OpenCV轮廓相关操作API (C++)
  • 字玩FontPlayer开发笔记4 性能优化 首屏加载时间优化
  • 【简博士统计学习方法】3. 统计学习方法的三要素
  • 策略模式(Stragety Pattern)
  • PostgreSQL 运维的难与“难” --上海PG大会主题记录
  • Cloudflare IP 优选工具:轻松找到最快的 CDN 节点
  • Unity 3D游戏开发从入门进阶到高级
  • 【蓝桥真题练习】蓝桥杯 2021 国赛 A 组 E 题
  • 云商城--业务+架构学习和环境准备
  • php 多进程那点事,用 swoole 如何解决呢 ?
  • 【Cocos TypeScript 零基础 5.1】
  • win下搭建elk并集成springboot
  • 未来趋势:电商平台API接口的智能化与自动化发展
  • MYSQL----------MySQL权限管理
  • get和post有什么区别
  • 解决LNMP环境下PHP使用freetds扩展连接SqlServer数据库乱码问题
  • 现代谱估计的原理及MATLAB仿真(二)(AR模型法、MVDR法、MUSIC法)
  • 超市管理系统(javaweb+mysql+redis)
  • VSCode 使用鼠标滚轮控制字体
  • 【商城的功能开发】
  • Space Nation白皮书(区块链游戏)
  • 【iOS Swift Moya 最新请求网络框架封装通用】
  • centOS7