【Linux】进程概念(2)(进程 操作系统管理进程 PCB(struct task_struct) 查看进程 进程的常见调用 系统调用 man getpid/getppid man fork)

本文主要是介绍【Linux】进程概念(2)(进程 操作系统管理进程 PCB(struct task_struct) 查看进程 进程的常见调用 系统调用 man getpid/getppid man fork),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

  • 进程
    • 概念
  • 操作系统管理进程
    • PCB(struct task_struct)结构体
  • 查看进程
    • 另一种查看进程的方式:
  • 进程的常见调用
    • 系统调用
      • man getpid
      • man getppid
      • man fork


进程

概念

  • 课本概念:程序的一个执行实例,正在执行的程序等。
  • 内核观点:担当分配系统资源(CPU时间,内存)的实体。

一个运行起来(加载到内存)的程序 — 进程
在内存中的程序 — 进程
进程和程序相比,进程具有动态属性

操作系统管理进程

操作系统如何管理进程?

先描述再组织,要管理这些进程,操作系统首先将这些进程描述,所以就引出了一个叫做PCB的概念,

PCB(struct task_struct)结构体

在这里插入图片描述
操作系统为了管理进程,给PCB里面写上包含了进程的所有属性,理所当然,这里的属性有几百个,我们通过每个进程对应的ID来进行管理。进程控制块里面保存的是进程被创建时的所有属性,这些属性不在我们曾经的可执行程序里面,可执行程序里面只有我们的代码数据和逻辑。

struct task_struct
{//进程的所有属性//该进程对应的代码和属性地址struct task_struct *next;}

struct tack_struct *next包含了大堆的指针信息,对我们来讲,描述每一个进程都是一个task_struct

task_struct内容分类:

  • 标示符: 描述本进程的唯一标示符,用来区别其他进程。
  • 状态: 任务状态,退出代码,退出信号等。
  • 优先级: 相对于其他进程的优先级。
  • 程序计数器: 程序中即将被执行的下一条指令的地址。
  • 内存指针: 包括程序代码和进程相关数据的指针,还有和其他进程共享的内存块的指针
  • 上下文数据: 进程执行时处理器的寄存器中的数据[休学例子,要加图CPU,寄存器]。
  • I/O状态信息: 包括显示的I/O请求,分配给进程的I/O设备和被进程使用的文件列表。
  • 记账信息: 可能包括处理器时间总和,使用的时钟数总和,时间限制,记账号等。
  • 其他信息

每一个程序加载进内存,每一个进程对应的代码和数据在加载的时候,操作系统要进行操作,然后就初始化完了一个task_struct结构体,

在这里插入图片描述

其中每一个进程都要做这个工作,所以在系统里面,除了加载进程到内存里,操作系统给每一个可执行程序匹配一个进程控制块,说白了就是task_struct所对应的结构体对象或者变,然后它们每一个都指向自己的代码,到这里就完成了先描述,然后再组织,操作系统把所有的进程的PCB,结构体对象链接起来,拿着process_header这个头,CPU要调度某个进程,操作系统就遍历所有进程的PCB,然后找到优先级最高的进程,比如说process_header指向的那个,然后把代码交给CPU,CPU就可以执行对应进程的代码了,若以后有一个进程想退出了,我们只需要确定PCB属性里面有没有一个进程的状态是死亡的,死亡的话操作系统遍历链表,把状态是死亡的节点找到,然后把对应的代码和数据释放掉,然后把PCB释放掉,此时这个进程就会释放了。
在这里插入图片描述

最后我们有了先描述再组织这样的理念,我们得出一个结论:**所谓的对进程管理,变成了对进程对应的PCB进行相关的管理。对进程管理本质不是对进程的可执行程序管理,而是对应的PCB结构做管理。
对进程管理——转化成立对链表的增删查

struct task_struct是一个内核结构体,是操作系统给我们提供的用来描述进程的结构体,当加载进程的时候会创建对应的内核对象task_struct 对象(内核变量),并且将结构和我们的代码和数据关联起来从而完成先描述再组织的工作。

