Linux_进程

本文详细解释了Linux进程的基本概念,包括进程的定义、进程控制块PCB,以及如何通过系统调用如fork创建进程。文章还深入讨论了进程的不同状态,如运行、休眠、停止、僵尸和孤儿状态,并提供了查看和操作进程的方法。
摘要由CSDN通过智能技术生成

Linux_进程

概念、创建、状态


文章目录

  • Linux_进程
  • 前言
  • 一、进程基本概念
    • 1、 进程
    • 2、描述进程-PCB
  • 二、对进程操作
    • 1、组织进程
    • 2、查看进程
    • 3、通过系统调用获取进程标示符
    • 4、★★★通过系统调用创建进程-fork初识
  • 三、进程状态
    • 1、进程状态查看
      • 1. R状态(运行状态)
      • 2. S、D状态(休眠状态 / 阻塞状态)
      • 3. T状态
      • 4. Z状态(zombie) && X状态(dead)
      • 5. 孤儿进程
  • 总结


前言

  • 冯诺依曼体系结构

我们常见的计算机,如笔记本。我们不常见的计算机,如服务器,大部分都遵守冯诺依曼体系。
在这里插入图片描述
截至目前,我们所认识的计算机,都是有一个个的硬件组件组成

  • 输入单元:包括键盘, 鼠标,扫描仪, 写板等
  • 中央处理器(CPU):含有运算器和控制器等
  • 输出单元:显示器,打印机等

关于冯诺依曼:

  • 这里的存储器指的是内存
  • 不考虑缓存情况,这里的CPU能且只能对内存进行读写,不能访问外设(输入或输出设备)
  • 外设(输入或输出设备)要输入或者输出数据,也只能写入内存或者从内存中读取。
  • 一句话,所有设备都只能直接和内存打交道。

  • 操作系统(Operator System)

概念

任何计算机系统都包含一个基本的程序集合,称为操作系统(OS)。笼统的理解,操作系统包括:

  • 内核(进程管理,内存管理,文件管理,驱动管理)
  • 其他程序(例如函数库,shell程序等等)

设计OS的目的

  • 与硬件交互,管理所有的软硬件资源
  • 为用户程序(应用程序)提供一个良好的执行环境

定位

  • 在整个计算机软硬件架构中,操作系统的定位是:一款纯正的“搞管理”的软件

如何理解 "管理"

  • 管理的例子
  • 描述被管理对象
  • 组织被管理对象

站在用户的角度:
用户->用户操作接口->system call

总结

计算机管理硬件

  1. 描述起来,用struct结构体
  2. 组织起来,用链表或其他高效的数据结构
    先描述在组织

系统调用和库函数概念

  • 在开发角度,操作系统对外会表现为一个整体,但是会暴露自己的部分接口,供上层开发使用,这部分由操作系统提供的接口,叫做系统调用。
  • 系统调用在使用上,功能比较基础,对用户的要求相对也比较高,所以,有心的开发者可以对部分系统调用进行适度封装,从而形成库,有了库,就很有利于更上层用户或者开发者进行二次开发。

↓ 对 进程 进行详解


一、进程基本概念

1、 进程

  • 课本概念:程序的一个执行实例,正在执行的程序等
  • 内核观点:担当分配系统资源(CPU时间,内存)的实体。

2、描述进程-PCB

  • 程信息被放在一个叫做进程控制块的数据结构中,可以理解为进程属性的集合。
  • 课本上称之为PCB(process control block),Linux操作系统下的PCB是: task_struct

task_struct-PCB的一种

  • 在Linux中描述进程的结构体叫做task_struct。
  • task_struct是Linux内核的一种数据结构,它会被装载到RAM(内存)里并且包含着进程的信息

task_ struct内容分类

  • 标示符: 描述本进程的唯一标示符,用来区别其他进程。

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

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

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

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

  • 上下文数据: 进程执行时处理器的寄存器中的数据[要加图CPU,寄存器]。

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

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

  • 其他信息

二、对进程操作

1、组织进程

  • 可以在内核源代码里找到它。所有运行在系统里的进程都以task_struct链表的形式存在内核里。

2、查看进程

  • 进程的信息可以通过 /proc 系统文件夹查看

如:要获取PID为1的进程信息,你需要查看 /proc/1 这个文件夹。
在这里插入图片描述

  • 大多数进程信息同样可以使用top和ps这些用户级工具来获取

运行如下查看进程

>#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
	while(1)
	{
		sleep(1);
	}
	return 0;
}

在这里插入图片描述

3、通过系统调用获取进程标示符

  • 进程id(PID)

  • 父进程id(PPID)

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

4、★★★通过系统调用创建进程-fork初识

  • 运行 man fork 认识fork
  • fork有两个返回值
  • 父子进程代码共享,数据各自开辟空间,私有一份(采用写时拷贝)
    在这里插入图片描述

返回值
在这里插入图片描述
fork之后,子进程返回值为 0 ,父进程返回子进程的PID,如果创建进程失败,返回-1,并且错误码被设置。

