【Linux】详细介绍进程的概念

目录

一、初识进程概念

真正的进程概念如下:

二、Linux中PCB的操作系统学科叫法:task_struct

[1、简单认识task_ struct内容分类](#1、简单认识task_ struct内容分类)

2、问题:操作系统怎么知道当前程序执行到哪一行代码了?

三、linux关于进程的常用指令:

[1、ps axj](#1、ps axj)

2、运行一个.c可执行文件

[3、 ps ajx | head -1](#3、 ps ajx | head -1)

[4、man getpid](#4、man getpid)

[5、man getppid](#5、man getppid)

[6、每一次启动程序 , 当前进程的pid值都会变化](#6、每一次启动程序 , 当前进程的pid值都会变化)

7、进程的当前工作目录:

8、函数chdir

四、初识fork:通过系统调用fork创建新进程

1、通过一段代码引出fork

2、fork的返回值

3、创建子进程的作用:

问题:上述代码中一个id变量怎么会即等于0,又大于0呐?


前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家

点击跳转到网站

一、初识进程概念

很多教材观点:

1、加载到内存的程序就叫进程;

2、正在运行的程序就叫进程;

但这些叙述是模模糊糊的,看着根本不明白进程到底是什么。下面我们逐步介绍:

事实上:程序会被编译成二进制文件,文件又是存在磁盘上,磁盘属于外设;

1、我们可以同时启动多个程序,将这多个程序加载到内存(通常是可执行程序.exe);

2、操作系统必须去管理这些加载到内存的程序,比如如何给这些程序分配内存。

3、操作系统是如何管理加载到内存的程序呐,就是我们上述所介绍的:先描述再组织。

第3点过程介绍,引出进程的概念:

操作系统刚开始将进程加载到内存的时候,它是不认识这些进程的,所以根据"先描述,后组织",会去创建一个结构体去描述这些进程,然后把对应的属性填入结构体的成员,这样就认识了,这样操作系统就可以通过访问这些结构体间接去管理这些进程数据。

如下:

这个结构体被叫做PCB(process ctrl block:进程控制块),上述图中只是部分属性,实际中可能有几百种属性。因为我们以链表为例,所以这里是在成员中添加一个PCB的指针用于指向下一个结点,这样对进程的管理就转化为对PCB链表的增删查改,这个就是对进程的建模过程。

上述就是大致管理图,

真正的进程概念如下:

进程 = 内核PCB对象(内核数据结构) + 可执行程序

所有对进程的控制和操作,都只和进程的PCB有关,和进程的可执行程序没有关系,因为是通过访问PCB去间接管理可执行程序,我们上面只是以链表为例,实际PCB结构体可以用任意数据结构进行管理。

二、Linux中PCB的操作系统学科叫法:task_struct

1、简单认识task_ struct****内容分类

(1)标示符: 描述本进程的唯一标示符,用来区别其他进程 。
(2)状态: 任务状态,退出代码,退出信号等。
(3)优先级: 相对于其他进程的优先级。
(4)程序计数器: 程序中即将被执行的下一条指令的地址。
(5)内存指针: 包括程序代码和进程相关数据的指针,还有和其他进程共享的内存块的指针
(6)上下文数据: 进程执行时处理器的寄存器中的数据[休学例子,要加图CPU,寄存器]。
(7)I/O状态信息: 包括显示的I/O请求,分配给进程的I/O设备和被进程使用的文件列表。
(8)记账信息: 可能包括处理器时间总和,使用的时钟数总和,时间限制,记账号等。
(9)其他信息

2、问题:操作系统怎么知道当前程序执行到哪一行代码了?

这就是通过task_ struct的内容中程序计数器知道的。CPU中存在一个eip寄存器(也叫PC指针),实际上程序计数器就是PC指针,里面存的是当前正在执行的指令的下一条指令,那些判断、循环、函数跳转等等操作的本质就是修改PC指针,因为程序是从上往下执行的,而这些操作都会改变这个顺序,所以实际上修改的是PC指针。

三、linux关于进程的常用指令:

1、ps axj

这个指令用来查看所有进程:

2、运行一个.c可执行文件

cpp 复制代码
 1 #include<stdio.h>
  2 #include<unistd.h>
  3 int main()
  4 {
  5     while(1)
  6     {
  7         printf("i am a process!\n");
  8         sleep(2);
  9     }
 10     return 0;
 11 }

如上,写了一个test。c文件,此时我们将它编译运行,如下:

此时这个程序就变成了一个进程,我们可以通过指令查看

3、 ps ajx | head -1

该指令用来显示当前系统中所有进程表头的详细信息。

但为了看见我们自己的,我们可以筛选一下,使用指令:

cpp 复制代码
ps ajx | head -1 && ps ajx | grep myprocess

这样可以筛选包含myprocess关键词的内容:

这里会显示第二行内容是因为:几乎所有的独立的指令都是程序,运行起来也会变成进程,所以筛选的时候会把grep这个进程也筛选进来,如果不想看到,可以使用-v选项,反向筛选不包括grep关键词的:

cpp 复制代码
ps ajx | head -1 && ps ajx | grep myprocess | grep -v grep

4、man getpid

这个指令用来查看getpid这个函数的信息:getpid就是一个系统调用,用来查看当前进程的pid值,返回值pid_t类型相当于int

5、man getppid

首先我们要知道,一般在linux中,普通进程都会有父进程,而getppid就是用来查看当前进程的父进程的pid值,man指令就是用来查看这个函数的信息。

6、每一次启动程序 , 当前进程的pid值都会变化

每一次启动程序 , 当前进程的pid值都会变化,因为每次启动程序,都会是一个新进程。

但会发现父进程始终不会变。

所以我们可以看一下父进程10645是谁?使用如图指令后,发现是进程bash。

bash就是命令行解释器,所以我们命令行启动的进程都是bash的子进程。

7、进程的当前工作目录:

在文件操作中我们知道"fopen("file.txt","w");",如果不存在这个文件,就会在当前路径下帮我们创建这个文件。这里的当前路径我们以前都认为是该.c文件所在的路径,但实际上是当前可执行程序对应的进程的工作目录(cwd):

这里的cwd就是进程的当前路径,对应的下面还有exe程序对应的路径。

8、函数chdir

四、初识fork:通过系统调用fork创建新进程

1、通过一段代码引出fork

首先我们写一段测试代码:

cpp 复制代码
  1 #include<stdio.h>
  2 #include<unistd.h>
  3 #include<sys/types.h>
  4 int main()
  5 {
  6     printf("before fork:i am a prcess,pid:%d,ppid:%d\n",getpid(),getppid());
  7     pid_t id = fork();
  8     printf("after fork:i am a prcess,pid:%d,ppid:%d\n",getpid(),getppid());
  9     return 0;
 10 }                                                                                                                                                                       

运行结果如下:

我们会发现,我们代码中只有两个printf,但运行结果却打印了三行内容,原因就在于,当执行fork函数后,fork会创建子进程,所以此时会走两条分支,所以after的printf会打印两次,第一次打印的是父进程的内容,第二次打印的是子进程的内容,可以通过ppid看出打印顺序。

(所以可以这样理解,fork之后代码是父子进程共享的,相当于要执行两份代码)

2、fork的返回值

可以通过手册查看,发现fork的返回值如下:

如果创建失败,则返回-1;

如果创建成功,则给父进程返回子进程的pid,给子进程返回0;因为父进程可以有多个子进程,而子进程只有一个父进程,所以父进程找子进程是比较困难的,就需要一些标识符,比如这里pid。子找父是比较简单的,比如直接getppid。

3、创建子进程的作用:

一般情况,我们创建子进程是想让父子进程去做不同的事,这样的场景就可以通过一些判断语句,结合fork的返回值,就可以让父子进程做不同的事。

这段代码也是让我们第一次接触同时运行两个死循环。

创建子进程的时候,操作系统会给子进程创建一个PCB,默认情况会将父进程的大部分属性拷贝给子进程,只有一些用于标识的属性才和父进程不一样,比如pid和ppid。创建一个进程的时候,系统中就会多一个进程。

问题:上述代码中一个id变量怎么会即等于0,又大于0呐?

首先我们要知道,一个进程崩溃了,是不会影响其他进程的,也就是说进程之间是具有独立性的,并不是相互影响。我们可以通过指令去杀死对应进程。

相关推荐
xixingzhe29 分钟前
docker转移镜像
运维·docker·容器
菜狗想要变强41 分钟前
Linux驱动开发--异步通知与异步I/O
linux·运维·驱动开发
SuperW1 小时前
Linux学习——IO多路复用知识
linux·服务器·学习
搬码临时工1 小时前
路由器转发规则设置方法步骤,内网服务器端口怎么让异地连接访问的实现
服务器·网络·智能路由器·内网穿透·端口映射·外网访问
CopyLower1 小时前
Spring Boot的优点:赋能现代Java开发的利器
java·linux·spring boot
终身学习基地2 小时前
第七篇:linux之基本权限、进程管理、系统服务
linux·运维·服务器
安顾里2 小时前
LInux平均负载
linux·服务器·php
unlockjy2 小时前
Linux——进程优先级/切换/调度
linux·运维·服务器
该死的碳酸饮料呀2 小时前
PLOG安装
linux·ubuntu
前进的程序员2 小时前
CentOS 系统 DeepSeek 部署
运维·人工智能·centos·deepseek