  • 进程 = 内核数据结构(task_struct)+ 进程对应的磁盘代码

查看进程

-rw-rw-r-- 1 test_106 test_106   76 Nov 11 17:09 Makefile
-rwxrwxr-x 1 test_106 test_106 8408 Nov 11 17:16 myproc
-rw-rw-r-- 1 test_106 test_106  135 Nov 11 17:16 myproc.c

进程在运行,本质是进程读取代码,依次做执行,进程在被调度运行的时候,进程就具有动态属性。

[test_106@VM-4-13-centos 20221111]$ man getpid
[test_106@VM-4-13-centos 20221111]$ ps ajx | head -1 && ps axj | grep 'myproc'

另一种查看进程的方式:

一个进程每一次被启动,就意味着每一次被重新加载到内存,每一次被重新加载到内存就意味这操作系统每一次都要给这个进程创建task_struct,重新分配PID,所以PID不断变化。
在Linux当中有一个目录结构:proc,它是一个内存级的目录
[test_106@VM-4-13-centos 20221111]$ ls /proc/
在这里插入图片描述

我们可以看到有一些目录是以数字开头,其实那些数字就是对应进程的PID
在这里插入图片描述
在Linux下,一个进程可以被当做文件来看待,最后我们可以通过proc这个目录来查到对应的属性信息。
[test_106@VM-4-13-centos 20221111]$ ls /proc/5660 -d
在这里插入图片描述
进程一退出,目录就自动删除了。

一个进程被加载到内存,这个进程已经跑起来了,现在把它在磁盘中的二进制程序删了,请问进程还能不能正常运行?
结果是还能。
在这里插入图片描述

进程的常见调用

系统调用

man getpid

在这里插入图片描述

返回值pid_t是系统给我们封装的类型,其实就是整数。提供的接口,谁调用就返回调它进程的PID。

  1 #include<stdio.h>2 #include<unistd.h>3 #include<sys/types.h>                                                                                                                             4 int main()5 {6   while(1)7   {8     printf("我是一个进程!,我的ID是:%d\n",getpid());9     sleep(1);10   }11   return 0;12 }

在这里插入图片描述
在这里插入图片描述
杀掉进程:kell -9 1193

man getppid

第一个p表示父,main getppid表示获取父进程ID,

  1 #include<stdio.h>2 #include<unistd.h>3 #include<sys/types.h>4 int main()5 {6   while(1)7   {8     printf("我是一个进程!,我的ID是:%d,父进程的pid:%d\n",getpid(),getppid());                                                 9     sleep(1);                                                                                      10   }                                                                                                11   return 0;                                                                                        12 }  

在这里插入图片描述
关掉进程,再次打开,我们发现子进程一直在变,父进程pid不变化,我们查看:
[test_106@VM-4-13-centos 20221111]$ ps ajx | grep 7651

在这里插入图片描述

我们发现7651进程的PID叫做bash
命令行上启动的进程,一般它的父进程没有特殊情况的话,都是bash!

man fork

  • 运行 man fork 认识fork
  • fork有两个返回值
  • 父子进程代码共享,数据各自开辟空间,私有一份(采用写时拷贝)
    在这里插入图片描述
    创建子进程:fork - create a child process fork 是一个函数.
    函数执行前:只有一个父进程,函数执行后:父进程+子进程
  1 #include<stdio.h>2 #include<unistd.h>3 #include<sys/types.h>4 int main()5 {6   fork();7   printf("我是一个进程,pid:%d,ppid:%d\n",getpid(),gitppid());8   sleep(2);                                                                                                                     14   return 0;15 }  

在这里插入图片描述

