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

一世执手 提交于 2020-02-26 12:05:43

目录

一.冯诺依曼体系结构

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. 存储器:    主要指内存

1.2内存,硬盘区别

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

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

二、操作系统

2.1 概念 + 目的

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

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

目的:

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

2.2 如何管理

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

2.3系统调用和库函数

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

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

三.进程相关概念

3.1进程概念

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

3.2如何描述进程

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

 

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


3.2.1查看进程状态

使用:ps -ef      或者   ps -aux

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

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

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

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
     printf("pid: %d\n", getpid());
     printf("ppid: %d\n", getppid());
      return 0;
}

在创建进程时要调用

 #include <unistd.h>

 pid_t fork(void);

返回值:(仅仅被调用一次,却能够返回两次)

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

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

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状态的进程

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

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

下面我们进行修改:

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

进程状态转换图:

 

 

3.4僵尸进程

  • 僵死状态(Zombies)是一个比较特殊的状态。当进程退出并且父进程(使用wait()系统调用,后面讲)没有读取到子进程退出的返回代码时就会产生僵死(尸)进程。
  • 僵死进程会以终止状态保持在进程表中,并且会一直在等待父进程读取退出状态代码。
  • 所以,只要子进程退出,父进程还在运行,但父进程没有读取子进程状态,子进程进入Z状态

僵尸进程的危害:

无法在内核中释放PCB资源,换句话说,就是无法释放PCB当中所占的内存,会造成内存泄漏问题。

如何避免僵尸进程:

使用kill -9 命令干掉父进程(这样退出原因的保存已经毫无意义)

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

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

测试:

 #include <stdio.h>
 #include <unistd.h>
 
 int main()
 {
   fork();
   while(1)                                                                                                  
   {
     sleep(1);
   }
   return 0;
 }

运行:

查看父子进程:

杀死父进程,使子进程成为孤儿进程

程序停止,子进程被一号Init进程领养。

 

四.环境变量

4.1概念

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

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

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

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

4.3环境变量相关操作命令

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

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

4.4环境变量的组织方式

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

4.5获取环境变量

通过代码获取

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

#include <stdio.h>
//参数介绍: 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获取

#include <stdio.h>
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 <stdio.h>
#include <stdlib.h>
int main()
{
    printf("%s\n", getenv("PATH"));
    return 0;
}

注意: 环境变量具有全局属性

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

 

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

下面是一个例子

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

 

六.进程优先级

进程优先级:cpu资源分配的先后顺序

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值会影响到进程的优先级变化。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!