[Linux]---进程初阶(冯诺依曼体系结构/认识操作系统/进程相关概念/环境变量/程序地址空间 & 进程地址空间比较/进程优先级)

目录

一.冯诺依曼体系结构

1.1组成

1.2内存,硬盘区别

二、操作系统

2.1 概念 + 目的

2.2 如何管理

2.3系统调用和库函数

三.进程相关概念

3.1进程概念

3.2如何描述进程

3.2进程简单操作(查看 + 创建)

3.3 进程状态 + 演示

3.4僵尸进程

3.5孤儿进程(不是进程状态)

四.环境变量

4.1概念

4.2常见环境变量 + 查看方法

4.3环境变量相关操作命令

4.4环境变量的组织方式

4.5获取环境变量

五.程序地址空间 & 进程地址空间比较

六.进程优先级

6.1查看系统进程

6.2修改优先级


一.冯诺依曼体系结构

1.1组成

  1. 输入设备:采集数据    eg:键盘
  2. 输出设备:数据输出    eg:显示器
  3. 控制器:    控制存取数据
  4. 运算器:    控制器运算器合称为中央处理器:CPU
  5. 存储器:    主要指内存

[Linux]---进程初阶(冯诺依曼体系结构/认识操作系统/进程相关概念/环境变量/程序地址空间 & 进程地址空间比较/进程优先级)_第1张图片

1.2内存,硬盘区别

  • 内存是计算机的工作场所,硬盘用来存放暂时不用的信息
  • 内存是半导体制作,硬盘是磁性材料制作。
  • 内存存储介质属于易失性介质,从而断电数据会丢失 。但硬盘存储介质属于持久化介质,断电依旧保存数据。

硬件结构决定软件行为(数据流向):QQ聊天。   所有的硬件都是围绕内存工作的。

二、操作系统

2.1 概念 + 目的

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

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

目的:

  1. 对内:与硬件交互,管理所有的软硬件资源
  2. 对外:为用户程序(应用程序)提供一个良好的执行环境

2.2 如何管理

描述(用struct结构体)  +  后组织(用链表或其他高效的数据结构)

[Linux]---进程初阶(冯诺依曼体系结构/认识操作系统/进程相关概念/环境变量/程序地址空间 & 进程地址空间比较/进程优先级)_第2张图片

2.3系统调用和库函数

从开发角度,操作系统对外会表现为一个整体,但是会暴露自己的部分接口,供上层开发使用,这部分由操作系统提供的接口,叫做系统调用。系统调用在使用上,功能比较基础,对用户的要求相对也比较高,所以有心的开发者可以对部分系统调用进行适度封装,从而形成库,有了库,就很有利于更上层用户或者开发者进行二次开发。

关系:  库函数是对系统调用接口的封装。

三.进程相关概念

3.1进程概念

进程是程序运行起来的实体。或者说是   担当分配系统资源(CPU时间,内存)的实体。

实际上进程就是pcb,是操作系统对于运行中的一个程序的描述,通过这个描述实现对这个程序的运行调控。

3.2如何描述进程

进程信息被放在一个叫做进程控制块的数据结构中,可以理解为进程属性的集合。即 PCB(process control block),Linux操作系统下的PCB是: task_struct(通过双向链表连接

[Linux]---进程初阶(冯诺依曼体系结构/认识操作系统/进程相关概念/环境变量/程序地址空间 & 进程地址空间比较/进程优先级)_第3张图片

 

3.2进程简单操作(查看 + 创建)


3.2.1查看进程状态

使用:ps -ef      或者   ps -aux

[Linux]---进程初阶(冯诺依曼体系结构/认识操作系统/进程相关概念/环境变量/程序地址空间 & 进程地址空间比较/进程优先级)_第4张图片

[Linux]---进程初阶(冯诺依曼体系结构/认识操作系统/进程相关概念/环境变量/程序地址空间 & 进程地址空间比较/进程优先级)_第5张图片

当然在查看详细的某一个进程信息时可以和我们之前的某些命令一起使用 : 比如 grep 命令

[Linux]---进程初阶(冯诺依曼体系结构/认识操作系统/进程相关概念/环境变量/程序地址空间 & 进程地址空间比较/进程优先级)_第6张图片

3.2.2使用系统调用创建进程:

进程就是PCB,意味着创建一个进程就是创建一个PCB.

在创建进程之前我们先使用系统调用查看进程标识符 

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

在创建进程时要调用   父子进程:代码共享数据独有。

 #include

 pid_t fork(void);

返回值:(仅仅被调用一次,父子进程有各自的返回,并不是返回两次) 

  1. 在父进程中,fork返回新创建子进程的进程ID;
  2. 在子进程中,fork返回0;
  3. 如果出现错误,fork返回一个负值;
#include 
#include 
#include 
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;
}

fork函数特点: 创建出的 父子进程代码共享,数据各自开辟空间,私有一份(采用写时拷贝)

[Linux]---进程初阶(冯诺依曼体系结构/认识操作系统/进程相关概念/环境变量/程序地址空间 & 进程地址空间比较/进程优先级)_第7张图片

