Linux系统---初识进程

顾得泉:个人主页

个人专栏:《Linux操作系统》  《C/C++》  《LeedCode刷题》

键盘敲烂,年薪百万!


一、冯·诺依曼体系结构

1.基本概念       

       冯诺依曼体系结构,是由数学家冯·诺依曼提出的一种计算机制造理念,它是现代计算机设计的基础。这个理论包括了三个主要原则:一是采用二进制逻辑,二是将程序和数据一同存储并在运行时读取,三是计算机应由五个主要部分组成,包括运算器、控制器、存储器、输入设备以及输出设备。

       在这个结构中,冯·诺依曼首次提出了程序存储的概念,也就是说,程序会被编码并存储在存储器中,从而实现了计算机功能的可编程化。此外,这种设计也实现了硬件和程序设计的分离。这套理论的实现使得计算机的运行更为高效且灵活,对现代计算机的发展产生了深远影响。

       我们常见的计算机,如笔记本。我们不常见的计算机,如服务器,大部分都遵守冯诺依曼体系。截至目前,我们所认识的计算机,都是由一个个的硬件组件组成,如:

       输入设备:话筒,摄像头,键盘,鼠标,磁盘,网卡等

       中央处理器(CPU):含有运算器和控制器等输出单元:显示器,打印机等

       输出设备:声卡,显卡,网卡,磁盘,显示器,打印机等

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

       存储器指的是内存!!!

       不考虑缓存情况,这里的CPU能且只能对内存进行读写,不能访问外设(输入或输出设备)。

       外设(输入或输出设备)要输入或者输出数据,也只能写入内存或者从内存中读取。

       总之,所有设备都只能直接和内存打交道。

2.为什么要存在内存

       内存,它主要用于暂时存放处理器中的运算数据,以及与硬盘等外部存储器交换的数据。当一个程序开始运行时,它会被加载到内存中。因此,无论是正在执行的程序还是暂时不执行的程序,都需要在内存中有一个存储空间。

       此外,内存还扮演着作为CPU和外存之间的沟通桥梁的角色。因为CPU的计算速度非常快,而从外存(如硬盘或光盘)读取数据的速度相对较慢,直接从外存执行程序会导致CPU大部分时间都在等待数据读取,这会极大地降低计算机的整体效率。就如同我们经常说的木桶效应因此,当程序被执行时,它会首先被加载到内存中,这使得CPU可以直接访问数据,从而提高了处理速度。


二、操作系统(Operator System)

1.基本概念

       任何计算机系统都包含一个基本的程序集合,称为操作系统(OS)。

笼统的理解,操作系统包括:

       内核(进程管理,内存管理,文件管理,驱动管理)
       其他程序(例如函数库,shell程序等等)

2.设计OS的目的

        操作系统设计的主要目标是为了提高计算机的效率、方便性和可靠性。

       首先,通过进程管理,操作系统可以更好地进行资源分配和调度,使得CPU、内存等硬件资源得到更合理的利用,从而提高计算机的处理效率。其次,通过提供友好的用户界面,操作系统极大地方便了用户使用计算机,降低了学习难度和使用门槛。最后,通过错误检测和故障恢复,操作系统增强了计算机的可靠性,减少了系统崩溃的可能性。

       此外,操作系统还承担着协调和管理计算机系统中所有软硬件资源的职责,确保整个系统能够高效、稳定地运行。同时,它也是计算机与用户之间的桥梁,使用户可以更加方便、快捷地使用计算机来处理信息和完成各种任务。

3.定位

       在整个计算机软硬件架构中,操作系统的定位是:一款纯正的“搞管理”的软件

4.如何理解 “管理”

       操作系统的管理可以类比为我们日常生活中的学校管理系统

       首先,我们可以把计算机硬件想象成学校的各种设施和资源,例如教室、图书馆、体育设备等。这些硬件需要被合理地利用,因此需要一个"校长"来负责分配和调度这些资源,这个"校长"在操作系统中就是管理硬件的部分。

       其次,我们的日常行为和学习活动可以被比作是运行在计算机上的各种应用程序。在我们日常的学习生活中,比如申请奖学金、评奖评优等,是由学校的辅导员或者相关机构来进行管理和决策的。而在计算机中,这部分功能就由操作系统的用户程序管理部分来完成,它抽象出进程和线程的概念,进行线程调度,管理用户程序的内存。

       最后,操作系统还起到了一个中介的作用,隔离了应用程序和具体的硬件。就像我们在更换声卡时,只需要重新安装声卡驱动程序就可以了,无需更改应用程序。这是因为应用程序是运行在操作系统提供的虚拟环境中,而非直接与硬件交互。这样设计的好处是增加了系统的灵活性和可维护性,例如当我们需要更新或替换某个硬件时,只需操作对应的驱动程序,而无需修改运行在系统上的应用程序。