代码如下

  1 #include<stdio.h>  
  2 #include<unistd.h>
  3 #include<sys/types.h>
  4 int main()
  5 {
  6   //  int i = 10;
  7     printf("我是一个进程,我的pid是:%d,我的父进程是:%d\n",getpid(),getppid());
  8     sleep(4);fork();
  9 
 10    while(1)
 11    {
 12     printf("我是一个进程,我的pid是:%d,我的父进程是:%d\n",getpid(),getppid());
 13     sleep(1);
 14    }
 15     return 0;                                                                            
 16 }            
~


在这里插入图片描述
在这里插入图片描述
可以看出在fork之前只是有父进程的,fork之后除了父进程又多了一个子进

  • fork 之后通常要用 if 进行分流
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
	int ret = fork();
	if(ret < 0)
	{
		perror("fork");
		return 1;
    }
	else if(ret == 0)
	{ //child
		printf("I am child : %d!, ret: %d\n", getpid(), ret);
	}
	else
	{ //father
		printf("I am father : %d!, ret: %d\n", getpid(), ret);
	}
	sleep(1);
	return 0;
}

1、fork做了什么事情?

2、为什么fork有2个返回值?
3、为什么forkd的两个返回值,会给父进程返回子进程pid,给子进程返回0?
4、fork之后父子进程谁先运行?
5、如何理解同一个变量,会有不用的值?


1、fork做了什么事情?

fork创建子进程,系统中会多一个子进程
1.以父进程为模板,为子进程创建PCB
2、但是你今天创建的子进程,是没有代码和数据的!
目前和父进程共享代码和数据!
所以fork之后,父子进程会执行一样的代码。

  1 #include<stdio.h>
  2 #include<unistd.h>
  3 #include<sys/types.h>
  4 int main()
  5 {
  6   //  int i = 10;
  7     printf("我是一个进程\n");
  8     fork();
  9 
 10     sleep(1);
 11     printf("我也是一个进程,我看看我会被执行几次?\n");
 12     
 13     sleep(1);
 14     return 0;                                                                            
 15 }      

在这里插入图片描述

A.fork之前的代码父子进程都可以看到吗? 答案是的!
B.那为什么子进程不从头开始执行代码呢?
为什么我们的程序从上往下按照顺序去执行,是因为在程序执行时,有一个pc指针/eip寄存器,当执行完当前指令的只有,这个eip会自动指向下一条指令
那么当eip指向的是fork后续的代码,eip也会被子进程继承


2、为什么fork有2个返回值?
在这里插入图片描述
由上可知,fork之后,代码父子进程代码共享,(fork之后后续代码都会被共享),那么return是代码吗,答案是的,所以return也要被共享
那么就有:

  • 父进程被调度,就要执行return
  • 父进程被调度,也要执行return
    因此会有2个返回值

3、为什么forkd的两个返回值,会给父进程返回子进程pid,给子进程返回0?

例如在现实生活中父亲与儿女的数量对比一定是1:n (n>=1),1个父亲有若干个子女,假设n=3,对于父亲来说我有3个子女,我要知道我这3个子女都是谁,都叫什么名字,而对于子女来说,我只有这一个父亲。因此对于子进程来说当创建成功只有令返回值为0,只要代表我是子进程就可以了,但是对于父进程来说,返回值是子进程的PID,我要知道我的子进程到底是哪个。

4、fork之后父子进程谁先运行?

创建完成子进程,只是一个开始,创建完成子进程之后,系统的其他进程,父进程,和子进程,接下来要被调度执行的。当父子进程的PCB都被创建并在运行队列中排队的时候哪一个进程的PCB先被选择调度,那个进程就先运行!有OS自主决定,由各自PCB中的调度信息(时间片+优先级)+调度器算法共同决定。

5、如何理解同一个变量,会有不用的值?

进程的独立性,首先是表现在有各自的PCB,进程之间不会互相影响!代码本身是只读的,不会影响!但是数据父子是会修改的!

因此代码共享,数据各个进程必须想办法各自私有一份(发生了写时拷贝)。


三、进程状态

首先看一下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): 并不意味着进程一定在运行中,它表明进程要么是在运行中要么在运行队列里。
  • S睡眠状态(sleeping): 意味着进程在等待事件完成(这里的睡眠有时候也叫做可中断睡眠
    (interruptible sleep))
  • D磁盘休眠状态(Disk sleep)有时候也叫不可中断睡眠状态(uninterruptible sleep),在这个状态的进程通常会等待IO的结束。
  • T停止状态(stopped): 可以通过发送 SIGSTOP 信号给进程来停止(T)进程。这个被暂停的进程可以通过发送 SIGCONT 信号让进程继续运行。
  • X死亡状态(dead):这个状态只是一个返回状态,你不会在任务列表里看到这个状态。

1、进程状态查看

ps aux / ps axj 命令

   #include<stdio.h>
   #include<sys/types.h>
   #include<unistd.h>
   int main()
   {
       while(1)
       {
           //                                                        
      }
      return 0;
  }

