Linux多任务-进程篇

一.进程

        进程:正在执行的程序实例,需要消耗内存和CPU,是系统进行资源调度和资源分配的独立单位。(系统资源分配的最小单位是进程,而 CPU 调度(执行)的最小单位是线程)。

进程是由多道程序的并发执行而引出的,它和程序是两个截然不同的概念。程序是静态的,
进程是动态的,进程的基本特征是对比单个程序的顺序执行提出的。
1)动态性。进程是程序的一次执行,它有着创建、活动、暂停、终止等过程,具有一定的
生命周期,是动态地产生、变化和消亡的。动态性是进程最基本的特征。
2)并发性。指多个进程同存于内存中,能在一段时间内同时运行。引入进程的目的就是使
进程能和其他进程并发执行。并发性是进程的重要特征,也是操作系统的重要特征。
3)独立性。指进程是一个能独立运行、独立获得资源和独立接受调度的基本单位。凡未建
立PCB的程序,都不能作为一个独立的单位参与运行。
4)异步性。由于进程的相互制约,使得进程按各自独立的、不可预知的速度向前推进。异
步性会导致执行结果的不可再现性,为此在操作系统中必须配置相应的进程同步机制。

进程和程序的区别:

1.程序是一组数据指令集合,存储在硬盘空间,是静态的;

2.进程存储在内存空间,执行过程需要CPU参与(内存+CPU);

3.进程具备动态生命周期,从启动到消亡;

4.程序运行起来产生一个进程,也可产生多个进程;

5.在一个进程中也可以执行多个程序。

二.进程的创建

在操作系统中,终端用户登录系统、作业调度、系统提供服务、用户程序的应用请求等都会引起进程的创建。操作系统创建一个新进程的过程如下(创建原语):

创建时的操作:

1)为新进程分配一个唯一的进程标识号,并申请一个空白PCB(PCB是有限的)。若PCB申
请失败,则创建失败。
2)为进程分配其运行所需的资源,如内存、文件、I/O设备和CPU时间等(在PCB中体现)。这些资源或从操作系统获得,或仅从其父进程获得。若资源不足(如内存),则并不是创建失败,而是处于创建态,等待内存资源。
3)初始化PCB,主要包括初始化标志信息、初始化CPU状态信息和初始化CPU控制信息,
以及设置进程的优先级等。
4)若进程就绪队列能够接纳新进程,则将新进程插入就绪队列,等待被调度运行。

./a.out ------>启动进程
进程在被创建的时候,操作系统会为该进程分配0-4G的虚拟内存空间。

三.进程的调度

CPU调度进程:

        宏观并行,微观串行(并发)

        并行:多个任务同时执行

调度算法:

1.时间片轮询(RR)算法

        时间片轮转(RR)调度算法主要适用于分时系统。这种算法最大的特点是公平,系统将所有的就绪进程按FCFS策略排成一个就绪队列,每隔一定的时间(如30ms)便产生一次时钟中断,激活调度程序进行调度,将CPU分配给就绪队列的队首进程,并令其执行一个时间片。在执行完个时间片后,即使进程并未运行完成,它也必须释放出(被剥夺)CPU给就绪队列的新队首进程,而被剥夺的进程返回到就绪队列的末尾重新排队,等候再次运行。

        在RR调度算法中,若一个时间片尚未用完而当前进程已运行完成,则调度程序会被立即激活;若一个时间片用完,则产生一个时钟中断,由时钟中断处理程序来激活调度程序。
        在RR调度算法中,时间片的大小对系统性能的影响很大。若时间片足够大,以至于所有进程都能在一个时间片内执行完毕,则时间片轮转调度算法就退化为先来先服务调度算法。若时间片很小,则CPU将在进程间过于频繁地切换,使CPU的开销增大,而真正用于运行用户进程的时间将减少。因此,时间片的大小应选择适当,时间片的长短通常由以下因素确定:系统的响应时间、就绪队列中的进程数目和系统的处理能力。

2.先来先服务(FIFS)算法

在作业调度中,FCFS 调度算法每次从后备作业队列中选择最先进入该队列的一个或几个作
业,将它们调入内存,分配必要的资源,创建进程并放入就绪队列。
在进程调度中,FCFS调度算法每次从就绪队列中选择最先进入该队列的进程,将CPU分配
给它,使之投入运行,直到运行完成或因某种原因此阻塞时才释放CPU。

3.优先级调度算法

在作业调度中,优先级调度算法每次从后备作业队列中选择优先级最高的一个或几个作业,将它们调入内存,分配必要的资源,创建进程并放入就绪队列。在进程调度中,优先级调度算法每次从就绪队列中选择优先级最高的进程,将CPU分配给它,使之投入运行。根据新的更高优先级进程能否抢占正在执行的进程,可将该调度算法分为如下两种:

1)非抢占式优先级调度算法。当一个进程正在CPU上运行时,即使有某个优先级更高的进程进入就绪队列,仍让正在运行的进程继续运行,直到由于其自身的原因此让出CPU时(任务完成或等待事件),才将CPU分配给就绪队列中优先级最高的进程。