  1 #include<stdio.h>2 #include<unistd.h>3 #include<sys/types.h>4 int main()5 {6   pid_t id = fork();7   printf("我是一个进程,pid: %d ,ppid:%d,id: %d\n",getpid(),getppid(),id);                                                     8   sleep(2);                                                                                                                     14   return 0;                                                          15 } 

在这里插入图片描述
同一个变量id,在后续不会被修改的情况下,竟然有不同的内容!

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

在这里插入图片描述
创建出来了多进程,在C语言中if和else if不能同时条件成立,以前写的代码,两个死循环不能同时跑,证明fork之后会有父进程+子进程两个进程在执行后续代码,fork后续的代码,被父子进程共享。通过返回值不同,让父子进程执行后续共享的代码的一部分!

这篇关于【Linux】进程概念(2)(进程 操作系统管理进程 PCB(struct task_struct) 查看进程 进程的常见调用 系统调用 man getpid/getppid man fork)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/625750

相关文章

Linux内核参数配置与验证详细指南

《Linux内核参数配置与验证详细指南》在Linux系统运维和性能优化中,内核参数(sysctl)的配置至关重要,本文主要来聊聊如何配置与验证这些Linux内核参数,希望对大家有一定的帮助... 目录1. 引言2. 内核参数的作用3. 如何设置内核参数3.1 临时设置(重启失效)3.2 永久设置(重启仍生效

C++中初始化二维数组的几种常见方法

《C++中初始化二维数组的几种常见方法》本文详细介绍了在C++中初始化二维数组的不同方式,包括静态初始化、循环、全部为零、部分初始化、std::array和std::vector,以及std::vec... 目录1. 静态初始化2. 使用循环初始化3. 全部初始化为零4. 部分初始化5. 使用 std::a

python uv包管理小结

《pythonuv包管理小结》uv是一个高性能的Python包管理工具,它不仅能够高效地处理包管理和依赖解析,还提供了对Python版本管理的支持,本文主要介绍了pythonuv包管理小结,具有一... 目录安装 uv使用 uv 管理 python 版本安装指定版本的 Python查看已安装的 Python

kali linux 无法登录root的问题及解决方法

《kalilinux无法登录root的问题及解决方法》:本文主要介绍kalilinux无法登录root的问题及解决方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,... 目录kali linux 无法登录root1、问题描述1.1、本地登录root1.2、ssh远程登录root2、

前端下载文件时如何后端返回的文件流一些常见方法

《前端下载文件时如何后端返回的文件流一些常见方法》:本文主要介绍前端下载文件时如何后端返回的文件流一些常见方法,包括使用Blob和URL.createObjectURL创建下载链接,以及处理带有C... 目录1. 使用 Blob 和 URL.createObjectURL 创建下载链接例子:使用 Blob

C++ vector的常见用法超详细讲解

《C++vector的常见用法超详细讲解》:本文主要介绍C++vector的常见用法,包括C++中vector容器的定义、初始化方法、访问元素、常用函数及其时间复杂度,通过代码介绍的非常详细,... 目录1、vector的定义2、vector常用初始化方法1、使编程用花括号直接赋值2、使用圆括号赋值3、ve

Linux ls命令操作详解

《Linuxls命令操作详解》通过ls命令,我们可以查看指定目录下的文件和子目录,并结合不同的选项获取详细的文件信息,如权限、大小、修改时间等,:本文主要介绍Linuxls命令详解,需要的朋友可... 目录1. 命令简介2. 命令的基本语法和用法2.1 语法格式2.2 使用示例2.2.1 列出当前目录下的文

Python 迭代器和生成器概念及场景分析

《Python迭代器和生成器概念及场景分析》yield是Python中实现惰性计算和协程的核心工具,结合send()、throw()、close()等方法,能够构建高效、灵活的数据流和控制流模型,这... 目录迭代器的介绍自定义迭代器省略的迭代器生产器的介绍yield的普通用法yield的高级用法yidle

Pytest多环境切换的常见方法介绍

《Pytest多环境切换的常见方法介绍》Pytest作为自动化测试的主力框架,如何实现本地、测试、预发、生产环境的灵活切换,本文总结了通过pytest框架实现自由环境切换的几种方法,大家可以根据需要进... 目录1.pytest-base-url2.hooks函数3.yml和fixture结论你是否也遇到过

利用Python快速搭建Markdown笔记发布系统

《利用Python快速搭建Markdown笔记发布系统》这篇文章主要为大家详细介绍了使用Python生态的成熟工具,在30分钟内搭建一个支持Markdown渲染、分类标签、全文搜索的私有化知识发布系统... 目录引言:为什么要自建知识博客一、技术选型:极简主义开发栈二、系统架构设计三、核心代码实现(分步解析