5.承上启下 

       那在还没有学习进程之前,就问大家,操作系统是怎么管理进行进程管理的呢?

       很简单,先把进程描述起来,再把进程组织起来!

       六字真言:先描述,再组织!


三、进程 

1.基本概念

       课本概念:程序的一个执行实例,正在执行的程序等

       内核观点:担当分配系统资源(CPU时间,内存)的实体

2.描述进程-PCB

       进程信息被放在一个叫做进程控制块的数据结构中,可以理解为进程属性的集合。

       课本上称之为PCB(process control block),Linux操作系统下的PCB是: task_struct

task_ struct内容分类

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

3.组织进程

       可以在内核源代码里找到它。所有运行在系统里的进程都以task_struct链表的形式存在内核里。

4.查看进程

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

       如:要获取PID为1的进程信息,你需要查看 /proc/1 这个文件夹。

5.系统调用获取进程标示符

               进程id(PID)           父进程id(PPID)

使用简单的代码进行验证:

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>

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

当前我所运行时的pid和ppid为(中间的过程简略): 

6.系统调用创建进程-fork初识

       运行 man fork 认识fork

       fork有两个返回值
       父子进程代码共享,数据各自开辟空间,私有一份(采用写时拷贝)

使用简单的代码进行验证:

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>

int main()
{
     int ret = fork();
     printf("hello proc : %d!, ret: %d\n", getpid(), ret);
     sleep(1);
     return 0;
}

       由结果可以看出,上述我们只打印了一遍,却输出了两份代码!

       这是因为:fork 之后通常要用 if 进行分流

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>

int main()
{
     int ret = fork();
     if(ret < 0)
    {
         perror("fork");
         return 1;
     }
     else if(ret == 0)
    { //child
         printf("I am child : %d!, ret: %d\n", getpid(), ret);
     }
    else
    { //father
         printf("I am father : %d!, ret: %d\n", getpid(), ret);
     }
     sleep(1);
     return 0;
}

7.进程状态

看看 Linux 内核源代码怎么说: 
       为了弄明白正在运行的进程是什么意思,我们需要知道进程的不同状态。一个进程可以有几个状态((在Linux内核里,进程有时候也叫做任务)。
下面的状态在kernel源代码里定义:

/*
* The task state array is a strange "bitmap" of
* reasons to sleep. Thus "running" is zero, and
* you can test for combinations of others with
* simple bit tests.
*/
static const char * const task_state_array[] = {
"R (running)", /* 0 */
"S (sleeping)", /* 1 */
"D (disk sleep)", /* 2 */
"T (stopped)", /* 4 */
"t (tracing stop)", /* 8 */
"X (dead)", /* 16 */
"Z (zombie)", /* 32 */
};

       R运行状态(running):并不意味着进程一定在运行中,它表明进程要么是在运行中要么在运行队列里

       S睡眠状态(sleeping):意味着进程在等待事件完成(这里的睡眠有时候也叫做可中断睡眠( interruptible sleep) ) 

       D磁盘休眠状态(Disk sleep)有时候也叫不可中断睡眠状态(uninterruptible sleep),在这个状态的进程通常会等待lO的结束

       T停止状态(stopped):可以通过发送SIGSTOP信号给进程来停止(T)进程,这个被暂停的进程可以通过发送SIGCONT信号让进程继续运行

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

8.进程状态查看

         ps aux / ps axj 命令


结语:Linux系统关于进程的基础分享到这里就结束了,没有进行展示的操作大家可以自行练习,希望本篇文章的分享会对大家的学习带来些许帮助,如果大家有什么问题,欢迎大家在评论区留言~~~ 

猜你喜欢

转载自blog.csdn.net/m0_71746526/article/details/134588343