为什么要有优先级:就是因为CPU资源有限,进程太多,需要通过某种方式竞争资源;
更改进程的优先级就是更改nice值,在top命令中可以更改进程的优先级:
进入top后按"r" -> 输入进程PID -> 输入nice值
如果系统不允许将进程优先级调高,可以使用sudo top命令;
注:每次设置优先级都要从进程最开始的优先级开始设置,老的优先级基本都是80,也就是PRI(old)都是固定的;
每个进程在运行的时候都有时间片的概念,时间片一到,就让出cpu资源;
操作系统还支持抢占,更高优先级的进程会抢占低优先级的进程的cpu资源,还可以出让cpu资源
环境变量:一般是指在操作系统中用来指定操作系统运行环境的一些参数,通常在系统中具有全局特性;
系统中的bash命令也是存在文件中的,为什么执行bash命令时不用带路径,而执行我们自己的可执行程序时需要带路径:
这是因为bash命令所在的路径在环境变量路径的搜索当中,而自己的程序不在;
执行自己的程序不带路径的方法:
(1)将自己的程序拷贝到系统命令文件中(不推荐);
(2)将程序的路径复制到环境变量维护的路径中;
加入环境变量后,自己的程序就不用带路径了;
PATH:指定命令的搜索路径;
HOME:指定用户的祝工作目录(即用户登陆到Linux系统中时,默认的目录);
SEHLL:当前shell,它的值通常是/bin/bash;
查看环境变量的方法:
echo $NAME //NAME:你的环境变量名称
echo $NAME //NAME:你的环境变量名称
set PATH=$PATH:/home/lmx/linux/lesson0729_process/myproc //冒号后跟想要设置的路径
main函数的前两个参数argc和argv是系统给程序传的命令行参数,argc是参数个数,argv是参数列表;
可以通过这两个参数得到命令后面的参数个数和类型;
#include
#include
int g_val = 10;
int main()
{
pid_t id = fork();
if(id == 0)
{
//child
int cnt = 0;
while(1)
{
printf("I am child, pid: %d, ppid: %d, g_val: %d, &g_val: %p\n", getpid(), getppid(), g_val, &g_val);
sleep(1);
cnt++;
if(cnt == 5)
{
g_val = 200;
printf("child change g_val 100 -> 200 success\n");
}
}
}
else
{
//father
while(1)
{
printf("I am father, pid: %d, ppid: %d, g_val: %d, &g_val: %p\n", getpid(), getppid(), g_val, &g_val);
sleep(1);
}
}
return 0;
}
#include
#include
int g_unval;//未初始化全局变量
int g_val = 100;//已初始化全局变量
int main(int argc, char* argv[], char* env[])
{
printf("code addr: %p\n", main);//打印main函数的地址 - 代码区
printf("init global addr: %p\n", &g_val);//已初始化数据区
printf("uninit global addr: %p\n", &g_unval);//未初始化数据区
char* heap_mem = (char*)malloc(10);
printf("heap addr: %p\n", heap_mem);//堆区地址
printf("stack addr: %p\n", &heap_mem);//栈区地址
int i = 0;
for(i = 0; i < argc; i++)
{
printf("argv[%d]: %p\n", i, argv[i]);
}
for(i = 0; env[i]; i++)
{
printf("env[%d]: %p\n", i, env[i]);
}
return 0;
}
该代码在Linux系统下的运行结果为:
可以看出,验证结果满足进程地址空间的分布;
static修饰变量,变量被开辟到了全局数据区,本质是将局部变量转化为全局变量;
在32位系统下,一个进程的地址空间,取值范围是0x00000000 ~ 0xFFFFFFFF,共4GB内存,其中[0, 3GB]是用户空间,[3GB, 4GB]是内核空间;
以上结论,默认只在Linux系统下有效;
计算机刚开始是直接使用物理地址来进行内存管理的,因为内存本身是能够随时被读写的,但是直接对物理内存进行操作,如果遇到了野指针,可能会导致非法访问,进而导致内存中的代码或数据被改写,因此直接访问物理内存是不安全的;
现代计算机的内存管理是使用虚拟地址的方式,具体形式为:进程直接访问的的地址空间是一段虚拟地址空间,所有的进程的地址都是0x00000000 ~ 0xFFFFFFFF,通过虚拟地址来访问物理地址,中间需要一个映射关系,通过这个映射关系就能将虚拟地址转换为相应的物理地址,进而能够访问物理内存;对于野指针,映射关系对于非法地址是禁止映射的,很好的解决了野指针非法访问物理内存的问题;
(1)进程地址空间实际上是一种内核数据结构
(3)fork函数执行后为什么会有两个返回值
(1)当我们的程序编译完、形成可执行程序,但是还没有被加载到内存中时,我们的程序内部有地址吗?
(2)创建进程时,一定是操作系统先为该进程创建PCB,然后为该进程创建对应的地址空间mm_struct,并在PCB内用指针指向对应的地址空间对象,地址空间内部可以帮我们找到页表和操作系统对应的逻辑,帮我们自动进行虚拟地址到物理地址之间的转化;
(3)为什么要有进程地址空间?
凡是非法的访问或者映射,OS都会识别到,并且终止该进程,所有的进程崩溃,就是进程退出;虚拟地址有效的防止了用户对内存的非法访问,有效的保护了物理内存;
因为地址空间和页表是OS创建并维护的,意味着想要使用地址空间和页表进行映射,也一定在OS的监管之下来进行访问,这样便保护了物理内存中所有的合法数据,包括各个进程,以及内核的相关有效数据;
如果一个进程去修改常量字符串,那就是非法访问代码区,OS会杀掉该进程;
因为有地址空间的存在,有页表的映射的存在,在物理内存中,我们可以对未来的数据进行任意位置的加载,物理内存的分配和进程的管理可以做到没有关系;内存管理模块和进程管理模块就完成了解耦合;
我们在语言层面上申请空间(malloc、new),本质是在虚拟地址空间上申请,计算机会使用延迟分配的策略来提高整机的效率:上层申请空间是在地址空间上申请的,物理内存不会分配空间,在用户真正对物理地址空间进行访问的时候,才执行相关的管理算法,进行申请内存、构建页表映射关系等,然后再让用户进行内存的访问;这一步是由操作系统自动完成的,用户,包括进程都是完全没有感知的;
在物理内存中理论上可以在任意位置加载,但是并不意味着物理内存中的数据和代码就是乱序的,因为有页表的存在,它可以将地址空间上的虚拟地址和物理地址进行映射,在进程的视角,所有的内存分布都是有序的;
地址空间 + 页表可以将内存分布有序化 ,同样也可以实现进程的独立性:因为有地址空间的存在,每一个进程都认为自己拥有4GB空间(32位),并且各个区域都是有序的,进而可以通过页表映射到不同区域,来实现进程的独立性;
每一个进程不知道,也不需要知道其他进程的存在;
进程 = task_struct + mm_struct + 页表 + 分配的内存
(4)进程的挂起