大家好,我是苏貝,本篇博客带大家了解Linux进程(1),如果你觉得我写的还不错的话,可以给我一个赞👍吗,感谢❤️
目录
- 1. 冯诺依曼体系结构
- 2.操作系统(Operator System / OS)
- 2.1 概念
- 2.2 设计OS的目的
- 3.进程
- 3.1描述进程-PCB
- 3.2 进程启动
- (A)../XXXX,意思是运行我们自己写的可执行程序,本质就是让系统创建进程并运行
- (B)每个进程都有自己的唯一标识符,叫做进程pid
- (C)如何知道进程的pid
- (D)CTRL+C终止进程,kill -9 pid直接杀掉进程
- 3.3 进程创建的代码方式
- 3.4 创建子进程的目的:执行与父进程不同的代码
- 3.5 查看进程(用/proc)
1. 冯诺依曼体系结构
我们常见的计算机,如笔记本,我们不常见的计算机,如服务器,大部分都遵守冯诺依曼体系。
截至目前,我们所认识的计算机,都是有一个个的硬件组件组成:
1.输入设备:包括键盘, 鼠标,扫描仪, 写板,网卡,磁盘等
2.存储器:内存
3.中央处理器(CPU):含有运算器和控制器等
4.输出设备:显示器,打印机,网卡,磁盘等
数据是要在计算机的体系结构中进行流动的,流动过程中,进行数据的加工处理。从一个设备到另一个设备,本质是一种拷贝。数据设备间的拷贝的效率,决定了计算机的基本效率
我们看数据信号,为什么冯诺依曼体系需要存储器(内存)呢?只要输入设备,CPU和输出设备不就够了吗?反正输入设备传入的数据都是要通过CPU来计算的
理由之一:
从上图我们可以看出,距离CPU越近,效率越高,成本越高。所以,如果没有存储器(内存),那么一台电脑的成本就高,而高成本的东西是不利于广泛传播和使用的,所以内存的存在还是非常有必要的
在硬件数据流动角度,在数据层面:
1.CPU不和外设直接打交道,CPU只和内存打交道
2.外设(输入和输出)的数据,不是直接给CPU的,而是先要放在内存中
程序=代码+数据
冯诺依曼体系结构规定,运行程序时,一定要将程序先加载到内存中。那程序没有被运行时在哪里?磁盘
2.操作系统(Operator System / OS)
2.1 概念
操作系统是一款进行软硬件资源管理的软件
广义的认识:操作系统的内核+操作系统的外壳周边程序(给用户提供使用操作系统的方式)
狭义的认识:操作系统的内核
2.2 设计OS的目的
对软硬件资源进行管理(手段)
为用户提供一个良好(稳定,安全,高效)的运行环境(目的)
用户要访问操作系统,必须使用系统调用的方式来访问
计算机管理硬件 :先描述,再组织
- 描述起来,用struct结构体
- 组织起来,用链表或其他高效的数据结构
系统调用和库函数概念 :
1.在开发角度,操作系统对外会表现为一个整体,但是会暴露自己的部分接口,供上层开发使用,这部分 由操作系统提供的接口,叫做系统调用。
2.系统调用在使用上,功能比较基础,对用户的要求相对也比较高,所以,有心的开发者可以对部分系统 调用进行适度封装,从而形成库,有了库,就很有利于更上层用户或者开发者进行二次开发。
3.进程
3.1描述进程-PCB
进程信息被放在一个叫做进程控制块的数据结构中,可以理解为进程属性的集合。
课本上称之为PCB(process control block),PCB是统称,Linux操作系统下的PCB是: task_struct(具体的称呼)
进程=PCB+程序的代码和数据
在Linux下,进程=内核数据结构task_struct+程序的代码和数据
task_struct指向内存中对应的代码和数据
理解一个概念:如何理解进程的动态运行?
只要我们的进程的task_struct将来在不同的队列中,进程就可以访问不同的资源
3.2 进程启动
(A)../XXXX,意思是运行我们自己写的可执行程序,本质就是让系统创建进程并运行
先写myprocess.c和makefile
再运行可执行程序myprocess,就是让系统创建myprocess进程并运行。
我们自己写的代码形成的可执行文件=系统命令。在Linux中运行的大部分执行操作,本质都是运行进程
(B)每个进程都有自己的唯一标识符,叫做进程pid
我们用下面的命令来显示所有的进程,其中,ps:当前系统中所对应的进程;a:all,所有;xj:显示系统的详细信息
我们可以看到,此时系统中存在许多进程,我们如何只显示myprocess进程呢?
我们先修改一下myprocess.c的内容,让他一直循环下去
再启动myprocess进程:./myprocess
再打开另一个窗口,执行下面命令。grep的功能:在文件中搜索字符串,将找到的行打印出来,所以我们找到了该进程。
第一行是我们所找的myprocess进程,第二行是我们使用grep命令所运行的进程,记得吗,我们前面讲的,在Linux中运行的大部分执行操作,本质都是运行进程。
好的,那么只看第一行,这些列都是什么意思呢?为了查看每列的名称,我们使用下面的命令
于是我们看见第二列就是我们进程的唯一标识符pid,我们如何同时显示列名和进程各列的值呢?将2个命令用&&连接,表示2个命令都要执行
如何让grep进程不显示出来呢?用grep -v,反向选择,选出没有grep的行
(C)如何知道进程的pid
使用函数getpid()
先来了解一下getpid函数,用man getpid
我们发现和getpid函数一起出现的还有getppid函数
调用getpid和getppid函数需要引用头文件<sys/types.h>和<unistd.h>,两个函数的返回值都是pid_t类型,其实也就是int类型。不需要传参。getpid函数返回进程的id,getppid返回父进程的id。下面再介绍父进程,先来使用两个函数
修改myprocess.c文件
再来显示进程的全部信息
(D)CTRL+C终止进程,kill -9 pid直接杀掉进程
CTRL+C都知道,我们来试一试kill -9 pid来杀掉进程
先知道如何一直显示进程,用下面命令,表示每隔1秒,显示myprocess进程的信息
- 启动进程./myprocess
- 用上面命令每隔1秒显示myprocess进程的信息,找到pid
- kill -9 pid杀掉进程
结果:
在使用kill命令前,一直存在myprocess进程,用了kill后,myprocess进程不存在了
3.3 进程创建的代码方式
上面我们讲过如何得到pid(getpid)和ppid(getppid),现在我们再来看看它们的关系
我们来启动进程3次,发现每次的pid都不同,这是正常的,但ppid都相同,即父进程都是同一个
那pid为2544的是谁呢?bash(不同电脑的bash的pid可能不同)
上面的myprocess进程的父进程是bash,我们是否可以以myprocess为父进程,创建一个子进程?可以的,使用fork()函数
fork()函数,功能:创建一个子进程。头文件:<unistd.h>,返回值pid_t,即int。
fork之后,父子代码共享
展示一下:先将myprocess.c修改一下。fork()函数之后,如果父子代码共享,那么会打印2次”hello world”
确实会打印2次”hello world”,再查看myprocess进程的情况,发现一开始确实只有1个进程,后来有2个进程,等到程序运行完成,又没有进程了。
再看有2个进程时它们的pid和ppid,发现pid为6074的进程的父进程的pid是6060。所以fork()函数确实是以myprocess为父进程,创建一个子进程
问题:创建一个进程,本质是系统中多一个进程,就是多一个1.task_struct 2.自己的代码和数据
可子进程只有task_struct,代码和数据是哪来的呢?
父进程的代码和数据是从磁盘加载过来的,子进程的代码默认继承父进程的代码,数据我们后面会学:如果子进程没有写入,那父子进程的数据也是共享的,如果父子进程中有一个要对数据进行写入,那么会发生写时拷贝,数据就不共享了
3.4 创建子进程的目的:执行与父进程不同的代码
fork()函数后,父子进程共享,达不到让子进程执行与父进程不同代码的目的,所以还要有其它操作
我们再来看看fork()函数的返回值:
如果fork成功,返回子进程的id给父进程,返回0给子进程。如果失败,不会创建子进程,并返回-1给父进程。
修改.c文件,让父子进程执行不同的代码(看不懂下面的代码没关系,以后会讲到)
3.5 查看进程(用/proc)
查看进程的信息不仅可以用我们之前将的ps命令,也可以通过 /proc 系统文件夹查看。proc会实时的把内存中的进程信息挂接到文件当中,让我们可以以文件的方式查看进程信息
修改.c文件
在/proc中查看该进程
也可以查看该进程的详细信息
现在我们将进程终止,/proc文件就查不到该进程了
我们再来看看进程里面有哪些信息:ls /proc/pid -l
这里面的属性太多了,我们就只讲2个:cwd和exe
exe:表示该进程是由后面这个绝对路径下的程序加载形成的。所以进程的pcb中会记录自己对应的可执行程序的路径
现在在进程运行时,删除myprocess可执行程序,再用/proc查看进程信息,发现exe后面的可执行程序被删掉了,但是进程还能跑
问题:为什么讲可执行程序删掉了,但进程还能跑?
因为一个进程的形成是因为程序被加载到内存中,现在将磁盘中的程序删除,但内存中还有被加载进来的程序,所以进程还能跑。但是如果这个程序太大了,比内存还要大,那么进程可能会出问题
cwd(current work dir):进程的当前工作路径。每个进程在启动的时候,会记录自己当前在哪个路径下启动,这就是进程的当前路径。
当我们使用fopen函数,fopen(“log.txt”,”w”),如果当前路径(cwd)下没有该文件,那就在cwd下新建一个log.txt文件(绝对路径:cwd/log.txt)
现在我们来证明一下
能不能修改cwd呢?可以的,使用chdir函数
先了解一下chdir函数,作用:改变工作路径。如何改变?将新的工作路径作为参数传给chdir函数
先将当前路径下的log.txt文件删除
修改.c文件,当前路径为/home/zy/Linux_code/lesson_17,修改当前路径,改为/home/zy/Linux_code
运行程序
当前路径确实被改成了/home/zy/Linux_code
再看看/home/zy/Linux_code下有没有log.txt文件,有
好了,那么本篇博客就到此结束了,如果你觉得本篇博客对你有些帮助,可以给个大大的赞👍吗,感谢看到这里,我们下篇博客见❤️