1. R状态(运行状态)

在这里插入图片描述
在这里插入图片描述
一旦这个进程启动了,命令行命令就不起作用了这就是一个前台进程(前台进程会有一个"+")例如上图中的 R + R^+ R+

在这里插入图片描述

./mypro & ,以后台进程的形式运行
在这里插入图片描述

并且后台进程ctrl+c是无法终止掉的!杀掉进程才能结束

2. S、D状态(休眠状态 / 阻塞状态)

代码如下

#include<stdio.h>    
#include<sys/types.h>    
#include<unistd.h>    
int main()    
{    
    while(1)    
    {    
        int a= 0;    
        printf("pleasr ENTER#:\n");    
        scanf("%d",&a);                                                                      
    }    
    return 0;    
}

//运行后,就是不输入
//
在这里插入图片描述
//由于不输入卡住了,这个卡住的状态就是阻塞状态
在这里插入图片描述
以上这种休眠属于 浅度睡眠

  • S睡眠状态(sleeping): 意味着进程在等待事件完成(这里的睡眠有时候也叫做可中断睡眠
    (interruptible sleep))
  • D磁盘休眠状态(Disk sleep)有时候也叫不可中断睡眠状态(uninterruptible sleep),在这个状态的进程通常会等待IO的结束。
    D状态也是休眠状态,深度睡眠,专门针对磁盘设计的,不可被杀掉,OS也没有资格

3. T状态

T(stopped)
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

查看进程信号的详细列表 kill -l在这里插入图片描述
上图表示将进程暂停之后,进程状态的变化 S + S^+ S+->T。

我们为什么要暂停?,当进程访问软件资源的时候,可能暂时不让进程进行访问,就将进程设置为STOP

指令如下
kill -SIGSTOP PID

还有一个t(tracing stop):debug程序的时候,追踪程序,遇到断点,进程暂停了。

4. Z状态(zombie) && X状态(dead)

Z 僵尸状态
X 是一种瞬时的状态
在这里插入图片描述

代码如下:

#include<stdio.h>    
#include<sys/types.h>    
#include<unistd.h>    
#include<stdlib.h>    
int main()    
{    
    pid_t id = fork();    
    if(id<0) return 1;    
    else if(id == 0)    
    {    
        //子进程    
        int cnt = 5;    
        while(cnt)    
        {    
            printf("I am a child,run times:%d\n",cnt--);    
            sleep(1);    
        }    
        exit(0);//终止子进程    
    }    
    else{    
        //父进程    
        while(1)    
        {    
            printf("I am a father,running any time!\n");    
            sleep(1);                                                                        
        }    
    }    
    return 0;    
} 

在这里插入图片描述在这里插入图片描述

可以看到,当子进程被终止后,其状态变更为Z状态。

5. 孤儿进程

>子进程的父进程直接退出了,子进程要被领养,变成孤儿进程
>(如果)

在这里插入图片描述
父进程被终止后,此时子进程被1号进程领养,变成孤儿进程。1号进程一般是OS
那么
A.为什么父进程退出后直接消失了,没有变成僵尸进程?
在这里插入图片描述
可看到父进程的父进程正是bash
在这里插入图片描述
是因为这个父进程直接被bash回收了,(能创建你,也能回收你)

B.为什么要领养?

如果不领养的话,那么这个进程在退出的时候,就没有人去回收它的PCB,就会造成内存泄漏
如果不领养就会造成大量的孤儿进程在退出之后变成Z状态,进而消耗内存资源

总结

对进程的查看,状态,创建过程的简单理解

  • 29
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Linux进程调度器是一个负责管理进程的代码模块,它负责从就绪队列中选择一个进程来运行,并将CPU时间片分配给该进程。Linux进程调度器使用多种调度算法来决定哪个进程应该被选中运行。下面介绍几种常见的调度算法。 1. 时间片轮转调度算法 时间片轮转调度算法是一种基于时间片的调度算法。在这种算法中,每个进程被分配一个固定大小的时间片,当时间片用完后,进程将被放回就绪队列中,然后选择下一个进程来运行。这种算法确保了所有进程在一定时间内都能获得CPU时间。 2. 最短作业优先调度算法 最短作业优先调度算法是一种基于进程执行时间的调度算法。在这种算法中,进程被按照它们的执行时间排序,然后从最短的进程开始运行。这种算法可以确保短进程优先,但会导致长进程等待时间过长。 3. 优先级调度算法 优先级调度算法是一种基于进程优先级的调度算法。每个进程都有一个优先级值,较高优先级的进程将被优先选择运行。这种算法可以确保高优先级进程优先,但会导致低优先级进程饥饿。 4. 多级反馈队列调度算法 多级反馈队列调度算法是一种基于进程优先级和时间片的调度算法。在这种算法中,进程被分配到多个队列中,每个队列具有不同的优先级和时间片大小。当进程在当前队列中使用完其时间片时,它将被放在下一个队列中,直到完成为止。这种算法可以平衡短进程和长进程的等待时间。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值