3.3 进程状态 + 演示

  • R运行状态(running): 并不意味着进程一定在运行中,它表明进程要么是在cpu上运行中,要么在运行就绪队列里。
  • S睡眠状态(sleeping): 意味着进程在等待事件完成(这里的睡眠有时候也叫做可中断睡眠(interruptible sleep))。如使用Ctrl + c
  • D磁盘休眠状态(Disk sleep)有时候也叫不可中断睡眠状态(uninterruptible sleep),在这个状态的进程通常会等待IO的结束
  • T停止状态(stopped): 可以通过发送 SIGSTOP 信号给进程来停止(T)进程。这个被暂停的进程可以通过发送SIGCONT 信号让进程继续运行。
  • t (tracing stop) :跟踪状态, 常用于gdb调试
  • X死亡状态(dead):这个状态只是一个返回状态,你不会在任务列表里看到这个状态。
  • Z (zombie)僵尸状态。

下面用一个例子来简单说明下这几种状态。

首先这是一个处于S状态的进程

[Linux]---进程初阶(冯诺依曼体系结构/认识操作系统/进程相关概念/环境变量/程序地址空间 & 进程地址空间比较/进程优先级)_第8张图片

为什么我们写了这个进程,让它运行起来之后查看仍然处于S+即处于可终端休眠的前台进程?

这是因为在上面的程序中我们使用了printf()函数来进行写入,既然有写入那就有等待,而I/O的速度远慢于CPU的处理速度所以状态看起来就是S+了。

下面我们进行修改:

[Linux]---进程初阶(冯诺依曼体系结构/认识操作系统/进程相关概念/环境变量/程序地址空间 & 进程地址空间比较/进程优先级)_第9张图片

注意: 单独的kill 命令 不能杀死处于T 停止状态的进程,kill -9 +进程号 可以强杀一切处于任何状态的进程

进程状态转换图:

[Linux]---进程初阶(冯诺依曼体系结构/认识操作系统/进程相关概念/环境变量/程序地址空间 & 进程地址空间比较/进程优先级)_第10张图片

 

 

3.4僵尸进程

  • 当进程退出并且父进程没有读取到子进程退出的返回信息时就会产生僵死(尸)进程。
  • 僵死进程会以终止状态保持在进程表中,并且会一直在等待父进程读取退出状态代码。
  • 所以,只要子进程退出,父进程还在运行,但父进程没有读取子进程状态,子进程就会进入Z状态。

[Linux]---进程初阶(冯诺依曼体系结构/认识操作系统/进程相关概念/环境变量/程序地址空间 & 进程地址空间比较/进程优先级)_第11张图片

僵尸进程的危害:

一个用户所能创建的进程数量有限,资源没有完全回收就会占用系统资源。无法在内核中释放PCB资源,换句话说,就是无法释放PCB当中所占的内存,会造成内存泄漏问题。

处理方法:

我们可以用到进程等待来解决这个问题。后面会讲。

如何避免,消除僵尸进程:

使用kill -9 命令干掉父进程(父进程退出,子进程保存退出原因就已经毫无意义,因此也会被操作系统释放)。

3.5孤儿进程 + 守护进程(不是进程状态)

父进程先退出,子进程就称之为“孤儿进程”。
孤儿进程被1号init进程领养,并且由一号init进程回收当前孤儿进程的状态信息进而内核就释放了孤儿进程的PCB块。

测试:

 #include 
 #include 
 
 int main()
 {
   fork();
   while(1)                                                                                                  
   {
     sleep(1);
   }
   return 0;
 }

运行:

查看父子进程:

杀死父进程,使子进程成为孤儿进程,这个孤儿进程的父进程会成为1号进程,并且这个孤儿进程运行在后台,孤儿进程是不会成为僵尸进程的,因为一号进程随时关注子进程退出。

程序停止,子进程被一号Init进程领养进行处理回收资源。

守护进程(精灵进程)是一种特殊的孤儿进程,父进程是一号进程,运行在后台,与终端以及登录会话脱离关系,不再受影响。守护进程一般通常是运行在后台的批处理程序(在默默地做一些循环往复的事情)。

四.环境变量

4.1概念

环境变量:配置系统运行环境参数的变量,如:我们在编写C/C++代码的时候,在链接的时候,从来不知道我们的所链接的动态静态库在哪里,但是照样可以链接成功,生成可执行程序,原因就是有相关环境变量帮助编译器进行查找。

4.2常见环境变量 + 查看方法

  • PATH : 指定命令的搜索路径 [重点]
  • HOME : 指定用户的主工作目录(即用户登陆到Linux系统中时,默认的目录)[重点]
  • SHELL : 当前Shell,它的值通常是/bin/bash
  • LD_ LIBRARY_PATH 动态库或者静态库的搜索路径

查看方法:echo $NAME   或者  env | grep NAME   //NAME:你的环境变量名称

[Linux]---进程初阶(冯诺依曼体系结构/认识操作系统/进程相关概念/环境变量/程序地址空间 & 进程地址空间比较/进程优先级)_第12张图片

