Linux入门学习:进程概念
文章目录
- 1. 什么是进程?
- 1.1 基本概念
- 1.2 task_struct
- 2. 组织进程
- 3. 查看进程
- 3.1 父进程与子进程
- 3.2 fork创建子进程
- 3.3 kill
- 3.4 /proc
1. 什么是进程?
1.1 基本概念
在课本的概念中,进程程序的一个执行实例,正在执行的程序。其内核观点:担当分配系统资源(CPU时间,内存)的实体。但这些概念太笼统,并不能让我们更加清晰的知道进程是什么,实际上,进程是内核数据结构(pcb) + 程序的代码与数据。
pcb(process control block)进程控制块,进程信息被放在一个叫pcb的数据结构中,可以理解为进程属性的集合。在Linux入门学习:深刻理解计算机硬件与OS体系中,我们已经了解到操作系统对我们的硬件进行管理的基本思想----先描述,后组织,pcb就是操作系统用于描述进程的数据结构。
Linux操作系统下的PCB是: task_struct,也就是说Linux描述进程的结构体叫做task_struct,task_struct是Linux内核的一种数据结构,它会被装载到RAM(内存)里并且包含着进程的信息。
1.2 task_struct
在Linux系统中,task_struct一般包含以下信息:
标示符: 描述本进程的唯一标示符,用来区别其他进程。
状态: 任务状态,退出代码,退出信号等。
优先级: 相对于其他进程的优先级。
程序计数器: 程序中即将被执行的下一条指令的地址。
内存指针: 包括程序代码和进程相关数据的指针,还有和其他进程共享的内存块的指针
上下文数据: 进程执行时处理器的寄存器中的数据[休学例子,要加图CPU,寄存器]。
I/O状态信息: 包括显示的I/O请求,分配给进程的I/O设备和被进程使用的文件列表。
记账信息: 可能包括处理器时间总和,使用的时钟数总和,时间限制,记账号等。
其他信息
2. 组织进程
Linux描述完进程task_struct,在内存中,他是怎么将他们组织起来的?答案:是以链表的形式组织起来的!
进程与进程之间:
在内存中,程序文件一般在磁盘,我们要运行时,先在内存中拷贝一份exe文件,再进程管理的指针中,插入新的task_struct,这个新的task_struct会有成员管理内存中加载的exe,并加载这个exe对应的属性到进程之中。
内存指针: 包括程序代码和进程相关数据的指针,还有和其他进程共享的内存块的指针
由此,我们可以知道一个事实:我们在Windows双击一个exe文件,把他运行起来,实质上,就是在系统中创建了一个新的进程!在Linux中的,像ls , pwd指令,本质上也是运行了一个exe,创建了一个进程,只不过运行完就直接退出了,而在Windows上的很多软件是常驻进程,一般运行了之后就不会再退出了,没有"运行完"的说法,除非用户直接×掉,直接终止运行。
3. 查看进程
3.1 父进程与子进程
pid – 进程id
ppid – 父进程id
先要明白一个概念:在Linux操作系统中,创建新进程的时候,都是由自己的父进程创建的!!在C语言中,我们可以通过函数getpid与getppid,来查看当前启动的C语言程序的pid与ppid。
我们先来观察一个现象:在Linux中写下这样的一段代码
#include <stdio.h>
#include <unistd.h>
int main()
{
while(1)
{
printf("我是一个进程, 我的pid: %d, ppid, %d\n", getpid(), getppid());
}
return 0;
}
反复运行后:
我们发现反复运行后,每次的pid都不一样,但是他们的ppid,每次都是一样的!!为什么?
#查看对应进程的相关信息指令
ps axj | head -1 && ps ajx | grep 进程id
结论:这些进程的父进程都叫bash!!。bash:命令行解释器。在命令行中,我们运行我们自己的可执行程序或者命令行指令本质是bash进程创建的子进程,由子进程 执行我们的代码! !
我们在登录的时候,操作系统会自动为我们启动一个bash程序,bash前面有一个-,代表着我们是通过命令行终端进行登录的
3.2 fork创建子进程
在C语言中有一个函数fork,可以借用系统调用,来创建子进程,头文件是<unistd.h>,借此,我们在Linux写一个程序,来先看看现象:
#include <stdio.h>
#include <unistd.h>
int main()
{
printf("父进程运行: pid: %d, ppid:%d\n", getpid(), getppid());
pid_t id = fork();
if (id == 0)
{
// 子进程
while (1)
{
printf("我是子进程,我的pid: %d, ppid: %d,ret = %d\n", getpid(), getppid(),id);
sleep(1);
}
}
else
{
// 父进程
while (1)
{
printf("我是父进程,我的pid: %d, ppid: %d,ret = %d\n", getpid(), getppid(),id);
sleep(1);
}
}
return 0;
}
运行之后
我们可以观察到一个奇怪的现象:为什么if和else的条件同时被满足了?!!竟然同时进行了两个循环?!!毫无疑问的,肯定是程序启动了两个进程,这其中的细节会衍生出很多问题:
❓Q1:代码中fork返回值是什么?
pid_t id = fork();
如果fork成功创建进程,对于父进程的返回值为子进程的pid,子进程的返回值的0。如果创建失败,父进程的返回值为-1。由返回值的不同,满足if或else的不同条件,启动对应的循环。
——》父进程往往只有一个,而子进程往往有多个,给父进程返回子进程的id,是为了方便管理子进程。Linux进程的整体是一个树型结构!!
❓Q2:一个函数(fork)为什么能返回两次?两次的数据还不相同?!
我们的程序在fork之后,毫无疑问,会启动两个程序,这两个程序的关系是父子关系,一般而言,这两个程序的代码是共享的!但是数据是自己保有一份的!!
——》进程与进程之间是相互独立的! 多个进程之间,运行的时候,即使他们是父子,也不会相互影响,即使其中一个挂了,另一个也能正常运行。对于代码而言,任何进程对代码的权限只是可读的,所以不同的进程使用同一份代码就能得到安全型的保证。但是数据一定是独立的,对于父子进程,fork各自返回了一次,他们得到了各自的数据,各自对自己的数据进行处理,从而启动了不同的循环。
数据是独立的还有一个验证:
int cnt = 0;
if (id == 0)
{
// 子进程
while (1)
{
printf("我是子进程,我的pid: %d, ppid: %d,cnt = %d\n,", getpid(), getppid(),cnt);
sleep(1);
cnt++;
}
}
else
{
// 父进程
while (1)
{
printf("我是父进程,我的pid: %d, ppid: %d,cnt = %d\n", getpid(), getppid(),cnt);
sleep(1);
}
}
——》观察现象,父进程与子进程的cnt值各不相同。
3.3 kill
对于没有结束的进程,可以用kill指令将其杀掉
kill -9 进程标示符
3.4 /proc
/proc目录里面存储文件不在磁盘上,里面的文件都是内存级的,在关机时会消失,开机时又会出现,它是对动态运行的所有进程的一个可视化信息。
蓝色表示的是目录文件,因为一个进程里面可能会存在很多信息,进去看看
1、其中exe说明当前的进程是可以找到自己要执行的代码的(可视化出来了)。
2、cwd表示的是当前进程的工作目录(current work dir),所以为什么你fopen出来的新文件会被默认放在当前目录,这其实是由该进程的cwd决定的!!
本文就到这里,感谢你看到这里❤️❤️! 我知道一些人看文章喜欢静静看,不评论🤔,但是他会点赞😍,这样的人,帅气低调有内涵😎,美丽大方很优雅😊,明人不说暗话,要你手上的一个点赞😘!
希望你能从我的文章学到一点点的东西❤️❤️