系列文章目录
文章目录
前言
进程是操作系统分配资源的最小单位,linux系统上的进程是如何表示和操作的呢?
一、冯诺依曼体系结构
大部分的计算机硬件体系都是冯诺依曼体系。
- 输入、输出设备:统称外设,有键盘、话筒、摄像头、网卡、磁盘等
- 存储器:这里就是指内存
- *处理器:运算器和控制器
1、内存的意义
所有设备都只能直接和内存打交道。
这样做的好处是将需要等待的数据存储到内存中,读取数据就是内存的速度,提升了计算机整体的运行速度。
2、数据流
即使是些简单操作,都用到每一部分。
QQ中传递文件:输入:磁盘、输出:网卡 、输入:网卡、输出:磁盘
QQ中聊天:输入:键盘、输出:网卡 、输入:网卡、输出:显示器
二、操作系统
任何计算机系统都包含一个基本的程序集合,称为操作系统(OS)。
操作系统包括:
- 内核(进程管理,内存管理,文件管理,驱动管理)
- 其他程序(例如函数库,shell程序等等)
1、操作系统的目的
- 与硬件交互,管理所有的软硬件资源
- 为用户程序(应用程序)提供一个良好的运行环境
-
硬件部分遵守冯诺依曼体系
-
OS不信任任何用户,任何对系统硬件或者软件访问,都必须通过OS的手
-
计算机体系是一个层状结构,任何访问硬件或者软件的行为,都必须通过OS接口,贯穿OS进行访问
-
库函数:语言或者第三方库(第一方:系统的、第二方:自己的,其余是第三方的)给我们提供的接口
-
系统调用:OS提供的接口
总结:
- 计算机管理硬件:描述起来,用struct结构体组织起来,用链表或其他高效的数据结构
- 操作系统是进行软硬件资源管理的软件(其中管理的本质是先描述在组织(是对数据的管理)
- 管理分为三种:管理者、执行者、被管理者(eg管理者为OS、执行者为驱动程序、被管理者为底层硬件)
三、进程
1、基本概念
课本概念:程序的一个执行实例,正在执行的程序等
内核观点:担当分配系统资源(CPU时间,内存)的实体。
操作系统:内核关于进程的数据结构(PCB) + 当前进程的代码和数据
描述进程-PCB
- 进程信息被放在一个叫做进程控制块的数据结构中,可以理解为进程属性的集合。称之为PCB(process control block),Linux操作系统下的PCB是: task_struct 。task_struct是PCB的一种
- 在Linux中描述进程的结构体叫做task_struct。
- task_struct是Linux内核的一种数据结构,它会被装载到RAM(内存)里并且包含着进程的信息
task_ struct内容分类
- 标示符: 描述本进程的唯一标示符,用来区别其他进程。
- 状态: 任务状态,退出代码,退出信号等。
- 优先级: 相对于其他进程的优先级。
- 程序计数器: 程序中即将被执行的下一条指令的地址。
- 内存指针: 包括程序代码和进程相关数据的指针,还有和其他进程共享的内存块的指针
- 上下文数据: 进程执行时处理器的寄存器中的数据
- I/O状态信息: 包括显示的I/O请求,分配给进程的I/O设备和被进程使用的文件列表。
- 记账信息: 可能包括处理器时间总和,使用的时钟数总和,时间限制,记账号等。
- 其他信息
2、查看进程
ps axj
//查看进程
ps axj | head -1 && ps axj | grep "test"
//带标题栏和过滤带有“test"的进程
top
//查看进程占资源情况
ls /proc
ls /porc/xxx -al
//这些目录保存了当前系统中运行的所有进程的信息
3、通过系统调用创建进程-fork
1、创建子进程
- fork有两个返回值的原因是在创建子进程成功之后,子进程和父进程共享代码
- fork:子进程的返回值是0,父进程返回值是子进程的pid,因为子进程只有一个父进程,而父进程有多个子进程,需要对每个子进程进行标识(pid),并且记住他们
- 父子进程代码共享,数据各自开辟空间,私有一份(采用写时拷贝)
四、进程状态
1、linux内核源码
/*
* The task state array is a strange "bitmap" of
* reasons to sleep. Thus "running" is zero, and
* you can test for combinations of others with
* simple bit tests.
*/
static const char * const task_state_array[] = {
"R (running)", /* 0 */
"S (sleeping)", /* 1 */
"D (disk sleep)", /* 2 */
"T (stopped)", /* 4 */
"t (tracing stop)", /* 8 */
"X (dead)", /* 16 */
"Z (zombie)", /* 32 */
};
- R运行状态(running): 并不意味着进程一定在运行中,它表明进程要么是在运行中要么在运行队列里。
- S睡眠状态(sleeping): 意味着进程在等待事件完成(这里的睡眠有时候也叫做可中断睡眠
- D磁盘休眠状态(Disk sleep)有时候也叫不可中断睡眠状态(uninterruptible sleep),在这个状态的进程通常会等待IO的结束。
- T停止状态(stopped): 可以通过发送 SIGSTOP 信号给进程来停止(T)进程。这个被暂停的进程可以通过发送 SIGCONT 信号让进程继续运行。
- X死亡状态(dead):这个状态只是一个返回状态,你不会在任务列表里看到这个状态。
- Z(zombie)-僵尸进程:
2、僵尸进程
- 僵死状态(Zombies)是一个比较特殊的状态。当进程退出并且父进程(使用wait()系统调用)没有读取到子进程退出的返回代码时就会产生僵死(尸)进程。
- 僵死进程会以终止状态保持在进程表中,并且会一直在等待父进程读取退出状态代码。
- 只要子进程退出,父进程还在运行,但父进程没有读取子进程状态,子进程进入Z状态。
3、孤儿进程
父进程先退出,子进程就称之为“孤儿进程”
五、进程优先级
进程的优先级:
- cpu资源分配的先后顺序,就是指进程的优先权(priority)。
- 优先权高的进程有优先执行权利。配置进程优先权对多任务环境的linux很有用,可以改善系统性能。
- 还可以把进程运行到指定的CPU上,这样一来,把不重要的进程安排到某个CPU,可以大大改善系统整体性能
- 优先级是在一定能得到某种资源,只是先后的问题
- 权限是决定你能还是不能得到某种资源
- 优先级是得到某种资源(CPU)的先后顺序,其本质是因为资源有限(CPU
1、查看进程优先级
- UID : 代表执行者的身份
- PID : 代表这个进程的代号
- PPID :代表这个进程是由哪个进程发展衍生而来的,亦即父进程的代号
- PRI :代表这个进程可被执行的优先级,其值越小越早被执行
- NI :代表这个进程的nice值
- Linux的优先级由pri和nice值共同确定(优先级的数值越小,优先级越高;优先级的数值越大,优先级越低)
- nice值就是优先级的修正数据,范围是[-20,19]
- 优先级不可能一味的高,也不可能一味的低(操作系统的调度器要适度地考虑平衡问题,避免“饥饿问题”)
2、设置进程优先级
- PRI是进程的优先级,或者通俗点说就是程序被CPU执行的先后顺序,此值越小
进程的优先级别越高 - NI就是nice值了,其表示进程可被执行的优先级的修正数值
- PRI值越小越快被执行,PRI(new)=PRI(old)+nice
- 当nice值为负值的时候,那么该程序将会优先级值将变小,即其优先级会变高,则其越快被执行
- 调整进程优先级,在Linux下,就是调整进程nice值
- nice其取值范围是-20至19,一共40个级别。
- 进程的nice值不是进程的优先级,但是进程nice值会影响到进程的优先级变化。
- nice值是进程优先级的修正修正数据
- 竞争性: 系统进程数目众多,而CPU资源只有少量,甚至1个,所以进程之间是具有竞争属性的。为了高效完成任务,更合理竞争相关资源,便具有了优先级
- 独立性: 多进程运行,需要独享各种资源,多进程运行期间互不干扰
- 并行: 多个进程在多个CPU下分别,同时进行运行,这称之为并行
- 并发: 多个进程在一个CPU下采用进程切换的方式,在一段时间之内,让多个进程都得以推进,称之为并发
六、环境变量与命令行参数
1、基本概念
环境变量(environment variables)一般是指在操作系统中用来指定操作系统运行环境的一些参数。如:我们在编写C/C++代码的时候,在链接的时候,从来不知道我们的所链接的动态静态库在哪里,但是照样可以链接成功,生成可执行程序,原因就是有相关环境变量帮助编译器进行查找。
环境变量通常具有某些特殊用途,还有在系统当中通常具有全局特性
- PATH : 指定命令的搜索路径
- HOME : 指定用户的主工作目录(即用户登陆到Linux系统中时,默认的目录)
- SHELL : 当前Shell,它的值通常是/bin/bash。
- 查看环境变量方法:echo $NAME //NAME:你的环境变量名称
2、查看环境变量
3、环境变量通常是具有全局属性的
命令行中一般有两个变量:本地变量、环境变量
本地变量:只能够在当前shell命令行解释器内被访问,不可以被子进程继承
环境变量:具有”全局属性“ 可以被子进程继承
- echo: 显示某个环境变量值
- export: 设置一个新的环境变量
- env: 显示所有环境变量
- unset: 清除环境变量
- set: 显示本地定义的shell变量和环境变量
4、命令行参数
- 命令行参数可以帮助我们设计出,在同一个程序中可以设计出不同的业务功能
- argv指针数组,其中最后一个元素指向的是NULL
5、环境变量的组织方式及通过代码如何获取环境变量
每个程序都会收到一张环境表,环境表是一个字符指针数组,每个指针指向一个以’\0’结尾的环境字符串
此外还可以用这种方式
#include <stdio.h>
int main(int argc, char *argv[])
{
extern char **environ;
int i = 0;
for(; environ[i]; i++){
printf("%s\n", environ[i]);
}
return 0;
}
七、进程地址空间
1、进程地址空间的分布
- 进程地址空间不是内存地址空间
- 进程地址空间,会在进程的整个生命周期内一直存在,直到进程退出
2、什么是进程地址空间
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int g_val = 0;
int main()
{
pid_t id = fork();
if(id < 0)
{
perror("fork");
return 0;
}
else if(id == 0)
{
//child,子进程肯定先跑完,也就是子进程先修改,完成之后,父进程再读取
g_val=100;
printf("child[%d]: %d : %p\n", getpid(), g_val, &g_val);
}
else
{
//parent
sleep(3);
printf("parent[%d]: %d : %p\n", getpid(), g_val, &g_val);
}
sleep(1);
return 0;
}
child[3046]: 100 : 0x80497e8
parent[3045]: 0 : 0x80497e8
父子进程中的g_val的地址竟然是一样的
- 任何的编程语言里面的地址,绝对不是物理地址,而是虚拟地址(C++/C语言中的&得到的是虚拟地址不是物理地址)
- 虚拟地址是操作系统提供的,数据和代码一定在物理内存上(冯诺依曼规定),因此需要将虚拟内存转化成物理内存(由OS自动完成)
- 父子进程代码共享,而数据是各自私有一份的(写时拷贝)
- 当所有程序运行起来之后,该程序立即变成进程
地址空间本质是进程看待内存的方式,是抽象出来的一个概念,内核struct mm_struct,这样的每一个进程,都认为自己独占系统内存资源
区域划分本质:将线性地址空间划分成为一个一个的area,[start,end]
虚拟地址本质:在[start,end] 之间的各个地址叫做虚拟地址
总结
进程管理是操作系统的重要作用之一。
强烈的信仰会赢取坚强的人,然后又使他们更坚强。——华特贝基霍