Linux 【进程】
目录
1.冯诺依曼体系结构(硬件结构)
冯诺依曼结构计算机的基本工作原理是什么?
举例说明数据的流动过程
2.操作系统简述
总结:先描述,再组织
3.进程
3.2 通过代码创建子进程:fork
3.3 Linux操作系统进程状态
环境变量
1.冯诺依曼体系结构(硬件结构)
存储器指的是:物理内存
冯诺依曼体系结构中数据输入设备的有:键盘、摄像头、话筒、磁盘(读取)、网卡等
冯诺依曼体系结构中数据输出设备的有:显示器、磁盘、音响、网卡等
中央处理器(CPU)的运算器进行运算的两种情况:算术运算,逻辑运算;
控制器:CPU可以响应外部事件(拷贝数据到内存)
硬件层面上的数据流向
CPU读取数据(数据+代码),都是要从内存中读取。站在数据的角度,我们认为CPU不和外设直接交互。外设(输入或输出设备)要输入或者输出数据,也只能写入内存或者从内存中读取。
冯诺依曼体系结构最典型的特征是所有的外设(输入单元和输出单元)产生的数据,必须将数据写入存储器。存储器本身没有计算能力,CPU通过某种方式来访问存储器,将数据读取进CPU内部,CPU进行运算器运算,控制器控制,最终将计算完的结果写回到内存当中。最后将最终的结果显示到显示器当中。
总结:所有设备都只能直接和内存打交道
冯诺依曼结构计算机的基本工作原理是什么?
计算机就是为了完成指定的数据处理,而通过指令按指定流程完成指定功能,指令的合集就是一段程序。计算机就是按照指定的指令执行流程完成对指定数据的处理
简写为:存储程序、程序控制
举例说明数据的流动过程
由于程序已经在内存中运行,当我们发送消息时,通过键盘输入(输入设备),输入的数据放到内存,cpu经过计算回写到存储器,存储器将数据定期刷新出去,此时的输出设备叫做网卡。
接收消息的电脑接受数据,此时的输入设备是网卡,网卡收到的数据放到内存里,然后经过cpu运算,把处理完的结果再写回存储器里,存储器定期将数据刷新到输出设备,此时的输出设备是显示器。
总结:冯诺依曼结构规定了计算机硬件体系结构走向,如果数据流向不清楚,想象一下硬件体系结构是什么样子,程序的数据流走向也就清楚了(硬件决定了走向,例如胳膊只能向上弯)
2.操作系统简述
操作系统是“搞管理”的软件,与硬件交互,管理所有的软硬件资源,为用户程序(应用程序)提供一个良好的执行环境,操作系统包括:
1.内核,也就是操作系统(进程管理,内存管理,文件管理,驱动管理)
2.其他程序(例如函数库,shell程序,接口,库等等)
总结:先描述,再组织
如何管理软硬件?管理者(操作系统)拿到被管理者(硬件)的核心数据,来进行管理决策才最重要的,先对被管理对象进行描述,在根据描述类型,定义对象,可以把对象组织成数组或者其他数据结构。也就是对数据的管理工作,变成了对数组或者其他数据结构的增删改查
管理:是对被管理对象数据的管理
1. 描述起来,用struct结构体 2. 组织起来,用链表或其他高效的数据结构
系统调用和库函数
操作系统会展开部分接口,供开发使用,由操作系统提供的接口,叫做系统调用。
对系统调用进行适度封装,形成库,更加方便开发者或者用户使用
3.进程
管理大量进程:先描述,再组织(描述:Linux内核中操作系统会为每个进程申请一个结构体:PCB)
该结构体保存了对应进程所有的属性,方便操作系统管理进程。组织:对进程的管理,变成了对进程PCB结构体管理(增删查改),等于对数据结构管理(相当于学生管理系统,学生在学校不代表就是该校学生,配合管理系统信息才能确定)
总结:进程=对应的代码和数据+进程对应PCB结构体
PCB(进程控制块 process control block)
Linux系统中PCB名称:struct task_struct{};
查看进程状态:ps ajx | head -1 && ps axj | grep 'mytest'
写一个死循环,查看mytest状态
PID:进程ID,代表当前进程的ID值
grep --color =auto mytest:最后一个是grep进程,查找mytest,grep中包含该关键字,通常一起显示
ls /proc:把所有内存级进程数据以文件形式显示出来(目录文件是动态的)
![](https://img-blog.csdnimg.cn/1b4b7dac59684ab5aecb5497c49faa85.png)
获取当前进程pid的系统调用函数:getpid
其中pid_t是操作系统提供的属性类型,实际是一个无符号整数
知道pid,还可以使用kill命令终止指令:kill -9 pid
获取当前进程的父进程的系统调用函数:getppid
查看当前进程父进程可以发现:ppid是bash,就是shell命令行,外壳程序
在命令行中运行命令或者程序时,父进程永远都是bash(执行命令时,所有的命令都是以子进程的形式去运行,shell以创建子进程的方式运行一段命令或程序)
3.2 通过代码创建子进程:fork
fork有两个返回值:
1.失败的时候返回-1
2.成功时 a.给父进程返回子进程pid b.给子进程返回0
可以看到,printf只是一条语句,却被打印了两次
fork之后,变为两个进程,一个是父进程(自己),一个是子进程
当前进程21155,父进程返回子进程pid21156;子进程返回0
fork用法
由于fork之后代码是父子共享,通常要用 if 进行分流(id值不同,父子进程执行不同的代码)
3.3 Linux操作系统进程状态
OS理论状态:
新建:初步具备进程相关数据结构,没有入队列中时叫做新建状态,字面意思
运行:进程的task_struct结构体在CPU运行队列中排队等待调度,就叫做运行态。
阻塞:等待非CPU资源就绪时(例如网卡硬盘传输,或者scanf时等待键盘输入),就叫做阻塞状态
挂起:和进程访问某种资源关系不大,当内存不足时,操作系统将长时间不执行的进程的代码和数据,通过适当的置换到磁盘中,此时进程的状态就叫做挂起
OS实际进程状态
一个进程可以有几个状态(在 Linux内核里,进程有时候也叫做任务)
R 运行状态(running):
并不意味着进程一定在运行中,它表明进程要么是在运行中要么在运行队列里
S 睡眠状态(sleeping):
意味着进程在等待事件(某种资源,类似阻塞状态)完成(这里的睡眠有时候也叫做可中断睡眠 (interruptible sleep))
while1循环时,我们看到死循环进程状态是R+;while1循环内部printf时,我们看到死循环进程状态是S+;和R状态不同的原因是:
在cpu看来运行printf时间只需要纳秒级别,其实有运行状态,只不过显示器在冯诺依曼体系结构中处于外设,进程向显示器打印时,printf的PCB放入CPU时间比运行队列速度快,导致看到的大部分是S状态;
当压力过大内存严重不足时,操作系统可能会通过一定手段干掉一些进程(S睡眠状态),起到节省空间的作用
可中断睡眠
改变sleep状态,可以发送信号来更改状态
状态后带+
带+:意味任务处于前台进程(前台进程一启动,执行其他命令失效,可以被ctrl+c终止,占用bash对话框)
不带+:需要后台运行时,在执行时加 &即可,随后显示后台进程pid,不可被ctrl+c终止,bash可以使用
D 磁盘休眠状态(Disk sleep)
有时候也叫不可中断睡眠状态(uninterruptible sleep),在这个状态的进程不可被被动唤醒,通常会等待IO的结束。
当压力过大内存严重不足时,操作系统无法干掉D状态
T暂停/调试状态(stopped/tracing stop):
可以通过发送 SIGSTOP 19号信号给进程来停止(T)进程。
被暂停的进程可以通过发送 SIGCONT 18号信号让进程继续运行。kill -18 pid
打断点停下调试==进程暂停
X死亡状态(dead):
这个状态瞬时性很强,通常只是一个返回状态,你不会在任务列表里看到这个状态。
Z(zombie)-僵尸进程
是什么:一个进程已经退出,但是还不允许被OS释放其所属资源,处于一个被检测的状态(进程状态检测一般是父进程或者操作系统检测)
当进程退出并且父进程没有读取到子进程退出的返回代码时就会产生僵尸进程,僵尸进程会以终止状态保持在进程表中,并且会一直在等待父进程读取退出状态代码。 父进程还在运行,但父进程没有读取子进程状态,子进程进入Z状态
为什么要维持该状态:为了让父进程和OS回收(最终Z-->X)
僵尸进程危害:不被任何进程读取,也不会和外设交互,但是其创建的PCB进程基本信息一直存在,造成内存资源浪费(内存泄漏)
循环监测mytest进程状态:
while :; do ps axj | head -1 && ps axj | grep mytest | grep -v grep; sleep 1; echo "#############################################"; done
#include <stdio.h>
2 #include <stdlib.h>
3 #include <unistd.h>
4 int main()
5 {
6 pid_t id = fork();
7 if(id < 0)
8 {
9 perror("fork");
10 return 1;
11 }
12 else if(id > 0)
13 { //parent
14 while(1)
15 {
16
17 printf("I am parent,pid%d,ppid%d\n", getpid(),getp pid());
18 sleep(3);
19 }
20 }
21 else
22 {
23 //child
24 while(1)
25 {
26 printf("I am child,pid :%d, ppid %d\n", getpid(),get ppid());
27 sleep(3);
28 exit(-1);
29 }
30 }
31 return 0;
32 }
孤儿进程
父进程提前退出,子进程就称之为“孤儿进程”,孤儿进程此时被1号init进程(系统本身)领养,init进程进行回收
如果父进程退出,此时子进程从前台进程转化为后台进程,使用kill -9即可(照常输入)
杀掉子进程后进入Z状态,直接被操作系统回收
环境变量
main函数最多有三个参数(int argc,char* argv[],char* env[]),前两个是命令行参数(启动程序时,传入的参数选项,意义是同样一个参数,通过传入不同的选项,使用同一个程序的不同子功能),最后一个是环境变量参数(每一个进程启动时,启动该进程的进程传递给main函数的环境变量信息,都可以以该参数传导)
![](https://img-blog.csdnimg.cn/ae2a44e7b2854ae1995404783b7e9855.png)
![](https://img-blog.csdnimg.cn/c329d89cad554c0a8920ff4cb3bc2963.png)
环境变量的参数一般是由父进程继承导入,默认所有的环境变量都会被子进程继承