欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 房产 > 家装 > Linux:进程(一)

Linux:进程(一)

2025/6/28 6:24:36 来源:https://blog.csdn.net/m0_68142120/article/details/144247156  浏览:    关键词:Linux:进程(一)

冯 诺依曼体系结构

大多数的笔记本,服务器都会遵守冯诺依曼体系

1. 此处的存储器指的是内存。

2. 不考虑缓存情况,这里的CPU能且只能对内存进行读写,不能访问外设(输入或输出设备)。

3. 外设(输入或输出设备)要输入或者输出数据,也只能写入内存或者从内存中读取。

4. 所有的设备都只能和内存打交道。

下图用来模拟在qq上与好友聊天时,数据的流动过程。

如果是传文件最开始的输入设备变为磁盘,最后的输出设备变为显示器。

操作系统的概念

任何计算机系统都包含一个基本的程序集合,称为操作系统(os)。

笼统的理解,操作系统包括:

1. 内核(进程管理、内存管理、文件管理、驱动管理)。

2. 其他程序(例如函数库,shell程序等等)。

 设计os的目的

对下:与硬件交互,管理所有软硬件资源。

对上:为用户程序(应用程序)提供一个良好的执行环境。

 其核心功能是:管理

在开发角度,操作系统对外会表现为一个整体,但是会暴露自己的部分接口,供上层开发使用,这部分由操作系统提供的接口叫做系统调用。

系统调用在使用上,功能比较基础,对用户的要求也比较高。所以部分系统调用进行适度封装即可形成库,有了库就很有利于更上层用户或者开发者进行二次开发。

进程

1. 基本概念

进程,它代表了正在运行的程序的实例,当一个程序正在执行时操作系统会为它创建一个进程。进程可以管理各种资源、如内存、文件、设备等。他们可以申请和释放资源,并且可以对资源进行保护和共享。通过进程管理,操作系统可以有效分配资源并且可以防止资源浪费。

2. PCB

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

Linux操作系统下的PCB是task_struct,用来描述进程的结构体。

利用类似双向链表的形式将各个进程控制块PCB联系起来,方便管理。

3. task_struct

1. 标识符:描述本进程唯一标识符,用来区别其他进程。

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

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

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

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

6. 上下文数据:进程执行时处理器的寄存器中的数据源。

进程中的代码不可能在很短时间运行完的,规定每个进程的时间片(单次运行的最长时间),用户感受到的多个进程同时运行,本质是CPU的快速切换,CPU只有一套寄存器,为了保护上下文,进程的这些临时数据被写入在PCB中,再来执行时恢复上下文。

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

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

9. 其他信息

 进程操作

1. 查看进程信息

进程的信息可以通过/proc系统文件夹(在根目录下)查看

上面的数字就是进程的PID,记录对应进程的信息。

要获取PID为1的进程信息,就查看/proc/1这个文件夹。

 我们使用指令ps aux查看所有的进程信息。

通过系统调用获取进程标识符

  • 进程id(PID)
  • 父进程id(PPID)

 通过以下程序来查看

#include<iostream>
#include<cstdio>
#include<sys/types.h>
#include<unistd.h>using namespace std;int main()
{while(1){ printf("pid: %d\n",getpid());                                                          printf("ppid: %d\n",getppid());}return 0;
}

 

可以通过ctrl+c或者kill -9 进程的PID杀死相应的进程。

而且我们发现每次程序的pid不一样但是ppid是一样的(实际就是bash)

 2. 创建进程

我们可以利用fork函数创建一个子进程。

代码如下

#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
int main()
{fork();while(1){printf("pid: %d\n",getpid());                                                          printf("ppid: %d\n",getppid());sleep(1); }return 0;
}

 上图用红框圈起来的为该进程的pid和ppid,用蓝框圈起来的为通过fork()函数创建的子进程的pid和ppid。fork新创建的pid的父进程就是这个进程(即第一个进程)。

fork有两个返回值

