Linux学习记录——구 进程概念的基础理解

news2024/9/26 5:22:01

文章目录

  • 一、操作系统概念理解
  • 二、进程的基本理解
    • 1、什么是进程?
    • 2、进程的属性
      • 1、指令查看进程
      • 2、目录查看进程
    • 3、进程与进程之间
      • 1、父子进程概念
      • 2、创建子进程---fork的基础使用方法
      • 3、fork原理的初级理解
        • 1、fork的操作
        • 2、fork如何看待代码和数据
        • 3、fork如何看待两个返回值问题


一、操作系统概念理解

接着上一篇。

上一篇写的总结一下。

冯-诺依曼结构在外设和CPU之间放了一个内存作为中间物,以此来提高整体运行的速度。除此之外,内存还有别的用处,即使没有调用数据的要求,外设也可以先把数据放到内存中进行预加载,来减轻CPU负担。当然,如果不使用,内存也可以释放数据,还给外设。

了解这些后,接下来就是操作系统。这时应有一个细致的提问

为什么要有操作系统?

上一篇讲到计算机要处理数据,需要一定的规则。这也就是操作系统对于数据的安排原理:先描述,再组织。操作系统会把所有数据构建成对象,填充值,并以某种数据结构来管理起来,比如链表,之后再以结构建模,对于数据的管理就变成了对结构的管理,最后给用户更好的服务。

只有硬件无法起作用,所以操作系统就是为了计算机给人服务而出现的。上面所说的到冯-诺依曼结构也体现了操作系统的作用,内存如何知道数据应当还到哪里?外设怎么确定把数据给内存是为了预加载?这里还有更多细致的问题,这些问题也都可以去找操作系统来解答。

了解了后操作系统,操作系统可不了解我们。虽然它给人提供服务,但不相信人类。操作系统为了自己的隐私,做了很多的保险,比如给用户提供接口来使用功能,也就是系统调用,不给用户开权限看系统的代码等等,在这基础之上,操作系统也衍生出了更多功能,以往所写的C语言代码是跨平台的,如果是使用系统接口来实现代码的,也就只能在这个系统上才能运行。

像平常所使用的ls指令,查看文件,文件的各项属性此时已经在磁盘里存储着,使用ls后,系统就会使用相应的系统接口来传达指令,到达磁盘部分,磁盘再往上返回用户要求的数据。

系统是一个体系,在这个体系下,每个细节都有规则。整个结构像一个层状结构,必须一层一层走,不能跳过某一环节。

在这里插入图片描述

总结一下,

用结构体来描述数据,用链表或者其它数据结构来组织数据。

操作系统给到用户的接口,也就是系统调用,为了方便,把某些相关联的接口封装起来,形成库。计算机语言有自己的库和编译器,说明语言也是在系统规则之上建立起来的,语言也有系统接口,用这些接口来调用系统的功能。有的库文件由系统接口,有的则无,只要用到系统功能那就会封装进系统接口。库文件之所以封装接口也是为了程序员更方便地使用功能。

二、进程的基本理解

1、什么是进程?

在系统形成一个可执行文件时,或者我们touch了一个文件,文件都会有内容+属性。文件被加载进内存时,它的代码和数据都会一并加入,系统以此来运行它们。但是这样还不算进程。之前提到过,系统对于数据的管理方式是先描述再组织,进入内存加载的这些文件又是如何管理的?文件在被加载进内存时,操作系统内核就会创建一个数据结构,叫做pcb,在Linux中则是task_struct。pcb会把进程的属性拿过来储存,并有指针指向这个已经存在的进程。更多的程序进入内存后,系统会创建更多pcb,一一对应着存入进程的属性;为了更好地管理,每个pcb之间也有指针相连,这样就形成了一整个链表。用户想要结束某一进程时,系统就遍历链表,找到对应的pcb,释放掉即可;用户想要执行某一个程序时,就把对应pcb的属性以及程序的代码和数据拿到cpu里运算。这样,对于进程的管理就变成了对数据结构的管理,这也就是系统对进程建模的过程。