4.3环境变量相关操作命令

  1. echo: 显示某个环境变量值
  2. export: 设置一个新的环境变量
  3. env: 显示所有环境变量
  4. unset: 清除环境变量
  5. set: 显示本地定义的shell变量和环境变量

环境变量存在的文件: ~/.bashrc   或者   ~/.bash_profile

4.4环境变量的组织方式

[Linux]---进程初阶(冯诺依曼体系结构/认识操作系统/进程相关概念/环境变量/程序地址空间 & 进程地址空间比较/进程优先级)_第13张图片

每个程序都会收到一张环境表,环境表是一个字符指针数组,每个指针指向一个以’\0’结尾的环境字符串。

4.5获取环境变量

通过代码获取

<1>.通过main函数的第三个参数 char* env[]

#include 
//参数介绍: argc:命令行参数的个数  argv:指针数组每个元素指向命令行参数内容
//env: 指针数组  每个元素指向代表的是某个环境变量的值
int main(int argc, char *argv[], char *env[])
{
    int i = 0;
    for(; env[i]; i++){
    printf("%s\n", env[i]);
}
    return 0;
}

<2>.通过第三方变量libc库中的全局变量environ获取    extern关键字:声明一个外部变量,这个变量实际在其他地方定义了。

#include 
int main(int argc, char *argv[])
{
    extern char **environ;
    int i = 0;
    for(; environ[i]; i++){
        printf("%s\n", environ[i]);
    }
    return 0;
}

注意:libc中定义的全局变量environ指向环境变量表,environ没有包含在任何头文件中,所以在使用时 要用extern声明。

<3>.通过系统调用获取或设置环境变量 (常用于访问具体的某一个环境变量)

#include 
#include 
int main()
{
    printf("%s\n", getenv("PATH"));
    return 0;
}

注意: 环境变量具有全局属性  环境变量也具有继承性:子进程用于父进程的环境变量

[Linux]---进程初阶(冯诺依曼体系结构/认识操作系统/进程相关概念/环境变量/程序地址空间 & 进程地址空间比较/进程优先级)_第14张图片

刚开始直接执行发现没有任何输出,但当用export设置后,执行就会输出。

 

五.程序地址空间 & 进程地址空间比较

地址?程序地址空间?

地址:对内存单元的编号。

程序是不占内存的,只有运行起来的程序被加载到内存,才会占用内存。

[Linux]---进程初阶(冯诺依曼体系结构/认识操作系统/进程相关概念/环境变量/程序地址空间 & 进程地址空间比较/进程优先级)_第15张图片

下面是一个例子

[Linux]---进程初阶(冯诺依曼体系结构/认识操作系统/进程相关概念/环境变量/程序地址空间 & 进程地址空间比较/进程优先级)_第16张图片

上面这个例子我们发现在未修改之前g_val值为100,在子进程中修改之后值为10 父进程未做修改值还是100.但父进程和子进程中g_val的地址竟然一样,那这是为什么?


虚拟地址空间就是一个结构体mm_struct-->是一个对内存的描述-->通过这个描述向进程虚拟出一个完整,连续,线性的内存空间。

为啥会有这个空间?为了不让进程不直接访问物理内存。

进程直接访问物理内存后果:

  1. 进程中的代码数据都是连续的地址,如直接使用连续的物理内存会造成内存的浪费。
  2. 直接访问物理内存会因为缺乏内存访问控制导致进程的不安全。

[Linux]---进程初阶(冯诺依曼体系结构/认识操作系统/进程相关概念/环境变量/程序地址空间 & 进程地址空间比较/进程优先级)_第17张图片

进程使用虚拟地址空间,通过页表映射物理内存,可以实现进程中数据再物理内存上离散式存储。通过这种方式提供内存利用率。在页表中可以直接针对某地址设置,这个地址的访问权限--》这个地址是只读的--》通过这种方式实现内存访问控制。 

六.进程优先级

进程优先级:CPU资源分配的先后顺序  或者  说是一个进程获取CPU的优先权顺序。

为什么会有优先级:

  1. 交互式进程:直接与用户进行交互的进程---》要求最好能够更加优先被CPU处理。
  2. 批处理进程:在后台默默做循环往复工作的进程

优先级作用:让操作系统运行更加良好。

 

6.1查看系统进程

使用   ps -l 

  • UID : 代表执行者的身份
  • PID : 代表这个进程的代号
  • PPID :代表这个进程是由哪个进程发展衍生而来的,亦即父进程的代号
  • PRI :代表这个进程可被执行的优先级,其值越小越早被执行 
  • NI :代表这个进程的nice值

6.2修改优先级

使用:进入top后按“r”–>  输入进程PID  –>  输入nice值

PRI值越小越快被执行,那么加入nice值(nice值了,其表示进程可被执行的优先级的修正数值)后,将会使得PRI变为:PRI(new)=PRI(old)+nice这样,当nice值为负值的时候,那么该程序将会优先级值将变小,即其优先级会变高,则其越快被执行所以,调整进程优先级,在Linux下,就是调整进程nice值nice其取值范围是-20至19,一共40个级别。

注意:进程的nice值不是进程的优先级,他们不是一个概念,但是进程nice值会影响到进程的优先级变化。

你可能感兴趣的:(Linux)