Linux:进程概念 | 进程状态


全文约 4967 字,阅读时长预计: 13分钟


进程

  • 什么是进程:

    • 程序的一个执行实例,正在执行的程序等。
    • 内核观点:担当分配系统资源(占用CPU时间,内存)的实体。
  • 白话:一个程序,在磁盘上放着就是一个普通的可执行二进制文件;加载到内存跑起来就是进程。

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

  • Linux操作系统下的PCB是: task_struct,一个结构体。所有运行在系统里的进程都以task_struct链表的形式存在OS操作系统内核里。

  • OS操作系统通过这样的结构体描述进程,再通过链表的方式组织起来,形成一个运行队列,CPU从运行队列里取指令干活…

    • CPU核心工作流程:取指令,解析指令,执行指令…

task_ struct内容分类

  • 标示符: 描述本进程的唯一标示符,用来区别其他进程。
  • 状态:任务状态,退出代码,退出信号等。   — 睡觉、吃饭、打游戏
  • 优先级:相对于其他进程的优先级。    – 排队拿到某种资源,排队吃饭
  • 程序计数器: 程序中即将被执行的下一条指令的地址。
  • 内存指针: 包括程序代码和进程相关数据的指针,还有和其他进程共享的内存块的指针.   – 指向程序的代码和数据(变量常量什么的)
  • 上下文数据: 进程执行时处理器的寄存器中的数据。   – 运行中存放在CPU中的临时数据
  • I/O状态信息: 包括显示的I/O请求,分配给进程的I/O设备和被进程使用的文件列表。
  • 记账信息: 可能包括处理器时间总和,使用的时钟数总和,时间限制,记账号等。  — 不同的年龄干不同的事情
  • 其他信息

PID

  • 查看进程属性:ps ajx | grep 进程名字
  • 加上属性列显示:ps axj | head -1 && ps ajx | grep 进程的PID
  • top:任务管理器
  • 获取进程的PID
int main()
{
    while(1)
    {
       printf("i am a process,my pid is:%d\n",getpid());
       printf("i am a process,my father is:%d\n",getppid());
       sleep(1);
    }
    return 0;
}
  • 由上面代码可知,bash是父进程,bash创建子进程去执行任务。
    • PID : 代表这个进程的代号
    • PPID :代表这个进程是由哪个进程发展衍生而来的,亦即父进程的代号

并发、并行、时间片

  • 时间片:进程在CPU上运行时,不是一直运行到程序结束,而是有个运行时间单位。
    • 进程让出CPU:1,运行队列里,来了一个优先级更高的;2,时间片到了
  • 并发:单核CPU,运行多个进程,是通过进程快速切换的方式,在一段时间内,让所有的程序都得到推进。
  • 并行:多核多CPU,任何时刻,允许多个进程同时执行。

上下文数据

  • 当一个进程在运行时,因为某些原因,需要被暂时停止执行,让出CPU,需要进程保存自己的所有临时数据。
    • 这个临时数据就是上下文数据,保存到PCB里的某部分。
    • 保存的目的是为了等会儿回复继续运行

Fork

  • 操作系统提供的创建子进程的接口。
