当前位置: 首页 > article >正文

进程状态 | 僵尸进程 | 孤儿进程 | 前台后台进程 | 守护进程

文章目录

        • 1.进程的三种基本状态
        • 2.Linux中进程状态查看
          • 2.1.进程检测脚本
          • 2.2.各种状态查看
        • 3.孤儿进程
        • 4.前台、后台、守护进程

1.进程的三种基本状态

进程的在系统当中是走走停停的,「运行 - 暂停 - 运行」的活动规律;进程在活动期间的三种状态:运行状态、就绪状态、阻塞状态

在这里插入图片描述

  • 运行状态(Running):该时刻进程占用 CPU;
  • 就绪状态(Ready):可运行,由于其他进程处于运行状态而暂时停止运行;
  • 阻塞状态(Blocked):该进程正在等待某一事件发生(或等待非CPU资源,如等待输入/输出IO操作的完成)而暂时停止运行,这时,即使给它CPU控制权,它也无法运行;

PCB是描述进程信息的结构体,它是通过链表的方式组织的。如果处于就绪状态的进程链在一起的是运行队列;而处于阻塞状态的进程链在一起的是阻塞队列!在Linux中,就绪和运行态都是R状态。来看看kernel源代码对进程状态的定义:

/*
* 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 */
};
2.Linux中进程状态查看
2.1.进程检测脚本
while :; do ps axj | head -1 && ps axj | grep mytest;sleep 1;echo "------------------";done

更加干净!
while :; do ps axj | head -1 && ps axj | grep mytest | grep -v grep;sleep 1;echo "------------------";done
2.2.各种状态查看

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

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

int main()
{
    while(true)
    {
        printf("hello world\n");
        sleep(1);
    }
    return 0;
}

测试结果:R+后面的进程代表是一个前台进程

在这里插入图片描述

