【Linux探索学习】第十二弹——初识进程:进程的界说、描述和一些简朴的相
Linux学习笔记:https://blog.csdn.net/2301_80220607/category_12805278.html?spm=1001.2014.3001.5482
前言:
在前面经过那么多篇的铺垫后,今天我们正式进入Linux学习的第一个重难点——进程,理解进程对于我们学习利用系统的别的部分,尤其是多文件处理和资源管理非常重要,下面我们正式进入进程的第一篇解说
目录
一、进程概念
二、进程描述
三、检察进程
四、通过系统调用获取进程标识符
五、通过系统调用创建进程——初识fork
六、总结
一、进程概念
进程是一个正在执行的程序的实例。它不仅包括程序的代码,还包括程序的当前运动、寄存器、程序计数器、堆栈及其全部与执行相干的资源。简朴来说,进程是一个程序在运行时的一个动态实体。
必要留意的一点是进程的程序是一定被加载在内存中的,因为进程是系统将要进行处理的数据,而CPU是从内存中获取数据的,所以说进程的程序一定被加载在内存中的,比如我们vim写的一个.c的C语言程序,它在利用系统下的本质就是一个文件,是存放在外设中的,当运行起来时,我们就会将它的相干数据存放到内存中去,以便于CPU直接获取
二、进程描述
一个利用系统大概可以同时进行多个进程,比如我们可以让多个程序同时进行,我们的电脑可以同时跑多个软件,为了避免进程执行起来互相干扰,所以我们要对进程进行管理
一般进行管理的过程就是:先描述+再组织
所以我们要进行进程描述:任何一个进程,在被加载到内存,形成真正的进程时,利用系统都要先创建描述进程的结构体对象——PCB,也叫做进程控制块,可以理解为进程属性的集合,利用系统是C语言写的,所以PCB一定是一个struct结构体,PCB中会包罗进程如下的重要信息:
[*]进程ID(PID):唯一标识一个进程的编号。
[*]进程状态:当前进程的状态。
[*]程序计数器:指向当前执行指令的地址。
[*]CPU寄存器:进程在执行时的寄存器内容。
[*]内存管理信息:如页表和段表。
[*]进程优先级:调治时的优先级信息。
此外为了方便管理进程,处理进程与进程之间的关系,进程在内存中是以队列的形式存在的,具体点来讲就是链表(双链表),进程在内存中的存在形式可以抽象为下图:
https://i-blog.csdnimg.cn/direct/38c83b76c2384f2bae22cca0c6427ab8.png
由于PCB中包罗着进程的全部信息,所以对进程管理的本质其实就是对进程的PCB做管理,进程在利用系统又通过队列进行链接,所以对进程的管理,其实就是对链表的增编削查
这里的PCB是针对全部利用系统而言的,在我们的Linux中我们每每习惯称呼这个概念为task struct
三、检察进程
在上面我们讲到进程的许多属性,包括进程编号、进程状态等等许多内容
起首我们可以通过检察/proc/文件,来检察我们目前正在执行的全部进程
ls /proc/ https://i-blog.csdnimg.cn/direct/ab0f6acc20a1422cbf454ba667368f24.png这些数字就是进程的PID,每个进程都会有一个对应的PID,PID就是我们上面所说的进程ID,也叫做进程标识符,我们可以通过这些进程标识符来检察每个进程具体的信息,比如检察1号进程
ls /proc/1 除了上面的这个方法外,我们还可以通过下面这个指令,不仅可以看到全部的进程,还可以看到它们的进程的属性信息:
ps axj 我们节选一部分:
https://i-blog.csdnimg.cn/direct/90a6495d0d3645efb2c66b815d7d13b8.png
执行结果的第一行就是我们的进程属性信息的列名,下面就是每个进程对应的属性信息,我们可以只打印出一行来看一下进程属性的内容(必要借助之前的知识:管道 | 和打印行数head)
ps axj | head -1 https://i-blog.csdnimg.cn/direct/8495fa1ac707465d9225945cae43dec8.png
对于这些属性信息中,我们先记住前两个就行了,PPID指的是父进程标识符,PID知道是当前进程标识符
目前我们自己创建的可执行文件有test
https://i-blog.csdnimg.cn/direct/63f2c9ffedfa493494d4e140b452c3e3.png
我们可以检察下我们自己创建的这个进程的相干信息(留意只有当我们的程序在跑着的时候它才叫进程,所以我们可以将我们的程序写成一个死循环,然后让它执行起来)
ps axj | head -1 && ps axj | grep test https://i-blog.csdnimg.cn/direct/2ab62689605243fba588016466992da4.png
观察这个执行结果,我们可以发现有两个相干进程,会出现第二条的缘故原由就是执行查找test进程的下令自己也会成为一个进程,而这个进程中含test,所以会把自身也带上
如果不想要,可以在背面加上 | grep -v grep,这个-v选项我们在前面讲指令的时候是讲过的,是反向匹配的意思
ps axj | head -1 && ps axj | grep test | grep -v grep https://i-blog.csdnimg.cn/direct/14b0245ed14648b794a718234093ee2f.png
四、通过系统调用获取进程标识符
除了上面获取进程标识符的方法外,我们还可以通过系统调用的方式来获取体现符,系统接口为getpid和getppid,我们可以通过man手册来检察这个接口
man 2 getpid https://i-blog.csdnimg.cn/direct/d19139f309b94c21b0e7417bb790517a.png
具体方法如下:
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
printf("pid: %d\n", getpid());
printf("ppid: %d\n", getppid());
return 0;
} 多次执行这个程序,我们会发现pid一直在变化,而ppid一直不变,也就是说子进程编号一直在变化,而父进程一直没变,为什么会出现这个现象呢?
这是因为,我们在打开Linux时,会起首创建一个bash进程,形成对话框,这个bash进程也是别的全部进程的子进程,所以一般代码重新运行时,它的子进程编号会变,而父进程编号不会变
我们可以创建一个监视窗口方便观察(了解):
while :; do ps axj | head -1 ; ps axj | grep test | grep -v grep;
echo "----------------------------"; sleep 1 ; done 五、通过系统调用创建进程——初识fork
我们可以通过fork手动创建进程,我们可以通过man手册检察一下fork
man fork https://i-blog.csdnimg.cn/direct/0e9aed5ffef643b19142a05b38a5a085.png
我们先来看下面的一个小程序:
#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
int main()
{
printf("before test");
fork();
printf("after test");
return 0;
} 运行结果:
https://i-blog.csdnimg.cn/direct/0f63904c1b1d4179926474c9d6bfc170.png
我们留意到在fork()函数之后的第二行打印语句执行了两次,说明在fork()之后一个进程变成了两个进程
此外,fork函数另有一个重要知识就是它是有两个整形返回值的,这点与我们之前所学的C语言中的函数差别很大,因为我们之前所学的函数都是只有一个返回值,fork的两个整形返回值中,大于0代表父进程,等于0是子进程
我们下面来看如许一个程序来验证一下:
1 #include<stdio.h>
2 #include<sys/types.h>
3 #include<unistd.h>
4
5 int main()
6 {
7 pid_t id=fork();
8 if(id>0)
9 {
10 //父进程
11 printf("I am parent process, pid:%d, ppid:%d\n",getpid(),getppid());
12 }
13 else if(id==0)
14 {
15 //子进程
16 printf("I am child process, pid:%d, ppid:%d\n",getpid(),getppid());
17 }
18 printf("hello linux\n");
19 return 0;
20 }
在这个函数中我们尝试将父子进程分开,而且在最后有一个公共代码区,执行结果:
https://i-blog.csdnimg.cn/direct/7fd6db23f8e04ca096a44a030762e7d6.png
我们可以看到子进程的ppid就是父进程的pid,所以也印证了它们的父子关系,而且最后一个打印代码父子进程都执行了
相信不少同学对上面的问题已经有了很大的迷惑了,比如fork为什么要给子进程返回0,给父进程返回子进程pid呢?其实这就是为了区分父子进程,让差别的执行流执行差别的代码
一般而言fork之后的代码是共享的,这也就是为什么上面的 "hello linux" 打印了两遍的缘故原由,因为父子进程都执行了它,那么如果此时子进程对共享数据进行利用了,我们就必要对额外利用的数据开发新空间,这就是写时拷贝,这我们会在背面具体解说
至于为何pid_t id中的id可以取两个值,这也必要我们背面讲到进程空间地址的问题时再提,现在只必要也简朴地理解为写时拷贝就可以了
六、总结
以上就是今天解说的进程的基础内容,篇幅较长,笔墨较多,相信认真看完的你会有所劳绩,背面我们就将开启进程知识的深度解说
https://i-blog.csdnimg.cn/direct/fd3dc0b029e44b568e6eaef35ba682ad.jpeg
感谢各位大佬观看,创作不易,还请各位大佬点赞支持!!!
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页:
[1]