所以进程我们可以理解为内核关于进程的相关数据结构+进程的代码和数据。

task_struct是pcb的一种,Linux的task_struct是以双链表形式来管理进程的。pcb拿到的属性包括标示符,状态,优先级,程序计数器,内存指针,上下文数据,I / O 状态信息,记账信息等等。

2、进程的属性

1、指令查看进程

pcb里有进程的属性,这个属性和文件的属性有关系但不多。pcb是一个内核的数据结构,和磁盘内的文件没有太大的关系,但也需要知道执行的是哪个程序。

现在实际操作一下,做一个文件。

  1 #include <stdio.h>
  2 #include <unistd.h>                                                            
  3 
  4 int main()
  5 {
  6   while(1)
  7   {
  8     printf("hello world\n");
  9     sleep(1);
 10   }
 11   return 0;
 12 }

在这里插入图片描述

此时查看一下进程

 ps axj | grep myprocess

只查看myprocess这个程序的进程。

在这里插入图片描述

为了更好的观察,我们把进程第一行拿出来,看看都是什么信息,以及使用逻辑与,把进程信息也一并显示出来

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

在这里插入图片描述

如果多次执行同样的程序,系统会开多个进程,这时候看进程属性会发现它们都不一样

在这里插入图片描述

grep那一行可以不用管,想去掉的话后面再加一个管道即可。

ps axj | head -1 && ps axj | grep myprocess | grep -v grep

2、目录查看进程

除了指令方式,还可以通过查看根目录的proc目录来查看当前进程

proc目录保存了进程的属性,proc是一个内存级的目录,只在程序运行时才会出现。

在这里插入图片描述

图中有很多蓝色数字的文件,这些数字代表存在的进程的PID,我们查看一下刚才两个进程中的其中一个。

 cd /proc/11644

里面就是这个进程的所有属性,比如这两个显而易见的

在这里插入图片描述

如果这时候结束掉这个进程,那么我们就无法查看这个文件了,或者说这个文件也不存在了,内容已经被删除了。

怎样查看PID

为了更方便的查看PID,而不是写一长串代码,我们可以在文件里打印出PID。getpid(),头文件是unistd.h 和 sys/types.h,用man指令查看即可。

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

效果就是这样

在这里插入图片描述

3、进程与进程之间

1、父子进程概念

进程与进程之间存在关系,比如常见的父子关系。getppid可以查看父进程的pid。添加进文件后,还是刚才的程序,无论怎样结束进程又重新执行,进程pid会变,但是父进程pid不变。

在这里插入图片描述

查看一下父进程

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

在这里插入图片描述

我们可以看到一个bush,也就是每次登录后形成的命令行解释器,这个本质上也是一个进程。对于用命令行启动的程序,都会成为一个进程,父进程都是bush。可是为什么父进程会是bash?bash要查看代码是否有错误,如果有错,那么bash就挂了,所以bash创建子进程来监管整个代码的运行,防止自己挂掉。

我们可以自行杀掉bash。除了Ctrl + c可以退出进程外,kill -9 进程pid就可以杀掉进程,不管是不是父进程。杀掉了bash,指令就无法正常使用了,这时候只能重启Xshell。

2、创建子进程—fork的基础使用方法

fork指令

fork可以用来创建子进程。

以这个代码为例

    printf("asssssssssssssssssssss\n");
    fork();
    printf("dddddddddddddddddddddd\n");
    sleep(1); 

打印出来的结果就是as打印了一次,d打印了两次。我们修改一下代码

printf("dddddddddddddddddddddd: pid : %d, 父进程pid: %d\n", getpid(), getppid());  

在这里插入图片描述

pid不一样,父进程pid也不一样,但是第二个ppid和第一个pid是一样的。所以这里就利用pid创建了一个子进程。

现在我们在as后面也打印pid。

在这里插入图片描述

这里as的父进程就是bash,d的父进程也是bash,不过后面又利用d创建了一个子进程。

