Linux-3-进程创建与进程状态

前言

Vue框架:Vue驾校-从项目学Vue-1
算法系列博客友链:神机百炼

查看进程

法一:aux指令

  • 查看单一进程指令:

    ps aux | head -1 && ps aux | grep 进程名
    
  • 进程PCB信息:
    进程信息
    关注点:PID + STAT + COMMAND

法二:proc文件夹

  • process文件夹:

    ls /proc/
    
  • 查看所有进程:
    所有进程

  • 以上所有蓝色数字就是PID,查看单一进程更多信息:

    ls /proc/对应进程的pid 				    //大致信息
    ls /proc/对应进程的pid -al				//详细信息
    
  • 单一进程大致信息:
    proc进程大致信息

  • 单一进程详细信息:
    proc进程详细信息

回顾权限:

  • PS:linux文件类型

    符号含义作用
    -普通文件
    d目录文件
    c字符设备文件外设驱动
    b块设备文件外设驱动
    l符号链接文件快捷方式—指向另一文件/文件夹
    s套接字文件
    p管道文件
  • PS:权限:

    linux文件的用户分三类:owner编写者 + group同组员 + others陌生人

    linux文件的权限有三种:r读取 , w写入 , x运行 , -无权限

    三位二进制权限:rw-为110 ,r–为100

    对于d文件夹:ls为r ,touch为w ,cd为x

  • PS:用户权限更改:

    chmod u/g/o  +/-  rwx 文件名
    	  用户    增删 权限
    
  • PS:粘滞位更改删除权限:

    chmod +t 文件名				//设置粘滞位t防止同group删除文件
    
  • PS:权限掩码umask

    假设二进制权限为110 110 110

    权限掩码umask为 001 001 001

    最终权限为:umask取反后 与 初始权限 ,即还是110 110 110

创建进程:

  • 先回顾一下什么叫创建进程:

    1. 程序文件和 相关数据 ,从硬盘转移到内存
    2. 内存中创建一个PCB结构体,加入PCB双链表同时链接上对应程序+数据
    3. PCB等待CPU调度

运行程序创建进程:

  • 初学进程时的概念就是程序运行起来就是进程

    ./文件名.文件后缀
    

系统调用接口创建进程:

fork()调用实例:

  • 创建进程的系统调用接口

    #include <unistd.h>
    	pid_t fork(void);
    //fork()有两个返回值:一会输出看看
    //为父进程返回子进程的pid
    //为子进程返回0
    
  • 在代码中创建子进程,同时查看其pid & ppid:
    fork创建进程

  • gcc 编译+ ./运行:
    查看fork进程ppd

  • 结论:

    1. 想要创建子进程,父进程必须在运行

    2. fork之前的代码,只有父进程执行

      fork之后的代码,父子进程都在执行

    3. fork之后的代码,父子进程谁先被执行取决于进程调度算法,先后顺序不定

    4. 所有进程由父进程创建,可以构成一颗多叉树

  • 硬件层面:子进程共享父进程数据和代码
    子进程共享父进程数据和代码

利用fork()双返回值实现伪并行:

  • 代码:
    fork()代码

  • 结论:fork()有两次/两个返回值
    结论
    第一次为父进程返回的是创建的子进程的pid

    第二次为子进程返回0

  • 应用:

    利用不同的返回值,让父子进程同时执行不同的代码,也就是if else可以同时执行了:

    #include <unistd.h>
    #include <stdio.h>
    int main(){
    	printf("I am running...\n");
        pid_t id = fork();
        if(id == 0){				//子进程任务
    		while(1){
    			printf("child process\n");
                sleep(1);
            }
        }else if(id > 0){			//父进程任务
    		while(1){
    			printf("parent process\n");
                sleep(2);
            }
        }else{						//fork调用失败时返回负数
    		while(1){
    			printf("failed process\n");
                sleep(1);
            }
        }
    }
    
  • 关于fork的软硬件细节,在《进程控制》中详细展开

exit(1)退出进程

  • 结束进程的系统调用接口:

    #include <stdlib.h>
    void exit(int status);					//status传递1即可
    
  • 使用:

    #include <stdio.h>
    #include <stdlib.h>
    #include <uinstd.h>
    int main(){
        pid_t id = fork();
        if(id == 0){
            for(int i=0; i<5; i++)
    			printf("这是子进程\n");
            printf("退出子进程\n");
            exit(1);						//进程退出
        }else if(id > 0){
    		while(1){
    			printf("这是父进程\n");
            }
        }
    	return 0;
    }
    

kill -9杀死进程

  • kill -9 进程和exit(1)不同,kill的进程属于非自然死亡,不经过下文的进程状态z

进程状态

  • 对于所有类型操作系统,宏观上的进程状态理解:
    宏观进程状态

  • linux将不同的进程状态通过宏定义,数字化表示

    且进程状态STAT信息存储在PCB/task_struct{}当中

    linux进程状态

R:

  • 含义:R状态虽然写作running,但是其实是“就绪”,并非“运行中”

    ​ 所以很多PCB进程块可以同时表示自己已经R,等待被CPU调度

  • 调度队列:

    1. 当一个进程准备就绪时,其PCB加入调度队列,CPU不必遍历所有PCB双链表来寻找可以调度的进程
    2. 当一个进程执行完毕或暂时不准备执行时,其PCB更改STATE,且退出调度队列

