进程基本概念、进程地址空间

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/guorong520/article/details/79734928

强调内容今天来谈一谈进程的一些基本概念,认识一些进程状态,重新认识一下程序地址空间(进程地址空间),进程调度算法,环境变量等属性。

一、进程

1.什么是进程?
程序的一个执行实例,正在执行的程序,是一个担当分配系统资源的实体,系统资源包括CPU时间、内存等。
2.linux中的PCB
进程信息被放在一个叫做进程控制块(process control block)的数据结构中,存储进程的属性。
在Linux下,PCB被定义为task_struct,称为文件描述符,它储存在内存(RAM)中。
3.task_struct
https://github.com/CCTVYI/task_struct/tree/master/LICENSE.md/LICENSE.md
4.可以使用top和ps查看大多数进程信息。
注:
1)进程id(PID),获取getpid()
2)父进程id(PPID),获取getppid()
5.使用fork()
fork有两个返回值,父子进程代码共享,数据各自开辟空间,采用写时拷贝,私有一份。
这里写图片描述
这里写图片描述
先执行父进程,在执行子进程

6.进程状态
1)R(running):运行状态,要么在运行,要么在运行队列里。
2)S(sleeping):进程在等待事件完成,也叫做可中断睡眠。
3)D(disk sleep):磁盘休眠状态,也叫做不可中断睡眠,这个状态经常会等待IO的结束。
4)T(stopped):可以发送SIGSTOP信号使进程停止。这个暂停的状态可以发送SIGCONT使进程继续运行。
5)X(dead):只是一个返回状态,任务列表不会看到这个状态。
6)Z(zombie):僵尸状态

7.僵尸进程
1)概念:当进程退出并且父进程没有读取到子进程退出的返回代码时,就会产生僵尸进程。
僵尸进程会进入终止状态,一直等待父进程读取退出的返回代码。
所以只要进程退出,父进程还在运行,没有读取子进程的状态,子进程就进入僵尸状态。
2)一些说明:
a.子进程变为僵尸状态,它就需要一直被维持下去,因为父进程必须知道子进程的事办的怎么样了,办完了,还是没有办完,还是异常退出,只要父进程没有读取,子进程就一直处于僵尸进程。
b.僵尸状态也需要task_struct保存进程基本信息
c.当父进程一直不读取很多子进程,这个内存资源就会被占用,有可能会发生内存泄漏。
3)模拟僵尸进程

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

int main()
{
    int ret=fork();  //创建子进程
    if(ret<0){
        perror("fork");
        return 1;
    }
    else if(ret==0){  //child
    printf("child=%d  %d\n",getpid(),ret);
    sleep(5);
    exit(EXIT_SUCCESS);  //子进程五秒后正常退出
    }
    else 
    {
        printf("father=%d  %d\n",getpid(),ret);
        sleep(20);
    }
    return 0;
}

这里写图片描述
注:
A.查看子进程状态时,可以使用ps aux | grep a.out查看,是静态查看。
B.也可以动态查看,使用top,-d 后面可以接秒数,就是整个进程界面更新的秒数,默认是5秒。-p 指定某些个PID来进程查看监测而已。比如 top -d1 -p进程号
8.孤儿进程
1)如果父进程由于工作完成退出或终止,子进程却还在运行,那么子进程会一直处于僵死状态,内存资源无法回收,这时子进程称为孤儿进程,这时会有1号进程init进行处理。因此,孤儿进程并没有危害。

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main()
{
    int ret=fork();
    if(ret<0){
        perror("fork");
        return 1;
    }
    else if(ret==0){
        printf("pid=%d ret=%d\n",getpid(),ret);
        sleep(30);
    }
    else
    {
        printf("ppid=%d ret=%d\n",getpid(),ret);
        sleep(8);
        exit(EXIT_SUCCESS);
    }
    return 0;
}

二、进程优先级