S睡眠状态(sleeping): 进程在等待事件完成(这里的睡眠有时候也叫做可中断睡眠(interruptible sleep)

#include<stdio.h>
int main()    
{       
    int a = 0;    
    printf("请输入一个整数:");                                            
    scanf("%d",&a);    
    
    return 0;    
} 

测试结果:

在这里插入图片描述

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

可以使用dd命令来,测试dd命令在处理数据时非常强大,但同时也具有一定的风险性。如果使用不当,可能会导致数据丢失或损坏(建议是不用测试这个状态)

T停止状态(stopped): 可以通过发送 SIGSTOP (kill -19 pid)信号给进程来停止(T)进程。这个被暂停的进程可以通过发送 SIGCONT(kill -18 pid) 信号让进程继续运行。**典型应用:调试!断点能停下来,是因为进程的T状态!**测试代码:

使用命令:kill -19 pid;将进程暂停

使用命令:kill -18 pid;将进程运行

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

int main()
{
    while(true)
    {
        printf("hello world\n");
        sleep(1);
    }
    return 0;
}

测试结果:

在这里插入图片描述

Z状态:僵尸(死)状态: 当子进程退出,但是父进程没调用waitwaitpid去获取子进程的状态信息,没有检测到子进程返回状态的代码时,子进程就会处于一个被检测的状态,就是僵尸状态。测试代码:

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

int main()
{                
    pid_t ret = fork();
    if(ret < 0)
    {
        perror("fork faile\n");
        return 1;
    }
    else if(ret == 0)
    {
        int cnt = 5;
        while(cnt)
        {
            printf("I am child pid = %d\n",getpid());
            cnt--;
            sleep(1);
        }
    }
    else    
    {      
        while(1)                                                               
        {    
            printf("I am father  pid = %d\n",getpid());    
            sleep(1);
        }    
    }    
    return 0;
}

测试结果:

在这里插入图片描述

僵尸进程的危害:子进程被创建出来,是为了完成父进程交给它的任务,父进程需要读取子进程的返回状态,但是父进程先退出,那么子进程就要一直维护Z状态;系统要一直维护子进程的PCB,那么就会造成内存泄漏。

父进程通过调用wait waitpid进程等待的方式解决僵尸进程问题。

3.孤儿进程

孤儿进程:父进程结束了,而它的一个或多个子进程还在运行,那么这些子进程就成为孤儿进程(father died)。子进程的资源由init进程(进程号PID = 1)回收。

测试代码:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main()
{
    pid_t id = fork();
    if (id < 0)
    {
        perror("fork");
        return 1;
    }
    else if (id == 0)
    {
        printf("I am child, pid : %d\n", getpid());
        sleep(10);
    }
    else
    { 
        printf("I am parent, pid: %d\n", getpid());
        sleep(3);
        exit(0);
    }
    return 0;
}

测试结果:

在这里插入图片描述

4.前台、后台、守护进程

前台进程通常是用户正在交互的进程,本质是谁占有键盘资源谁就是前台进程!

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

int main()
{
    while(true){
        printf("hello world\n");
        sleep(1);
    }
    return 0;
}

启动前台进程:./mytest 或输入指令 启动后台进程:./mytest &,从下图可以看到我启动一个前台进程之后,通过键盘输入的 ll cd指令都不管用了。

在这里插入图片描述

启动后台进程:我们程序也在显示器上打印,但是输入的 ll cd 指令可以执行。

在这里插入图片描述

前台进程可以使用信号的方式终止,将程序启动为后台进程发现 ctrl + c 发送信号不能终止进程!使用jobs查看系统运行的任务 使用 fg + 任务号将后台进程转成前台进程,再通过 ctrl + c终止进程!

[xiyan@hecs-34711 ~]$ jobs
[1]+  Running                 ./mytest &  (wd: ~/code/test)
[xiyan@hecs-34711 ~]$ fg 1

任务和进程的关系(一个任务,可以一个人完成,也可多个人完成!)

一个任务可以是一个进程执行也可以是多个进程执行,多个进程组成进程组以第一个进程的进程pid为任务号,一个进程可以自成进程组!通过查看PGID属性字段就能验证。

[xiyan@hecs-34711 test]$ sleep 1000 | sleep 2000 | sleep 3000 &
[1] 6114
[xiyan@hecs-34711 test]$ mytest >> log.txt &
[2] 6141
[xiyan@hecs-34711 test]$ jobs
[1]-  Running                 sleep 1000 | sleep 2000 | sleep 3000 &
[2]+  Running                 ./mytest >> log.txt &
[xiyan@hecs-34711 test]$ ps axj | head -1 && ps xj | grep 'sleep'
 PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
 4559  6112  6112  4559 pts/4     6267 S     1000   0:00 sleep 1000
 4559  6113  6112  4559 pts/4     6267 S     1000   0:00 sleep 2000
 4559  6114  6112  4559 pts/4     6267 S     1000   0:00 sleep 3000
[xiyan@hecs-34711 test]$ ps axj | head -1 && ps xj | grep 'mytest'
 PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
 4559  6141  6141  4559 pts/4     6285 S     1000   0:00 ./mytest

会话的概念

在这里插入图片描述

Linux操作系统中,当一个用户登录的时候会分配一个会话,一个会话期间可以创建多个进程组,退出登录,会话会销毁;会话中前台进程终止,后台进程可能不会终止,但是会受一定的影响。

一个会话有多个进程,但是前台进程只有一个,系统会让bash来,充当前台进程,如果启动一个前台进程,就会将bash替换,当我们自己启动的前台进程退出,又将bash换上来!

退出登录后再登录 , 我的Linux机器是直接就终止了我们程序!如何解决——进程守护化

[xiyan@hecs-34711 test]$ ./mytest >> log.txt &
[1] 12130
[xiyan@hecs-34711 test]$ jobs
[1]+  Running                 ./mytest >> log.txt &
[xiyan@hecs-34711 test]$ ps axj | head -1 && ps axj | grep 'mytest'
 PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
12035 12130 12130 12035 pts/3    12153 S     1000   0:00 ./mytest
12035 12154 12153 12035 pts/3    12153 R+    1000   0:00 grep --color=auto mytest
[xiyan@hecs-34711 ~]$ ps axj | head -1 && ps axj | grep 'mytest'
 PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
12181 12214 12213 12181 pts/3    12213 R+    1000   0:00 grep --color=auto mytest

什么是守护进程

将自成进程组自成会话的进程,称为守护进程,他的本质也是孤儿进程;

让会话1创建一个守护进程,当会话1退出守护进程不受影响!

在这里插入图片描述

[xiyan@hecs-34711 ~]$ man 2 setsid
[xiyan@hecs-34711 ~]$ man daemon	# 系统自带的方法

参考代码:

#pragma once

#include <iostream>
#include <cstdlib>
#include <unistd.h>
#include <signal.h>
#include <string>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

const std::string nullfile = "/dev/null";

void Daemon(const std::string &cwd = "")
{
    // 1. 忽略其他异常信号
    signal(SIGCLD, SIG_IGN);
    signal(SIGPIPE, SIG_IGN);
    signal(SIGSTOP, SIG_IGN);

    // 2. 将自己变成独立的会话
    if (fork() > 0)
        exit(0);
    setsid();

    // 3. 更改当前调用进程的工作目录
    if (!cwd.empty())
        chdir(cwd.c_str());

    // 4. 标准输入,标准输出,标准错误重定向至/dev/null
    int fd = open(nullfile.c_str(), O_RDWR);
    if(fd > 0)
    {
        dup2(fd, 0);
        dup2(fd, 1);
        dup2(fd, 2);
        close(fd);
    }
}

http://www.kler.cn/a/231370.html

相关文章:

  • 【Linux】基础02
  • 【c++丨STL】list模拟实现(附源码)
  • 海量数据面试题
  • 《译文》2024年11月数维杯国际大学生数学建模挑战赛题目
  • 大数据算法考试习题
  • 前后端交互之动态列
  • JavaEE作业-实验三
  • List与数组相互转换
  • 【react native】ScrollView的触摸事件与TouchableWithoutFeedback的点击事件冲突
  • 蓝桥杯基础知识8 list
  • Flutter组件 StatefulWidget、StatelessWidget 可继承写法
  • Docker 搭建mysql 集群(二)
  • C语言——oj刷题——实现字符串逆序
  • 波纹扩散效果
  • 阿里云游戏服务器租用价格表,2024最新报价
  • Linux下centos操作系统安装Mysql8.0过程及踩坑填补
  • 幻兽帕鲁服务器怎么搭建?palworld创建笔记(收藏备用)
  • nginx简单配置四种携带/时的拼接关系
  • 人工智能福利站,初识人工智能,图神经网络学习,第四课
  • Spring Cloud使用ZooKeeper作为注册中心的示例
  • postman执行批量测试
  • step导入到solidworks外观处理方法
  • 图数据库 之 Neo4j - 环境搭建(2)
  • Android Button background 失效
  • 最新GPT4.0使用教程,AI绘画,GPT语音对话使用,DALL-E3文生图
  • UniApp 快速上手与深度学习指南