int main()
{
    printf("i am a process,my pid is:%d\n", getpid());
    int id = fork();
    if (id == 0)
    {
        sleep(3);
        int a = 1 / 0;
    }
    else if (id > 0)
    {
        while (1)
        {
            sleep(2);
            printf("i amfather: im still alive\n");     进程独立性测试
        }
    }
  • 父子进程代码共享,数据各自开辟空间,私有一份(采用写时拷贝)。
    • 因为代码是只读的,不可修改或者写入
    • 数据各自一份,保证互不影响。就算子进程挂了,父进程还能跑。
    • OS中,所有进程,具有独立性。
  • fork两个返回值,:0,是子进程;大于0 ,就是父进程。
    • 会什么由两个返回值?
    • fork:一个函数,在返回PID之前;子进程已经创建出来了,代码共享;所有由两个返回值。
    • 为什么要返回值?:因为PID是给程序员用的…,便于使用;若不用,就 kill 命令 结束掉。
    • 那为什么子进程返回的是0,父进程fork返回的是子进程的PID?
    • 一个孩子找父亲是很简单的,而一个父亲,会有多个子女,需要给每个孩子进行标识。
  • fork 之后通常要用 if 进行分流,做不同的事情,可以创建多个子进程。
    • if else if 的语句都会被执行,cpu多执行流。
    • cpu切换时间纳秒级别,如播放时下载,下载时播放。

创建多个子进程:

int main()
{
	pid_ t ids[5] ;
	printf("I am father : %d\n", getpid());
	for(int i=0 ;i < 5; i++){
		ids[i] = fork();
		if(ids[i] == 0)
		{
			/ /chlild
			DoThing() ;
			exit(1) ;
		}
printf("%d, %d, %d, %d, %d\n",ids[0], ids[1], ids[2], ids[3], ids[4]) ;
getchar();
return 0;
}

Linux下的进程状态:

在这里插入图片描述

  • Linux内核源码下对状态的定义:
/*
* The task state array is a strange "bitmap" of
* reasons to sleep. Thus "running" is zero, and
* you can test for combinations of others with
* simple bit tests.
*/
static const char * const task_state_array[] = {
	"R (running)", /* 0 */
	"S (sleeping)", /* 1 */
	"D (disk sleep)", /* 2 */
	"T (stopped)", /* 4 */
	"t (tracing stop)", /* 8 */
	"X (dead)", /* 16 */
	"Z (zombie)", /* 32 */
};
  • R运行状态(running): 并不意味着进程一定在运行中,它表明进程要么是在运行中要么在运行队列里。
  • T停止状态(stopped): 可以通过发送 SIGSTOP 信号给进程来停止(T)进程。这个被暂停的进程可以通过发送 SIGCONT 信号让进程继续运行。如:kiil -19 PID
  • t (tracing stop):打断点调试时候的状态。
  • X死亡状态(dead):这个状态只是一个返回状态,你不会在任务列表里看到这个状态。一瞬间的事情。

浅度休眠、深度休眠

  • S睡眠状态(sleeping): 意味着进程在等待事件完成(这里的睡眠有时候也叫做可中断睡眠(interruptible sleep))。
    • 浅度休眠,随时可以接受外部的信号,处理外部的请求,对外部事件作出反应。
      在这里插入图片描述
  • D磁盘休眠状态(Disk sleep)有时候也叫不可中断睡眠状态(uninterruptible sleep),在这个状态的进程通常会等待IO的结束。
    • 因为外设磁盘的读取写入的速度很慢,需要等待磁盘的回应,写成功或失败;你操作系统也管不了我;
    • 如果正在写入过程中,被OS当成闲置资源终止掉(内存这么紧张,你怎么还不干活儿),那么可能导致数据丢失;场景:同一时间内,上百万用户正在注册,往硬盘里写数据时…
  • S:后台进程
  • S+:前台进程;一旦运行,我们的bash命令就无法进行解释;Ctrl C结束运行。

进程优先级

  • cpu资源分配的先后顺序,就是指进程的优先权(priority)。
    • 权限:决定了能不能拥有某种资源
    • 优先级:得到某种资源的先后次序。
    • 为什么有优先级?因为全球资源有限,human活着就是为了资源。
  • 优先权高的进程有优先执行权利。配置进程优先权对多任务环境的linux很有用,可以改善系统性能。
  • 还可以把进程运行到指定的CPU上,这样一来,把不重要的进程安排到某个CPU,可以大大改善系统整体性能。

  • ps -l
    在这里插入图片描述
  • PRI :代表这个进程可被执行的优先级,其值越小越早被执行
  • NI :代表这个进程的nice值

  • 优先级由 PRI 和 NI 决定:
    在这里插入图片描述
  • PRI值越小越快被执行,那么加入nice值后,将会使得PRI变为:PRI(new)=PRI(old)+nice
  • nice值,其表示进程可被执行的优先级的修正数值,【-20 ~ 19】。
  • 这样,当nice值为负值的时候,那么该程序将会优先级值将变小,即其优先级会变高,则其越快被执行所以,调整进程优先级,在Linux下,就是调整进程nice值

  • 用top命令更改已存在进程的nice:

    • top
    • 进入top后按“r”–>输入进程PID–>输入nice值
    • 每次输入计算的PRI(old)值都是不变的默认基准值。
  • 竞争性: 系统进程数目众多,而CPU资源只有少量,甚至1个,所以进程之间是具有竞争属性的。为了高效完成任务,更合理竞争相关资源,便具有了优先级。

  • 操作系统:公平调度,效率第二。


僵尸状态

  • 僵死状态(Zombies)是一个比较特殊的状态。当进程退出并且父进程(处在等待时期)没有读取到子进程退出的返回代码时就会产生僵死(尸)进程。
  • 僵死进程会以终止状态保持在进程表中,并且会一直在等待父进程读取退出状态代码。
  • 所以,只要子进程退出,父进程还在运行,但父进程没有读取子进程状态,子进程进入Z状态
  • 白话
    • 子进程的创建,是为了完成某项任务或者工作;

      进程退出,一般不是立马让OS释放进程的所有资源;而是在退出的时候,会自动将自己退出时的相关信息,写入进程的PDB中,供OS或者父进程读取。

      读取成功之后,才算真正的死亡。父进程通过调用接口让OS来回收。

      相关信息:子进程干的怎么样,完成任务了没有,遇到了什么bug等等。

---------------------------- 验证代码:
#include <stdio.h>
#include <stdlib.h>
int main()
{
	 pid_t id = fork();
	 if(id < 0)
	 {
	 perror("fork");
	 return 1;
 	 }
	 else if(id > 0){ //parent
		 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;
}
  • D、Z,状态下的进程都无法被KILL。

孤儿进程

  • 父进程先退出,子进程就称之为“孤儿进程”
  • 孤儿进程例子:
int main()
{
    printf("i am a process,my pid is:%d\n", getpid());
    int id = fork();
    if (id == 0)
    {
        sleep(3);
        int a = 1 / 0;  //除零错误,停止运行
    }
    else if (id > 0)
    {
        while (1)
        {
            sleep(10);
            printf("i amfather: im still alive\n");     
        }
    }
  • 孤儿进程被1号init进程领养,当然要由init进程回收喽。
  • 操作系统启动前有0号进,当OS完全启动以后,用户登录前,会被1号进程所取代。

自动化查看进程状态的脚本


while :; do ps axj | head -1 && ps axj | grep 进程名字; sleep 1; echo "---------------------"; done

G。

  • 资源有限,竞争者不计其数…
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值