2)抢占式优先级调度算法。当一个进程正在CPU上运行时,若有某个优先级更高的进程进入就绪队列,则立即暂停正在运行的进程,将CPU分配给优先级更高的进程。而根据进程创建后其优先级是否可以改变,可将进程优先级分为以下两种:

1)静态优先级。优先级是在创建进程时确定的,且在进程的整个运行期间保持不变。确定静态优先级的主要依据有进程类型、进程对资源的要求、用户要求。优点是简单易行,系统开销小;缺点是不够精确,可能出现优先级低的进程长期得不到调度的情况。

2)动态优先级。创建进程时先赋予进程一个优先级,但优先级会随进程的推进或等待时间
的增加而改变,以便获得更好的调度性能。例如,规定优先级随等待时间的增加而提高,
于是,对于优先级初值较低的进程,在等待足够长的时间后也可获得CPU。
一般来说,进程优先级的设置可以参照以下原则:

1)系统进程>用户进程。系统进程作为系统的管理者,理应拥有更高的优先级。

2)交互型进程>非交互型进程(或前台进程>后台进程)。大家平时在使用手机时,在前台运行的正在和你交互的进程应该更快速地响应你,因此自然需要被优先处理。
3)I/O型进程>计算型进程。所谓I/O型进程,是指那些会频繁使用I/O设备的进程,而计算型进程是那些频繁使用CPU的进程(很少使用I/O设备)。我们知道,I/O设备(如打印机)的处理速度要比CPU慢得多,因此若将I/O型进程的优先级设置得更高,就更有可能让I/O设备尽早开始工作,进而提升系统的整体效率。

4.短作业优先(SJF)调度算法

短作业(进程)优先调度算法是指对短作业(进程)优先调度的算法。短作业优先(SJF)
调度算法从后备队列中选择一个或几个估计运行时间最短的作业,将它们调入内存运行;短进程优先(SPF)调度算法从就绪队列中选择一个估计运行时间最短的进程,将CPU分配给它,使之立即执行,直到完成或发生某事件而阻塞时才释放CPU。
例如,考虑表2.2中给出的一组作业,若系统采用短作业优先调度算法,其平均等待时间、
平均周转时间和平均带权周转时间见表2.3。

SJF算法也存在不容忽视的缺点:
1)该算法对长作业不利,由表2.2和表2.3可知,SJF调度算法中长作业的周转时间会增加。更严重的是,若有一长作业进入系统的后备队列,由于调度程序总是优先调度那些(即
使是后进来的)短作业,将导致长作业长期不被调度,产生饥饿现象(注意区分死锁,后者是系统环形等待,前者是调度策略问题)。

2)该算法完全未考虑作业的紧迫程度,因此不能保证紧迫性作业会被及时处理。

3)由于作业的长短是根据用户所提供的估计执行时间而定的,而用户又可能有意或无意地缩短其作业的估计运行时间,致使该算法不一定能真正做到短作业优先调度。

        SPF 算法也可以是抢占式的(若未特别说明,则默认为非抢占式)。当一个新进程到达就绪队列时,若其估计执行时间比当前进程的剩余时间小,则立即暂停当前进程,将CPU分配给新进程。因此,抢占式SPF调度算法也称最短剩余时间优先调度算法。

四.进程的状态

普适进程状态(三种基本状态):

linux进程状态:

1.运行态(用户运行态、内核运行态)        R

        正在执行,且被CPU任务调度所执行的进程
2.就绪态        R

        正在执行,没有CPU任务调度执行的进程(只缺少cpu)
3.可唤醒等待态        S

        也称为睡眠态,阻塞等待资源的进程
4.不可唤醒等待态         D
        不想被CPU任务调度所打断的进程任务可以设置为不可唤醒等待态
5.暂停态        T

        被暂停执行的进程
6.僵尸态        Z

        进程执行结束,空间没有被回收
7.结束态        X        

        进程执行给束,空间被回收

五.进程的相关命令

1.ps -aux

查看操作系统所有进程的相关参数:PID(进程ID号)、创建者、状态、CPU占有率、内存占有率。

ps -aux | grep a.out(将ps -aux的输出作为grep的输入,查找与a.out相关的字符串)

| 管道:将前面的输出作为后面的输入

grep:字符串查找

2.ps -ef

查看操作系统所有进程的相关参数:PID(进程ID),PPID(父进程ID)

允许一个进程创建另一个进程,此时创建者称为父进程,被创建的进程称为子进程。子进程可以继承父进程所拥有的资源。当子进程终止时,应将其从父进程那里获得的资源还给父进程。

3.top

动态查看进程状态及CPU占有率,内存占有率

4.pstree

查看进程间的创建关系(族谱图)

pstree  -sp  进程ID

查看指定ID的进程的创建关系

5.kill

kill  -信号名称/信号ID  PID

给PID对应的进程发送一个信号

kill  -l        查看系统定义出来的信号

kill  -9  PID :结束一个进程