1.概念:CPU资源分配的先后顺序,合理地配置进程优先权,可以提高系统的性能。
2.查看系统进程:
这里写图片描述
UID:执行者的身份。
PID:进程ID号。
PPID:这个进程由哪个进程衍生而来,即父进程。
PRI:进程执行的优先级,值越小越早被执行
NI:代表这个进程的nice值。进程可被执行的优先级的修正数值,RPI(new)=RPI(old)+nice,
所以当nice值为负数时,优先级会变高,所以调整nice值,就可以改变进程的优先级,nice的取值范围为-20到19,共40个级别。
3.独立性
多进程运行,需要独享各种资源,多进程运行期间互不干扰。
4.并行
多个进程在多个CPU下,同时运行。
5.并发
多个进程在一个CPU采用进程切换的方式,在一段时间内每个进程看起来同时推进(CPU运算速度太快)

三、进程调度算法

  • a.时间片轮转调度算法
    时间片轮转是一种最古老,最简单,最公平,适使用最广的算法,每个进程都被分配一个时间段,即该进程运行的时间。如果时间到,进程还没有被执行完,CPU将被切走去执行其他的进程,如果未到时间,进程却阻塞了,CPU会立即被切走,当一个进程的时间片走完,它就会被移到队列的末尾。
    b.先来先服务
    根据先后顺序执行进程,但是会产生饥饿现象,所以短作业不适合。
    c.优先级调度算法
    在进程队列中选择优先级最高的来执行。
    d.多级反馈队列
    将时间片轮转与优先级调度相结合,把进程按优先级分成不同的队列,可以兼顾长短作业,适用很多场景。
    e.高响应比优先调度算法。

四、环境变量

1.概念:指在操作系统中用来指定操作系统运行环境的一些参数,通常具有全局特性。
2.常见的环境变量
PATH:指定命令的搜索路径。
HOME:指定用户的主工作目录。
HISTSIZE:保存历史命令记录的条数。
SHELL:当前shell,它的值通常是/bin/bash
注:查看它,echo $NAME
3.相关命令
1)echo:显示某个环境变量的值
2)export:设置一个新的环境变量
3)env:显示所有的环境变量
4)unset:清除环境变量
5)set:显示本地定义的shell变量和环境变量

a.命令行第三个参数可以获取环境变量

五、进程地址空间

1.父子进程代码共享,数据各自开辟空间,采用写时拷贝私有一份

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int okc=0;
int main()
{
    int ret=fork();
    if(ret<0){
        perror("fork");
        return 1;
    }
    else if(ret==0){
        okc=100;
        printf("child:%d %d %p\n",getpid(),okc,&okc);//子进程先退出,修改了okc的值
    }
    else
    {
        sleep(3);
        printf("father:%d %d %p\n",getpid(),okc,&okc);
    }
    return 0;
}

这里写图片描述
通过程序验证,发现其父子进程虽然地址相同,但是地址中的内容却不相同,说明该地址绝对不是物理地址,在linux中,地址被称为虚拟地址,C中看到的都是虚拟地址,物理地址用户是看不到的,物理地址由操作系统统一管理。操作系统可以将虚拟地址转换为物理地址。运用虚拟地址内存,可以更好的管理内存空间,保护了内存,每个进程认为自己都独享物理内存。
2.运行程序
运行一个程序,需要先将程序加载到内存中,早起的内存分配机制,进程地址空间不隔离,由于程序都是直接访问物理内存,恶意程序就可以随意修改别的进程的代码,从而破坏其他程序。
所以把物理地址暴露给进程会带来很严重的问题。
3.虚拟地址空间
1)CPU启动保护模式后,程序运行在虚拟地址中。但并不是所有的“程序”都运行在物理地址的。内核在初始化页表之前并不使用虚拟地址,而是直接使用物理地址。
2)页表:其存放在物理地址空间中,系统为每个进程建立了一张页面映像表,进行地址映射。
这里写图片描述

展开阅读全文

没有更多推荐了,返回首页