Linux——进程的概念(万字总结)(2)

简介: Linux——进程的概念(万字总结)(2)

五、如何获取pid和ppid

我们想要获取到pid和ppid,就要用到系统调用接口:

pid_t  getpid( void ) --- 返回的是子进程ID

pid_t  getppid( void ) --- 返回的是父进程ID

我们可以通过 man指令对这些函数进行详细说明的查看

1ecd1b2606ed46e9956a89f231c9802c.png

1.getpid() --- 获取子进程(pid)

#include<stdio.h>
#include<unistd.h>
int main()
{
  printf("I am child pid: %d\n",getpid());
  return 0;
}

2.getppid() --- 获取父进程(ppid)

#include<stdio.h>
#include<unistd.h>
int main()
{
  printf("I am father pid: %d\n",getppid());
  return 0;
}

这里将两个代码整合到一起后,通过死循环不断的打印有父子进程的ID,并对进程进程检测,发现ps检测到的ID和系统接口获取到的ID是一样的。

1ecd1b2606ed46e9956a89f231c9802c.png

六、进程的创建 --- fork初识

1.四种主要事件会导致进程的创建

1.系统初始化

2.正在运行的程序执行了创建程序的系统调用

3.用户请求创建一个新进程

4.一个批处理作业的初始化

2.用户如何请求创建一个新进程

       通过fork函数来进行进程的创建,我们可以man fork查看相关的函数信息这是一个系统调用,它会创建一个与调用进程相同的副本。在调用fork之后,这两个进程(父进程和子进程)拥有相同的内存映射。

1ecd1b2606ed46e9956a89f231c9802c.png

请看下面这段代码,我们在执行循环前创建了一个子进程,会有什么样的效果呢?

#include<stdio.h>
#include<unistd.h>
int main()
{
  fork();//创建子进程
  while(1){
    printf("I am child pid:%d I am father ppid:%d\n",getpid(),getppid());
    sleep(1);
  }
  return 0;
}

1ecd1b2606ed46e9956a89f231c9802c.png

       从上述的结果可以看出,main函数的进程和fork创建的进程打印的结果是一样的,并且通过pid和ppid发现,fork的父进程就是main函数的进程,说明fork所创建出来的子进程和父进程在内存上映射。

3. 如何让父子进程各有所需

 以上创建的子进程所做的事和父进程是一样的,显然意义并不大,我们要能够让所创建的子进程做和父进程不一样的事,才是我们想要的。那如何实现呢?


首先,对于fork是有两个返回值的


       1、如果子进程创建成功,在父进程中返回子进程的PID,而在子进程中返回0。

       2、如果子进程创建失败,则在父进程中返回 -1。

#include <stdio.h>  
#include <unistd.h>     
int main()  
{
    int ret = fork();
    if(ret == 0){ //如果子进程创建成功,给子进程返回0
        while(1){                                                                
            printf("I am child!\n");
            sleep(1);
        }
    }
    else if(ret > 0){ //如果子进程创建成功,给父进程返回子进程的pid
        while(1){
        printf("I am father!\n");
        sleep(1);
        }
    }
    else{ //如果子进程创建失败,给父进程返回-1
        //fork error
    }                                                              
    return 0;                                                      
} 

1ecd1b2606ed46e9956a89f231c9802c.png

七、进程的状态

1.进程状态有哪些

CPU对进程处理,取决于进程当前进程所处的状态,CPU对于不同状态的进程会采取不同的措施。

1ecd1b2606ed46e9956a89f231c9802c.png

/*
* 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  */  //追踪状态,类似于vs下打断点后直接运行到断点处
"X (dead)",           /* 16 */  //死掉的进程
"Z (zombie)",         /* 32 */  //僵尸进程
};

2.进程状态的查看

ps axj / ps aux

1ecd1b2606ed46e9956a89f231c9802c.png

3.进程状态的分析

1.运行状态 --- R

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

1ecd1b2606ed46e9956a89f231c9802c.png

上图想表达的意思是,进程A处于运行中,在一段时间后,就会切换到进程B....,这个时间很快,CPU运行这些进程是采用了时间轮转调度算法。在时间片轮转调度算法中,系统根据先来先服务的原则,将所有的就绪进程排成一个就绪队列,并且每隔一段时间产生一次中断,激活系统中的进程调度程序,完成一次处理机调度,把处理机分配给就绪队列队首进程,让其执行指令。当时间片结束或进程执行结束,系统再次将cpu分配给队首进程。