既然能创建子进程,那么进程之间又如何控制呢?

fork函数里有这么一个描述。

在这里插入图片描述
成功创建父进程后,子进程的pid返回给父进程,0返回给子进程。如果失败,-1会返回给父进程。

我们先看代码。

在这里插入图片描述

在这里插入图片描述

我们可以看到19941是下面子进程的pid,子进程也得到了0.

不过呢一般我们不会这么用fork.

    pid_t ret = fork();
    assert(ret != -1);
    if(ret == 0)
    {
      //子进程
      while(1)
      {
        printf("子进程, pid : %d, ppid: %d\n", getpid(), getppid());
        sleep(1);
      }
    }
    else if(ret > 0)
    {
      //父进程
      while(1)
      {
        printf("父进程, pid : %d, ppid: %d\n", getpid(), getppid());
        sleep(1);
      }                                                                                                                                                           
    }

代码可以正常执行,即使有if和else if,说明这是两个执行流,都可以运行。

这里就体现出了fork的规则

1、fork之后,执行流会变成若干个2个执行流
2、fork之后,运行顺序由调度器决定
3、fork之后的代码共享,通常我们通过if和else if来进行执行流分流

我们会发现fork为什么可以有两个返回值,为什么if和else if都能执行?接下来我们要解决fork的一些疑问。

3、fork原理的初级理解

1、fork的操作

在写入代码和数据后,内核会创建pcb数据结构,这是一个父进程,fork会相应地建立一个子进程,父进程的大部分属性会拷贝到子进程,比如pid,ppid,父子进程就不一样,所以子进程的属性是以父进程属性为模板创建的。父进程指向对应的代码和数据,子进程也会指向它们。

2、fork如何看待代码和数据

进程与进程之间是相互独立运行的,父子进程也一样。上面的代码实际运行时,我们杀掉父进程,代码还会继续运行,子进程并不受影响。虽然事实是这样,但是仍然有问题。虽然进程互相独立,但是指向的代码和数据是一样的,这如何保证父子进程不受影响?

两个层面来看

代码:虽然指向同样的代码,但本质上程序员写出来的都是给父进程的代码,父进程分出一个子进程去读子进程自己的代码;况且代码有一个特性,只读。在运行过程中,代码是不会被改动的,除非退出程序,我们再去修改,所以代码方面两者不会受影响。

数据:还是上面那些代码,先建立一个变量,给上固定的值,然后父子进程两个while里的printf括号里都打印上变量的值和地址,在其中一个进程printf后给这个变量重新赋值,比如子进程里,最终的结果就是父子进程会打印出不同的数值,不过地址都一样,所以也可以发现数据不受影响,这是因为当有一个执行流尝试修改数据的时候,系统会自动给当前进程触发写时拷贝。写时拷贝先不管,之后再详细说,只知道系统做到了什么就可以了。

3、fork如何看待两个返回值问题

在函数内部准备执行return的时候,函数的主体功能已经完成。对于系统来说,fork本质上是一个函数,fork创建完子进程pcb后,就只剩了return了,但是return是一个语句,也就是一个代码,父进程执行完前面的也会来到这里,所以return是被父子进程各自调用了一遍,也就出现了2个返回值的情况。

在fork前面我们定义了一个变量来接收值,但是一个变量真的就出现了两个值?return的时候,把值传给了外面接收用的变量,由于这个变量是个父进程的变量,传过来的时候系统就触发了写时拷贝,虽然我们地址都一样,但是实际还是放到了不同的位置。

本篇只是简单写了一些fork的知识,fork还有更复杂的知识,以及对于Linux、进程概念的理解还有更深层次的,本篇只是一个基础知识。

结束。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/170455.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Docker概念和实践 2】虚拟机 ubuntu18上安装docker

一、说明 已经安装了N遍Docker了&#xff0c;逐步成了一套习惯&#xff0c;这里专门记录之&#xff1b;总之&#xff0c;安装前必须回答得问题是&#xff1a;何种操作系统、何种版本、是否虚拟机、云数据源等问题。一个环境如果装得好&#xff0c;就不需要重装&#xff0c;如果…