调度队列

  • 调度算法:父进程创建子进程后,执行顺序由调度队列的调度算法决定

    1. FIFO:先来先调度
    2. 优先级:下一篇展开
    3. ……

S:

  • 含义:进程在等待其他事件就绪,称为浅度睡眠该进程随时可以被唤醒也可以被杀掉

  • 出现情况:

    1. 主动休眠:sleep()
    2. 被动休眠:等待外设或其余数据准备就绪,才能开始执行,如printf()
  • 典型情况:
    sleep()主动休眠

  • 查看进程状态:
    S状态

D:

  • 含义:磁盘休眠状态 / 深度睡眠deep sleep

    区别于直接sleep,D状态的进程除非自己结束,谁也不能kill该进程

    设立D状态的目的同样也是为了等待其他事件的就绪或者说发生

  • 当PCB队列非常长的时,OS是可以kill掉S状态的进程的,但是不能kill掉D状态进程

T:

  • 含义:暂停进程

  • 测试用例:
    测试用例

  • 查看此时进程状态:s
    s

  • kill -l 查看Linux支持的所有信号集:
    kill -l信号集可以通过kill命令向目标进程发送上述64条命令(1~31条是重点)

  • 向进程发送STGSTOP信号:

    kill -STGSTOP 28427
    
  • 再看该进程的状态:T
    T暂停状态

  • 与休眠的区别:

    1. S/D状态在等,等其他条件具备来唤醒该进程
    2. T状态单纯自己暂停一会,由STG信号来暂停和开启
  • 结束暂停信号:开始继续运行

    kill -STGCONT 28427
    

Z&X:

  • Z zombie僵尸状态:

    进程退出后,OS还保留着他曾申请的资源,供其父进程/OS读取

    X dead死亡状态:

    进程退出后,OS读取完他曾申请的资源后(一定要有人读取完才能释放),释放这些曾用过的资源

  • 为什么要有僵尸状态:

    创建进程是为了解决问题,问题是否解决就看数据是否处理完成

    进程结束后,数据拿给父进程/OS检查一下,才能知道进程的任务完成的好不好

  • 典型的僵尸状态的应用:

    #include<stdio.h>
    int main(){
        //任务代码
    	return 0;
    }
    

    main函数的调用关系:

    1. OS调用加载器,
    2. 加载器调用mainCRCStartup()函数,
    3. mainCRCStartup()函数调用main()函数
    4. 如果任务代码没有出问题,那么虽然main进程结束,但是return 的 0就会被mainCRCStartup()接收
  • 退出码$:

    命令行中,最近一次进程退出时return的数据称为退出码:
    $接收return的退出码

echo $?                         //$即退出码,此处打印11
  • 进程退出的相关信息:

    进程退出后,保留的相关消息有很多,不只$退出码,当然这些都保存在了PCB结构体属性中

  • 查看/读取退出相关信息:

    等到《进程控制》博客,再来展开

  • 查看僵尸状态:

    1. 检测进程状态脚本:

      while :; do ps aux | head -1 && ps aux | grep myproc | grep -v grep;echo "##################################"; sleep 1; done
      
      //##################################是自己设置的行分割符
      
    2. 创建进程:myproc.c

      #include <stdio.h>
      #include <unistd.h>
      #inclued <stdlib.h>
      int main(){
          pid_t id = fork();
          if(id == 0){
      		for(int i=0; i<5; i++){
      			printf("I am child process. pid:%d ppid:%d %d\n", getpid(), getppid(), i);
                  sleep(1);
              }
              printf("child quit\n");
              exit(1);
          }else if(id > 0){
      		while(1){
      			printf("I am father. pid:%d ppid:%d\n", getpid(), getppid());
              	sleep(1);
              }
          }else {
      		printf("failed child process\n");
          }
      	return 0;
      }
      
    3. 查看进程状态:
      僵尸进程状态查询

孤儿进程:

  • 僵尸进程:

    子进程直接退出,进入Z状态,等待父进程读取其退出信息后,子进程正式死亡

  • 孤儿进程:

    父进程直接退出,父进程进入Z状态,等待父进程的父进程读取其信息,将其死亡

    此时子进程还在运行,进入了**“孤儿进程状态”**,父进程已经退出,那么谁来回收子退出时的退出信息呢?

    如果孤儿进程的僵尸资源没有人回收,那么造成了经典内存泄漏问题

  • 孤儿进程会被OS领养,其退出时的僵尸资源被OS读取和释放,让孤儿僵尸进程彻底死亡

  • 举例:父进程先退出:

    #include <stdio.h>
    #include <unistd.h>
    #inclued <stdlib.h>
    int main(){
        pid_t id = fork();
        if(id == 0){
    		while(1){
    			printf("I am child. pid:%d ppid:%d\n", getpid(), getppid());
            	sleep(1);
            }
        }else if(id > 0){
            for(int i=0; i<5; i++){
    			printf("I am father. pid:%d ppid:%d %d\n", getpid(), getppid(), i);
                sleep(1);
            }
            printf("father quit\n");
            exit(1);
        }else {
    		printf("failed child process\n");
        }
    	return 0;
    }
    
  • 脚本查看父子进程状态:

    while :; do ps axj | head -1 && ps axj | grep myproc | grep -v grep; echo "################################" ; sleep 1; done;
    
  • 查看孤儿进程状态:ppid==1
    孤儿进程的父进程是OS,ppid=1

进程状态切换:

  • 一图总结:
    进程状态切换
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

starnight531

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值