父子进程代码共享数据各自开辟空间,私有一份(采用写时拷贝)

因为父子进程代码共享,而且fork函数在执行return语句时已经创建好了子进程,所以return语句会被父子进程创建两次,所以fork函数肯定有两个返回值

  • 若子进程创建成功,则在父进程中返回子进程PID,在子进程中返回0.
  • 若子进程创建失败,则在父进程中返回-1。

通过fork的返回值,让父子进程分别执行不同的操作,如下代码

#include<cstdio>
#include<unistd.h>
#include<sys/types.h>
int main()
{     int id= fork();if(id<0){perror("fork");//出错return 1;}else if(id==0){printf("子进程:%d!, id=%d\n",getpid(),id);}else{printf("父进程:%d!, id=%d\n",getppid(),id);}sleep(1);                                                                              return 0;
}

进程之间具有独立性,即使一个进程中途异常退出,也不会影响其他进程。 

进程状态

下面是在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 */
};

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

S 睡眠状态(sleeping):意味着进程在等待事件完成(这里的谁卖你也叫做可中断睡眠)

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

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

t 追踪状态(tracing stop):表示进程的追踪状态,主要在使用调试器时。

X 死亡状态(dead):这个状态只是一个返回状态,你不会在任务列表里看到这个状态。

查看进程状态

指令:

ps aux / ps axj

a:显示一个终端所有进程。

x:显示没有控制终端的进程,例如后台运行的守护进程。

j:显示进程归属的进程组ID,会话ID、父进程ID,以及与作业控制相关的信息。

u:以用户为中心的格式显示进程信息,提供进程的详细信息,如用户、CPU和内存使用情况等。

运行状态R

运行状态是不一定占用CPU的,也并不意味着进程一定在运行中,一个进程处于R状态,它只是表明进程要么是在运行中要么是在运行队列里,可以同时存在多个处于R状态的进程

 浅睡眠状态S

进程在等待事情完成时

比如一段程序里的sleep(1000);,执行sleep这个时候进程就属于浅度睡眠状态。

浅度睡眠状态可以用kill指令杀死对应进程。

深度睡眠状态D

有时候也叫不可中断睡眠(深度睡眠状态),通常会等待IO的结束。不可以被kill杀掉

比如对磁盘进行写入时,那么在磁盘进行写入期间,该进程就处于深度睡眠状态。是不会被杀的,等磁盘回复是否写入成功以做出相应回答。

停止状态T

可以通过发送SIGSTOP信号来停止进程,这个被暂停的进程通过发送SIGCONT信号继续运行。

追踪状态t

程序被debug,断点:进程被暂停了。

僵尸状态Z

这是一个比较特殊的状态,当一个进程退出并且父进程没有读取到子进程退出的代码,即子进程退出父进程还在运行并且没有读取子进程状态,子进程就会进入Z状态。

僵死进程会以终止状态保持在进程表中,并且会一直等待父进程读取退出状态代码。

死亡状态X

当一个进程的退出信息被读取后,该进程所申请的资源就会立即被释放,该进程也不会存在了,所以无法观察到死亡状态。

僵尸进程与孤儿进程

僵尸进程

什么是僵尸进程

子进程退出父进程还在运行,父进程没有读取子进程状态,此时子进程就是一个僵尸进程

僵尸进程的危害

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

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

并且父进程创建了很多子进程且没有回收,会造成内存资源的浪费(即内存泄漏)。因为数据结构对象是要占用内存的。进程退出之后内存泄漏问题就不存在了

孤儿进程

那父进程提前退出,子进程后退出并且进入Z状态之后要怎么处理呢?

父进程先退出,子进程就称之为"孤儿进程"。

孤儿进程会被1号init进程(操作系统)领养,也由init进程回收

被领养后孤儿进程就变成了后台进程了,不能被ctrl+c杀掉,可以被kill -9 进程id  杀掉


这篇就到这里啦(づ ̄3 ̄)づ╭❤~

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词