2.浅度睡眠状态 --- S

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

#include <stdio.h>
#include <unuistd.h>
int main()
{
    printf("hello linux!\n");
    sleep(50);
    return 0;
}

1ecd1b2606ed46e9956a89f231c9802c.png

处于S状态的进程,是可以被立即终止的

3.深度睡眠状态 --- D

       D磁盘休眠状态(Disk sleep)有时候也叫不可中断睡眠状态(uninterruptible sleep),在这个状态的进程通常会等待IO的结束。例如,当进程要求对磁盘进行写入操作,那么在磁盘进行写入期间,该进程就处于深度睡眠状态,是不会被杀掉的,因为该进程需要等待磁盘的回复(是否写入成功)以做出相应的应答。(磁盘休眠状态)

4.停止状态 --- T

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

#include <stdio.h>
#include <unuistd.h>
int main()
{
    while(1)
    {
        printf("hello linux!\n");
        sleep(1);
    }
    return 0;
}

在上述程序跑起来之后,处于浅度睡眠状态,我们可以发送 SIGSTOP 信号给进程来停止(T)进程;

1ecd1b2606ed46e9956a89f231c9802c.png

我们发送 SIGSTOP 信号给进程来停止(T)进程后,还可以发送SIGCONT 信号让进程继续运行。

1ecd1b2606ed46e9956a89f231c9802c.png

查看kill相关信号:

kill -l

1ecd1b2606ed46e9956a89f231c9802c.png

以上的信号,可以查看教程后,尝试尝试

5.僵死状态 --- Z

  僵死状态(Zombies)是一个比较特殊的状态。当子进程退出并且父进程没有读取到子进程退出的返回代码时就会产生僵死(尸)进程,僵死进程会以终止状态保持在进程表中,并且会一直在等待父进程读取退出状态代码。所以,只要子进程退出,父进程还在运行,但父进程没有读取子进程状态,子进程进入Z状态;

6.死亡状态 --- X

X死亡状态(dead):死亡状态只是一个返回状态,当一个进程的退出信息被读取后,该进程所申请的资源就会立即被释放,该进程也就不存在了,所以你不会在任务列表当中看到死亡状态。

八、僵尸进程与孤儿进程

1.僵尸进程的危害

       有如下代码,我们执行之后,子进程会不断的打印数据,父进程等待子进程的过程中,我们立刻杀掉子进程,那么子进程就会处于僵尸状态,而此时程序还在执行,父进程在等待子进程退出的状态,我们把这种进程称之为僵尸进程。

#include <stdio.h>
#include <unistd.h>
int main()
{
   int ret = fork();
   if(ret == 0){//子进程一直打印
     while(1){
       printf("hello linux!\n");
       sleep(1);
     }
   }
   else{//父进程什么都不干,就睡觉
     sleep(100);                                              
   }
   return 0;
}

1ecd1b2606ed46e9956a89f231c9802c.png

僵尸进程的危害:


1.进程的退出状态必须被维持下去,因为他要告诉关心它的进程(父进程),你交给我的任务,我办的怎么样了。可父进程如果一直不读取,那子进程就一直处于Z状态?


       是的!


2.维护退出状态本身就是要用数据维护,也属于进程基本信息,所以保存task_struct(PCB)中,换句话说,Z状态一直不退出,PCB一直都要维护?

        是的!

3.那一个父进程创建了很多子进程,就是不回收,是不是就会造成内存资源的浪费?

        是的! 因为数据结构对象本身就要占用内存,想想C中定义一个结构体变量(对象),是要在内存的某个位置进行开辟空间!

4.内存泄漏?

        是的!

2.孤儿进程

       刚刚提到了僵尸进程是由于子进程先退出而父进程没有对子进程的退出信息进行读取;那么父进程先退出,子进程在进入僵尸状态后,其父进程未能对其做出处理,那么就称该进程是孤儿进程。若是一直不处理孤儿进程的退出信息,那么孤儿进程就会一直占用资源,此时就会造成内存泄漏。因此,当出现孤儿进程的时候,孤儿进程会被1号init进程领养,此后当孤儿进程进入僵尸状态时就由int进程进行处理回收。