C语言刷题之摩尔投票法

目录 1.引入 2.摩尔投票算法 3.基本步骤 摩尔投票法分为两个阶段&#xff1a; 1.抵消阶段 2.检验阶段 4.代码实现 5.扩展沿伸 6.总结 1.引入 我们来看一个问题&#xff1a; 假设有一个无序数组长度为n&#xff0c;要求找出其中出现次数超过n/2的数&#xff0c;要求时间复…

vue3-环境搭建(docker版本)

序 大大小小项目经历无数&#xff0c;之前都是写的vue2的项目&#xff0c;因为项目需要&#xff0c;边学边用vue3&#xff0c;也算能转的开&#xff0c;但心里一直想系统的理顺一下vue3。 看了看极客时间&#xff0c;掘金小课&#xff0c;都没有能达到心里预期的“系统学习”…

免费内网穿透软件一步设置实现外网访问

在工作和生活中&#xff0c;有很多类似内网搭建服务器和外网连接内网的需求&#xff0c; 例如在任何地方都能访问自己家里的主机电脑笔记本上的应用&#xff0c;让出差外网和任何地方都能访问到公司内部局域网的服务器……这些需求我们可以统一用一个方案解决&#xff0c;那就是…

网络编程UDP+TCP

日升时奋斗&#xff0c;日落时自省 目录 1、网络编程基本概念 2、UDP数据报套接字编程 2.1、UDP相关API 2.1.1、DatagramSocket API 2.1.2、DatagramPacket API 2.2、UDP版本服务器 2.3、UDP版本客户端 2.4、UDP连接操作 2.5、翻译业务 2.6、总结 3、TCP流套接字编程 …

【项目实战】一文入门项目中Lombok的常用注解

一、Lombok介绍 1.1 Lombok是什么&#xff1f; 一个Java库&#xff0c;用于简化Java代码。 Lombok是一个非常神奇的 java 类库&#xff0c;会利用注解自动生成 java Bean 中烦人的 Getter、Setting&#xff0c;还能自动生成 logger、ToString、HashCode、Builder 等 java特色…

【GD32F427开发板试用】开发一款网络音乐播放器

本篇文章来自极术社区与兆易创新组织的GD32F427开发板评测活动&#xff0c;更多开发板试用活动请关注极术社区网站。作者&#xff1a;守勤 资源介绍 非常荣幸能够参与到这次GD32F427开发板试用的活动中来&#xff0c;开发板的设计非常简洁&#xff0c;板载了一颗GD32F103C8T6和…

Python中的递归及案例演示

目录 一.什么是递归 二.案例 递归找文件 步骤 os模块中的三个方法 演示 最终代码 三.总结 一.什么是递归 递归在编程中是一种非常重要的算法 递归:即方法(函数)自己调用自己的一种特殊编程写法 如&#xff1a; 函数调用自己&#xff0c;即称之为递归调用。 二.案例 递…

C++ 引用! 他是坤坤也是鸡哥

&#x1f451;专栏内容&#xff1a;C学习笔记⛪个人主页&#xff1a;子夜的星的主页&#x1f495;座右铭&#xff1a;日拱一卒&#xff0c;功不唐捐 目录一、前言二、引用1、引用的概念2、引用的声明3、引用的特性Ⅰ、 引用在定义时必须初始化Ⅱ、 一个变量可以有多个引用Ⅲ、引…

深度学习PyTorch 之 DNN-多分类

前面讲了深度学习&PyTorch 之 DNN-二分类&#xff0c;本节讲一下DNN多分类相关的内容&#xff0c;这里分三步进行演示 结构化数据 我们还是以iris数据集为例&#xff0c;因为这个与前面的流程完全一样&#xff0c;只有在模型定义时有些区别 损失函数不一样 二分类时用的损…

Pollard Rho算法

