Linux之进程

进程和程序,通俗理解是这样的:

我们写的代码没有运行的时候它就叫做程序,我们把它运行起来后就叫做进程。

也就是说,进程是程序的一个执行实例,正在执行的程序。如果站在操作系统的角度上看,进程就是担当分配系统资源(CPU时间、内存)的实体。

描述进程PCB

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

在书上或课本上我们称这种数据结构为PCB(process control block),但在Linux操作系统下的PCB是:task_struct

虽然名字不一样,但它也是PCB的一种,task_struct是Linux内核的一种数据结构,它会被装载到RAM(内存)里并且包含着进程的信息。

task_struct 内容分类

1.标识符:描述本进程的唯一标识符,用来区别其他进程。

2.状态:任务状态,退出代码,退出信号等。

3.优先级:相对于其他进程的优先级。

4.程序计数器:程序中即将被执行的下一条指令的地址。

5.内存指针:包括程序代码和进程相关数据的指针,还有其他进程共享的内存块指针。

6.上下文数据:进程执行时处理器的寄存器中的数据。

7.I/O状态信息:包括显示部分的I/O请求,分配给进程的I/O设备和被进程使用的文件列表。

8.记账信息:可能包括处理器时间总和,使用的时钟数总和,时间限制,记账号等。

9.其他信息。

Linux下如何查看进程:

进程的信息可以通过 /proc 系统文件查看。例如:

要获取PID(进程标识符)为1的进程信息,需要查看 /proc/1 这个文件夹,除此之外,也可以使用top和ps这些用户级工具来获取。

创建进程:

先认识一个系统调用:fork

我们通过man手册来了解一下这个系统调用

我们先写一段代码:

#include <unistd.h>
#include <stdio.h>

int main()
{
    int ret = fork();
    //getpid() 来获取进程的PID
    printf("hello proc:%d!,ret:%d\n",getpid(),ret);
    sleep(1);
    return 0;
}

运行上面的代码

我们发现fork有两个返回值,其实,一个是父进程,一个是子进程,父子进程共享一份代码,但父子进程会各自开辟一份空间存放数据。

所以我们在写代码的时候应该加上if判断

#include <unistd.h>
#include <stdio.h>

int main()
{
    int ret = fork();
    if(ret < 0){
        perror("fork error!");
        return 1;
    }
    else if(ret == 0){ //说明是子进程
        printf("i am child:%d!,ret:%d\n",getpid(),ret);
    }else{  //说明是父进程
        printf("i am father:%d!,ret:%d\n",getpid(),ret);
    }
    sleep(1);
    return 0;
}

这样我们就可以知道哪一个是父进程,哪一个是子进程了。

fork注意点:
    1.fork之后,父子进程交替运行。
    2.父进程死亡,子进程成为孤儿进程。
    3.如果子进程先死亡,子进程成为僵尸进程(僵尸进程有害)

进程状态:

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

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

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

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

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

僵尸进程:

僵尸状态是一个比较特殊的状态,当进程退出后并且父进程没有读取到子进程退出的返回代码就会产生僵尸进程。

僵尸进程会以终止状态保持在进程表中,并且一直在等待父进程读取退出状态代码。

所以,只要子进程退出,父进程还在运行,但父进程没有读取子进程的进程状态,子进程就会进入僵尸状态。

下面我们创建一个维持30秒的僵尸进程:

#include <stdio.h>
#include <stdlib.h>

int main()
{
    pid_t id = fork();
    if(id < 0){
        perror("fork error");
        return 1;
    }
    else if(id > 0){ //父进程
        printf("parent[%d] is sleeping ...\n",getpid());
        sleep(30);
    }else{        //子进程
        printf("child[%d] is begin Z...\n",getpid());
        sleep(5);
        exit(EXIT_SUCCESS);
    }
    return 0;
}

运行代码:

在Linux下,我们开启另外一个终端,来查看进程信息

产看命令:ps - ef | grep 可执行文件名

我们看见第二行的进程信息后面有<defunct> 说明该进程为僵尸进程。

僵尸进程的危害:
1.进程的退出状态必须被一直维持下去,因为它要告诉它的父进程,你交给我的任务我已经完成了,可如果父进程一直不读取,子进程就会变成僵尸进程。

2.维护退出状态本身就是要用数据来维护,也属于进程基本信息,所以保存在task_struct(PCB)中。也就是说,如果僵尸状态一直不退出,那么PCB就要一直维护下去。

3.如果一个父进程创建了很多子进程,可父进程就是不回收,就会造成资源浪费,因为数据结构对象本身就要占用内存,需要开辟空间。

严重点,会造成内存泄露的问题。

孤儿进程:

父进程如果提前退出,那么子进程退出后,进入了僵尸状态,这个该怎么办呢?
其实,我们把这种,父进程提前退出,子进程就称之为 孤儿进程 

孤儿进程会被1号信号Init进程回收

上面所说的,就是我们需要关注的几种进程状态,接下来,我们来看看进程的优先级

进程优先级的概念:

1.cpu资源分配的先后顺序,就是指进程的优先级。

2.优先权高的进程有优先分配的权利,配置进程优先权对多任务环境的Linux很有用,可以改善系统性能。

3.还可以把进程运行到指定的CPU上,这样,把不重要的进程安排到某个CPU,可以大大改善系统整体性能。

Linux下,我们用 ps-l 命令查看系统进程

注意几个重要的信息:

UID:代表执行者的身份。

PID:代表这个进程的代号。

PPID:代表这个进程是由哪个进程衍生出来的,就是父进程的代号。

PRI:代表这个进程可被执行的优先级,其值越小越早被执行。

NI:代表这个进程的nice值。

其中,PRI我们很好理解,它就是表明了进程的优先级,值越小,进程越早被执行。

NI,就是我们要说的nice值,其实它就是用来修改我们进程的优先级的,根据上面所说,PRI值越小越早被执行,如果我们加上nice值以后,PRI的值就会变化,变成这样:PRI(new) = PRI(old) + nice

这样,nice值为负值的时候,那么该程序的优先级值会变小,即优先级会变高,就会越快被执行。

所以,在Linux下,我们调整进程的优先级,就是调整nice的值,nice值的范围从-20 到 19 ,一共40个级别。

另外一些概念:

竞争性:系统进程数目众多,而CPU资源只有少量,甚至1个,所以进程间存在竞争属性的。为了高效的完成任务,更合理竞争相关资源,便具有了优先级。

独立性:多进程运行,需要独享各种资源,多进程运行期间互不干扰。

并行:多个进程在多个CPU下,同时进行运行,这称之为运行。

并发:多个进程在一个CPU下采用进程切换的方式,在一段时间之内,让多个进程都得以推进,称之为并发。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值