#include <stdio.h>
#include <stdlib.h>                                                     
#include <unistd.h>
int main()
{
   pid_t ret = fork();
   if(ret == 0){//子进程不断的打印数据
     while(1){
       printf("I am child, running!\n");
       sleep(1);
     }
   }
   else{//父进程打印数据,休眠10秒后,直接退出
     printf("father do nothing!\n");
     sleep(10);
     exit(1);
   }
   return 0;
}

1ecd1b2606ed46e9956a89f231c9802c.png

目录
相关文章
|
2月前
|
资源调度 Linux 调度
Linux c/c++之进程基础
这篇文章主要介绍了Linux下C/C++进程的基本概念、组成、模式、运行和状态,以及如何使用系统调用创建和管理进程。
40 0
|
1天前
|
运维 监控 Linux
Linux操作系统的守护进程与服务管理深度剖析####
本文作为一篇技术性文章,旨在深入探讨Linux操作系统中守护进程与服务管理的机制、工具及实践策略。不同于传统的摘要概述,本文将以“守护进程的生命周期”为核心线索,串联起Linux服务管理的各个方面,从守护进程的定义与特性出发,逐步深入到Systemd的工作原理、服务单元文件编写、服务状态管理以及故障排查技巧,为读者呈现一幅Linux服务管理的全景图。 ####
|
26天前
|
缓存 监控 Linux
linux进程管理万字详解!!!
本文档介绍了Linux系统中进程管理、系统负载监控、内存监控和磁盘监控的基本概念和常用命令。主要内容包括: 1. **进程管理**: - **进程介绍**:程序与进程的关系、进程的生命周期、查看进程号和父进程号的方法。 - **进程监控命令**:`ps`、`pstree`、`pidof`、`top`、`htop`、`lsof`等命令的使用方法和案例。 - **进程管理命令**:控制信号、`kill`、`pkill`、`killall`、前台和后台运行、`screen`、`nohup`等命令的使用方法和案例。
99 4
linux进程管理万字详解!!!
|
17天前
|
存储 运维 监控
深入Linux基础:文件系统与进程管理详解
深入Linux基础:文件系统与进程管理详解
58 8
|
14天前
|
Linux
如何在 Linux 系统中查看进程占用的内存?
如何在 Linux 系统中查看进程占用的内存?
|
26天前
|
算法 Linux 定位技术
Linux内核中的进程调度算法解析####
【10月更文挑战第29天】 本文深入剖析了Linux操作系统的心脏——内核中至关重要的组成部分之一,即进程调度机制。不同于传统的摘要概述,我们将通过一段引人入胜的故事线来揭开进程调度算法的神秘面纱,展现其背后的精妙设计与复杂逻辑,让读者仿佛跟随一位虚拟的“进程侦探”,一步步探索Linux如何高效、公平地管理众多进程,确保系统资源的最优分配与利用。 ####
66 4
|
27天前
|
缓存 负载均衡 算法
Linux内核中的进程调度算法解析####
本文深入探讨了Linux操作系统核心组件之一——进程调度器,着重分析了其采用的CFS(完全公平调度器)算法。不同于传统摘要对研究背景、方法、结果和结论的概述,本文摘要将直接揭示CFS算法的核心优势及其在现代多核处理器环境下如何实现高效、公平的资源分配,同时简要提及该算法如何优化系统响应时间和吞吐量,为读者快速构建对Linux进程调度机制的认知框架。 ####
|
28天前
|
消息中间件 存储 Linux
|
2月前
|
运维 Linux
Linux查找占用的端口,并杀死进程的简单方法
通过上述步骤和命令,您能够迅速识别并根据实际情况管理Linux系统中占用特定端口的进程。为了获得更全面的服务器管理技巧和解决方案,提供了丰富的资源和专业服务,是您提升运维技能的理想选择。
49 1
|
2月前
|
算法 Linux 调度
深入理解Linux操作系统的进程管理
【10月更文挑战第9天】本文将深入浅出地介绍Linux系统中的进程管理机制,包括进程的概念、状态、调度以及如何在Linux环境下进行进程控制。我们将通过直观的语言和生动的比喻,让读者轻松掌握这一核心概念。文章不仅适合初学者构建基础,也能帮助有经验的用户加深对进程管理的理解。
27 1