生日悖论 假设一年有nnn天&#xff0c;房间中有kkk人&#xff0c;每个人的生日在这nnn天中&#xff0c;服从均匀分布&#xff0c;两个人的生日相互独立 问至少要有多少人&#xff0c;才能使其中两个人生日相同的概率达到ppp 解&#xff1a;考虑k≤nk\le nk≤n 设kkk个人生日互…

Spring框架介绍及使用

文章目录1.概述1.1 Spring是什么1.2 Spring 的优势1.3 spring 的体系结构2. IoC 的概念和作用2.1 什么是程序的耦合2.2 IoC容器3. AOP的概念和作用超链接&#xff1a; Spring重点内容学习资料1.概述 1.1 Spring是什么 Spring 是分层的 Java SE/EE 应用 full-stack 轻量级开源…

使用docker-compose搭建Prometheus+Grafana监控系统

一、角色分配 Prometheus 采集数据Grafana 用于图表展示redis_exporter 用于收集redis的metricsnode-exporter 用于收集操作系统和硬件信息的metricscadvisor 用于收集docker的相关metrics 二、安装Docker 可以参考&#xff1a;https://ximeneschen.blog.csdn.net/article/d…

JVM调优实战:to-space exhausted Evacuation Failure

一次线上dubbo问题的定位&#xff0c;进行JVM调优实战。问题线上dubbo接口provider抛出异常&#xff1a;org.apache.dubbo.rpc.RpcException: Failfast invoke providers ... RandomLoadBalance select from all providers ... use dubbo version 2.7.16, but no luck to perfo…

vulnhub DC系列 DC-8

总结&#xff1a;exim4提权 目录 下载地址 漏洞分析 信息收集 网站爆破 后台webshell 提权 下载地址 DC-8.zip (Size: 379 MB)Download: http://www.five86.com/downloads/DC-8.zipDownload (Mirror): https://download.vulnhub.com/dc/DC-8.zip使用方法:解压后&#xff…

Cosmos 基础(二)-- Ignite CLI

官网 DOC GitHub 你的项目值得拥有自己的区块链。 Ignite使开发、增长和启动区块链项目比以往任何时候都更快。 Ignite CLI是一个一体化平台&#xff0c;可以在主权和安全的区块链上构建、启动和维护任何加密应用程序 Install Ignite 一、安装 你可以在基于web的Gitpod…

23种设计模式(七)——桥接模式【单一职责】

文章目录 意图什么时候使用桥接真实世界类比桥接模式的实现桥接模式的优缺点亦称:Bridge 意图 桥接模式是将抽象部分与实现部分分离,使它们都可以独立地变化。它是一种对象结构型模式,又称为柄体(Handle and Body)模式或接口(Interfce)模式。 什么时候使用桥接 1、如果一个…

详解MySQL数据库索引实现机制 - B树和B+树

详解MySQL数据库索引实现机制 - B树和B树1.索引的出现2.hash算法的缺点3.二叉排序树BST4.平衡二叉树AVL5.红黑树6.B树诞生了7.B树1.索引的出现 索引是一种用于快速查询和检索数据的数据结构&#xff0c;其本质可以看成是一种排序好的数据结构。 索引的作用就相当于书的目录。…

(Netty)Handler Pipeline

Handler & Pipeline ChannelHandler 用来处理 Channel 上的各种事件&#xff0c;分为入站、出站两种。所有 ChannelHandler 被连成一串&#xff0c;就是 Pipeline 入站处理器通常是 ChannelInboundHandlerAdapter 的子类&#xff0c;主要用来读取客户端数据&#xff0c;写…

【嵌入式处理器】CPU、MPU、MCU、DSP、SoC、SiP的联系与区别

1、CPU(Central Processing Unit) CPU(Central Processing Unit)&#xff0c;是一台计算机的运算核心和控制核心。CPU由运算器、控制器和寄存器及实现它们之间联系的数据、控制及状态的总线构成。众所周知的三级流水线&#xff1a;取址、译码、执行的对象就是CPU&#xff0c;差…