要想学习Linux,这些基本知识你确定你都了解清楚了吗?

Posted 程序猿是小贺

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了要想学习Linux,这些基本知识你确定你都了解清楚了吗?相关的知识,希望对你有一定的参考价值。

大家好,我是小贺,我带着我的新文章又回来了,是的,你没看错,今天没有王大爷哈哈哈,那么之前向大家介绍了linux常见指令以及vim编辑器等等,那么今天我就再来带领大家一起了解一下Linux的进程篇,码字不易,大家要是觉得还不错的话还请三连一下哈,谢谢啦!

在这里插入图片描述

1.冯诺依曼体系

1.1冯诺依曼

冯·诺依曼(John von Neumann,1903年12月28日-1957年2月8日),美籍匈牙利数学家、计算机科学家、物理学家,是20世纪最重要的数学家之一。 冯·诺依曼是罗兰大学数学博士,是现代计算机、博弈论、核武器和生化武器等领域内的科学全才之一,被后人称为“现代计算机之父”、“博弈论之父”。
在这里插入图片描述

1.2冯诺依曼体系结构图

![在这里插入图片描述](https://img-blog.csdnimg.cn/20210608103937452.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NDk0MDQwNQ==,size_16,color_FFFFFF,t_7

1.3冯诺依曼思想

1.所有的数据都采取二进制存储(为了契合电路的特性,对应高低电平)
2.数据都被保存在存储器中(内存中)

常见的输入输出设备

输入单元:包括键盘, 鼠标,扫描仪, 写板等
中央处理器(CPU):含有运算器和控制器等
输出单元:显示器,打印机等
网卡:既是输入设备又是输出设备

1.4关于冯诺依曼,必须强调几点

  • 这里的存储器指的是内存
  • 不考虑缓存情况,这里的CPU能且只能对内存进行读写,不能访问外设(输入或输出设备)
  • 外设(输入或输出设备)要输入或者输出数据,也只能写入内存或者从内存中读取。
  • 一句话,所有设备都只能直接和内存打交道

2.操作系统

2.1操作系统概念

任何计算机系统都包含一个基本的程序集合,称为操作系统(OS)。笼统的理解,操作系统包括:
内核(进程管理,内存管理,文件管理,驱动管理)
其他程序(例如函数库,shell程序等等)
例如win,linux,unix,Mac OS等等操作系统都是由一堆代码构成的。

操作系统在做什么?

操作系统是管理计算机中软硬件资源软件,与硬件交互
为用户程序(应用程序)提供一个良好的执行环境
在整个计算机软硬件架构中,操作系统的定位是:一款纯正的“搞管理”的软件

操作系统是怎样完成这些事情的?

管理=描述+组织
描述过程,用struct结构体
组织过程,用链表或其他高效的数据结构

2.2系统调用和库函数

在开发角度,操作系统对外会表现为一个整体,但是会暴露自己的部分接口,供上层开发使用,这部分由操作系统提供的接口,叫做系统调用。
系统调用在使用上,功能比较基础,对用户的要求相对也比较高,所以,有心的开发者可以对部分系统调用进行适度封装,从而形成库,有了库,就很有利于更上层用户或者开发者进行二次开发.
系统调用接口:是操作系统提供给程序设计人员的一种服务。程序设计人员在编写程序时,可以利用系统调用来请求操作系统的服务。
目的:方便用户使用。
**库函数:**用来封装系统调用的函数,函数库是由系统建立的具有一定功能的函数的集合。

3.进程

3.1进程概念

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

特征

动态性:进程的实质是程序在多道程序系统中的一次执行过程,进程是动态产生,动态消亡的。
并发性:任何进程都可以同其他进程一起并发执行
独立性:进程是一个能独立运行的基本单位,同时也是系统分配资源和调度的独立单位;
异步性:由于进程间的相互制约,使进程具有执行的间断性,即进程按各自独立的、不可预知的速度向前推进
结构特征:进程由程序、数据和进程控制块三部分组成。
多个不同的进程可以包含相同的程序:一个程序在不同的数据集里就构成不同的进程,能得到不同的结果;但是执行过程中,程序不能发生改变。

进程和程序的区别?

程序是静态的,进程是动态的,程序是存储在某种介质上的二进制代码,进程对应了程序的执行过程,系统不需要为一个不执行的程序创建进程,一旦进程被创建,就处于不断变化的动态过程中,对应了一个不断变化的上下文环境。. 程序是永久的,进程是暂时存在的。. 程序的永久性是相对于进程而言的,只要不去删除它,它可以永久的存储在介质当中。

从操作系统内核角度看进程

描述:task_struct,Linux操作系统下的PCB(进程控制块)是: task_struct,task_struct是Linux内核的一种数据结构,它会被装载到RAM(内存)里并且包含着进程的信息。
组织:使用双向链表

task_ struct内容分类

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

3.2进程号PID

在当前操作系统中唯一标识一个进程。

ps aux:查看当前操作系统中的所有进程
在这里插入图片描述

ps aux | grep 文件名:查看某个进程

3.3进程状态

粗略划分

运行态:正在使用cpu资源进行运算的进程所持有的状态
就绪态:一切的准备资料都已准备就绪,等待操作系统分配cpu资源
阻塞态:等待某种资源到来之后才能进行运算
在这里插入图片描述

细微划分
n

R 运行状态

S 可中断睡眠状态
S后面的+表示前台进程,没有+表示后台进程,可以类比手机的应用比如你聊着微信你朋友叫你打游戏,你打开了王者荣耀,显示在当前主页面,此时王者荣耀就是前台进程,可你后台的微信还在运行,即为后台进程。
在这里插入图片描述

D 磁盘休眠状态,即不可中断睡眠状态,在这个状态的进程通常会等待IO的结束

T 暂停状态
ctrl z进入暂停状态,此时进程暂停但是并没有结束,要结束进程ctrl c。要恢复暂停之前的状态 fg暂停结束。
在这里插入图片描述

t 跟踪状态,当进程被gdb调试时,会产生t状态

X 死亡状态,这个状态只是一个返回状态,你不会在任务列表里看到这个状态

Z 僵尸状态(重要,在后面单独介绍)

内存指针

保存了程序地址空间的首地址
在这里插入图片描述

进程切换

进行进程切换就是从正在运行的进程中收回处理器,然后再使待运行进程来占用处理器。 这里所说的从某个进程收回处理器,实质上就是把进程存放在处理器的寄存器中的中间数据找个地方存起来,从而把处理器的寄存器腾出来让其他进程使用。

程序计数器

保存了进程即将要执行的下一条汇编指令的地址。
在这里插入图片描述在这里插入图片描述ni nice值,查看执行信息
上下文信息
保存了程序运行时的寄存器中的内容
在这里插入图片描述
I/O信息
/proc/进程号/
cd fd
在这里插入图片描述

3.4fork创建子进程(重点)

复刻(英语:fork,又作派生、分支)是UNIX或类UNIX中的分叉函数,fork函数将运行着的程序分成2个(几乎)完全一样的进程,每个进程都启动一个从代码的同一位置开始执行的线程。这两个进程中的线程继续执行,就像是两个用户同时启动了该应用程序的两个副本。
从一个软件包拷贝了一份源代码然后在其上进行独立的开发,创建不同的软件。这个术语不只意味着版本控制上的分支,同时也意味着开发者社区的分割,是一种形式的分裂。

fork函数介绍

pid_t fork( void);
fork系统调用用于创建一个新进程,称为子进程,它与进程(称为系统调用fork的进程)同时运行,此进程称为父进程。创建新的子进程后,两个进程将执行fork()系统调用之后的下一条指令。子进程使用相同的pc(程序计数器),相同的CPU寄存器,在父进程中使用的相同打开文件。
它不需要参数并返回一个整数值。下面是fork()返回的不同值。
负值:创建子进程失败。
零:返回到新创建的子进程PID。
正值:返回父进程PPID或调用者。该值包含新创建的子进程的进程ID
本质是在不同的进程各返回一次,一次父进程一次子进程总返回两次。
子进程拷贝父进程的PCB
在这里插入图片描述
要想父子进程执行不一样的代码,则需要使用返回值进行分支判断在这里插入图片描述
giepid()谁调用返回谁的进程号
gieppid()谁调用返回谁的父进程进程号
在这里插入图片描述
此时父进程的父进程是bash
在这里插入图片描述

思考问题

  1. 父进程与子进程哪个先运行?

取决于操作系统的调度,是抢占式执行的

  1. 子进程被创建出来之后,与父进程还有关系吗

没有关系

  1. 子进程被创建出来后,代码从哪里开始运行?

父进程调用fork之后,在父进程的PCB中,程序计数器:调用fork完毕之后的下一条汇编指令,上下文信息:调用fork完毕之后保存了程序运行时的寄存器中的内容。子进程执行的是从父进程拷贝过来的程序计数器:调用fork完毕之后的下一条汇编指令。

3.5僵尸进程模拟

1.在代码当中创建出来一个子进程
2.模拟让子进程先于父进程退出,通过fork函数的返回值,让父子进程走不同的分支逻辑
子进程直接退出
父进程不退出(while (1) )

 #include <stdio.h>                                                                          
#include <unistd.h>

int main()
{
    pid_t pid = fork();
     if(pid < 0)
     {
         printf("创建失败!\\n");
        return 0;
    }
    else if(pid == 0)
     {
        printf("我是子进程!pid:%d,我的父进程是%d\\n",getpid(),getppid());
     }
     else
     {
        while(1)
        {
             sleep(1);
            printf("我是父进程!pid:%d,我的父进程是%d\\n",getpid(),getppid());
         }
     }
     return 0;
 }

在这里插入图片描述在这里插入图片描述
僵尸进程的危害

kill [pid]
kil1 -9 [pid] :强杀命令
僵尸进程在内核当中的task_struct结构体没有被释放掉,导致内存泄露。并且使用强杀命令也不能够将僵尸进程结束掉。

解决方案

  1. 重启操作系统(不推荐)
  2. 将僵尸进程的父进程结束掉
  3. 进程等待(此处先不解释)

总结:

父进程创建出来一个子进程,子进程先于父进程退出。子进程在退出时会向父进程发送一个信号(SIGCHLD:标识子进程停止或结束的信号),而父进程对于该信号会忽略处理,导致子进程在退出时没有进程来回收子进程的资源(PCB),子进程就会变成僵尸进程。

孤儿进程模拟

父进程创建一个子进程,父进程先于子进程退出,子进程就是孤儿进程。在子进程代码当中写一个死循环,让其父进程直接退出。

#include <stdio.h>                                                                          
#include <unistd.h>

int main()
{
    pid_t pid = fork();
    if(pid < 0)
    {
        printf("创建失败!\\n");
         return 0;
    }
    else if(pid == 0)
     {
        while(1)
        {
            sleep(1);
            printf("我是子进程!pid:%d,我的父进程是%d\\n",getpid(),getppid());
         }
    }
    else
    {
         printf("我是父进程!pid:%d,我的父进程是%d\\n",getpid(),getppid());
     }
   return 0;
 }

在这里插入图片描述

父进程结束,孤儿进程会被1号init进程领养,此刻子进程会变成后台进程(init进程就好像是一个民政局,专门负责处理孤儿进程的善后工作。每当出现一个孤儿进程的时候,内核就把孤 儿进程的父进程设置为init,而init进程会循环地wait()它的已经退出的子进程。这样,当一个孤儿进程凄凉地结束了其生命周期的时候,init进程就会代表党和政府出面处理它的一切善后工作。)

注明:转载请提示出处

以上就是本篇文章的重点,今天就到此结束了哈,有不同的观点或者有不同思路的朋友欢迎大家赏光私我哈,本着相互进步的原则,希望大家能多多向我提意见,谢谢~~

在这里插入图片描述

以上是关于要想学习Linux,这些基本知识你确定你都了解清楚了吗?的主要内容,如果未能解决你的问题,请参考以下文章

关于Linux系统,这些知识你都了解吗?

这些计算机领域的重要定律你都清楚吗?

Python 零基础入门必看,这些知识点你都掌握了吗?

浏览器缓存的这些知识点你都清楚吗?

C语言知识这些知识你都不知道?难怪说你学不懂C语言

作为一个Android程序员,关于音视频开发,这些你确定这些你都懂了吗?