kill  -19  PID :停止一个进程

S+:处于可中断睡眠状态的前台进程

S:处于可中断睡眠状态的后台进程

T:处于暂停态的后台进程

6.fg  后台进程编号

将后台进程调到前台

jobs:查看当前后台进程编号

六.进程相关函数

  • fork()

子进程:操作系统通过拷贝完整父进程0-3G内存空间数据到子进程

内核空间拷贝部分内容:PCB块;程序执行的位置;文件管理;

                                        PID不拷贝,PPID不拷贝

注意:

1.进程公用3G-4G这1G的内核空间;

2.各自进程0-3G用户空间互相独立,互不干扰,不能互相通信;

3.进程间不能直接通信,需要借助内核空间(IPC机制)。

pid_t  fork(void)
功能:创建新的进程,新进程叫子进程,原来的进程为父进程。

参数:

返回值:

        成功:父进程中返回子进程的PID号

                   子进程中返回0;

        失败:-1,没有新进程被创建

getpid():获取当前进程自己的PID号

getppid():获取当前进程的父进程的PID号

进程的退出:

1.进程执行结束退出

2.主函数中遇到return退出

3.通过信号结束一个进程(kill  -9  进程ID)

4.exit,_exit,_Exit()结束进程(也可直接结束main函数)

  • exit()

void  exit(int status)

功能:退出一个进程

参数:

        status:进程结束时的状态

                0:正常退出

                !0:因为其他原因退出

void  _exit(int status);

void  _Exit(int status);

僵尸进程与孤儿进程:

僵尸进程:进程已退出,但其资源空间没有被其父进程回收。

孤儿进程:父进程先结束,其父进程创建的子进程成为孤儿进程,将会被系统进程所收养,结束后,由系统进行回收。(守护进程)

如何避免僵尸进程产生:

1.由其父进程对僵尸态进程进行回收:wait(),waitpid();

2.让进程成为孤儿进程,结束后,由操作系统回收。

  • wait()

#include<sys/wait.h>

#include<sys/types.h>

pid_t  wait (int * wstatus);

功能:回收僵尸态进程

参数:

        wstatus:接收子进程的exit()中的参数,判断子进程是如何死亡的,自杀还是他杀(指针指向的变量中的某一位接收)

返回值:

        成功:回收到的子进程ID号

        失败:-1

wait(NULL)

  pid_t pid = fork();
   if( pid >0 )
    {
        int wstate;
       pid_t wpid = wait(&wstate);
       if(-1 == wpid)
         {
             perror("wait error");
             exit(1);
         }
         printf("wpid = %d\n",wpid);
         if(WIFEXITED(wstate))    //为真说明进程正常结束,即主动调用exit
         {
            printf("son over normally,status = %d\n",WEXITSTATUS(wstate));
         }
         else if(WIFSIGNALED(wstate))    //为真说明被信号杀死
         {
             printf("son over by signal,num = %d\n",WTERMSIG(wstate));
         }
     }
  • waitpid()

pid_t waitpid(pid_t pid, int *status, int options);
功能:回收指定进程的资源
        和wait功能相似,比wait更灵活
参数:
pid:
        <-1回收指定进程组内的任意子进程 (-100.等待GID=100的进程组中的任意子进程)
        -1 回收任意子进程,组内外
        0 回收和当前调用waitpid一个组的所有子进程,组内
        >0回收指定ID的子进程


status :进程退出时候的状态,
        如果不关注退出状态用NULL;

options选项:
        0 表示回收过程会阻塞等待
        WNOHANG 表示非阻塞模式回收资源。
返回值:
        成功 返回接收资源的子进程pid
        失败 -1
        设定为非阻塞且没有回收到子进程返回0


waitpid如果以阻塞方式回收和wait使用方式一样
如果父进程有任务需要执行,则可以选择waitpid非阻塞方式回收,此时搭配轮询方式实现。

七.exec函数簇

作用:在一个进程里面执行外部的可执行程序(外部命令)

外部命令:bin下的,ls,cp,mv...

内部命令:cd

命令别名:ll(ls -l别名)

本质:exec将要执行的外部程序替换到了进程的文本区,后面的原代码不会被执行, 不会创建新进程

int  execl(const  char* path,const char *arg,......(可省略参数))

l:list列表,把参数列出来

参数:

        path:可执行程序所在路径

                    ”/bin/ls“

        arg:执行可执行程序时传递的参数

        NULL:参数结束标志

返回值:

        失败:-1

        成功不返回

int  execlp(const  char* path,const char *arg,......(可省略参数))

功能:执行PATH环境变量保存的路径中的可执行程序(/bin,/usr/bin)

参数:

        file:可执行文件的名称

        arg:执行可执行程序时,传递的参数

        NULL: 参数的结束标志

env查看系统定义出来的环境变量

int  execv(const  char* path, char *const argv[ ])

将参数用argv数组保存

int  execvp(const  char* path, char *const argv[ ])

结合execlp与execv,自动去/bin下找path,同时argv保存参数

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值