僵尸状态和孤儿进程
僵尸状态
当子进程处于僵尸状态时,数据以及代码已经被退出,而保留的是他的pcb,然后要等待父进程来回收,如果父进程未回收,子进程的pcb存在在内存,导致内存泄漏
孤儿进程
父进程先退出,等到子进程退出的时候,没有父进程来回收子进程,这种叫做孤儿进程,而对于孤儿进程会被1号init进程领养
孤儿进程演示
#include<stdio.h> #include<unistd.h> int main() { size_t id=fork(); if(id==0) { while(1) { printf("hello world pid:%d\n",getpid()); sleep(1); } } else { int cnt=5; while(cnt) { printf("hello world pid:%d\n",getpid()); cnt--; sleep(1); } } }
为什么要被领养?
子进程退出,父进程早已经不在,子进程需要被进程回收,所以被领养
进程优先级
1.为什么要有优先级?
是因为cpu的资源是有限的,进程太多,需要通过某种方式竞争资源
2.什么是优先级?
确认谁应该先获得某种资源,谁后获得。
我们可以通过一些数据表明优先级的,而评判优先级是通过调度器
linux下的优先级
ps-la指令可以查看到优先级
优先级=老的优先级+Nice值(修正优先级的值)
调整Nice值
步骤:
1.top
2.r
3.进程号
4.输入你要设置的Nice值(-20至19)操作系统让进程在一定程度下保持均衡
==每次调Nice时,pri都默认为80;优先级的值越小,优先级越高
基本概念
竞争性: 系统进程数目众多,而CPU资源只有少量,甚至1个,所以进程之间是具有竞争属性的。为了高
效完成任务,更合理竞争相关资源,便具有了优先级
独立性: 多进程运行,需要独享各种资源,多进程运行期间互不干扰
并行: 多个进程在多个CPU下分别,同时进行运行,这称之为并行
并发: 多个进程在一个CPU下采用进程切换的方式,在一段时间之内,让多个进程都得以推进,称之为并发
独立性解释:多进程运行,需要独享各种资源,多进程运行期间互不干扰,父进程和子进程互不影响。
时间片:在调度进程时,不是将该进程跑完,而是在cpu上跑几ms,然后cpu再调度别的进程,每个进程的时间片到了,就调度别的进程。
抢占与出让:优先级高的进程会将比他优先级低的正在调度的进程抢占cpu资源,让cpu调度优先级高的进程,尽管优先级低的时间片还没到叫做抢占。
出让:正在调度的进程还没有到时间片,就主动退出,让下一个进程调度,叫做出让。
切换进程
cpu中存在寄存器,当进程A被运行的时候,cpu中的寄存器,保存进程A的临时数据,我们直到当一个函数要返回值的时候,由于该变量是局部变量,出来函数就要被销毁,在销毁前,变量会将他的值保存在寄存器里面,然后返回。寄存器中的临时文件,叫做A的上下文。
上下文可以被丢弃吗??
不可以,当进程A被切换走,会带走他的上下文数据(保存在pcb中),为了下次回来的时候,能恢复上去,从上次执行那里接着执行。
而cpu寄存器只有一份,而上下文有多份,对于不同的进程。
命令行参数
大家在写main函数的时候默认省略了参数
int main(int argc , char*argv[])
main函数的参数可带可不带
这些参数的意义到底是啥??
先写一段代码
#include<stdio.h> #include<unistd.h> int main(int argc, char *argv[]) { for(int i=0;i<argc;i++) { printf("argv[%d]:%s\n",i,argv[i]); } }
char *argv[]是一个指针数组,而argc是指针数组的元素个数,我们通过for循环,打印指针数组里面的内容看看到底是什么
这段代码c99不支持这样写,应该将int i定义在外面。
当我们执行该可执行程序的时候,他会把我们的输入指令转换为字符串,将每段字符串的地址存在数组中
默认在指针数组最后一个是NULL,如何进行验证??
#include<stdio.h> #include<unistd.h> int main(int argc, char *argv[]) {int i; for( i=0;argv[i];i++) { printf("argv[%d]:%s\n",i,argv[i]); } }
将黄色的地方换成了argv[i],当执行这个程序时,当到最后一个指针数组元素时,为NULL,for循环终止,程序不会陷入循环。
1.为什么要有命令行参数??
本质:命令行参数本质是交给我们程序的不同选型,用来定制不同的功能。命令中会携带很多选项,下面的代码就可以解释。
#include<stdio.h> #include<unistd.h> #include<string.h> int main(int argc, char *argv[]) { if(argc!=2) { printf("usage: %s -[a,b,c,d]\n",argv[0]); return 1; } if(strcmp(argv[1],"-a")==0) { printf("功能1\n");} else if(strcmp(argv[1],"-b")==0) { printf("功能2\n"); } else if(strcmp(argv[1],"-c")==0) { printf("功能3\n"); } else if(strcmp(argv[1],"-d")==0) { printf("功能4\n"); } }
代码解释:第一个if语句是判断如果没有带选项,就模拟别的指令那样,让你加选项,没带选项的话,对应的argc!=2,然后分情况判断输入的选项是啥,执行对应的功能,,这样就模拟输入linux指令+对应选项,就会输出对应选项的功能。
2.是谁这样干的?
解答这个问题之前,我们要验证一个东西,就是父进程的数据,默认能被子进程看到并访问(通过代码验证)
#include<stdio.h> #include<unistd.h> #include<string.h> int ret=10000; int main() { printf("i am father pid=%d,ppid=%d\n",getpid(),getppid()); sleep(1); size_t id=fork(); if(id==0) { while(1) { printf("i am child pid=%d,ppid=%d,ret=%d\n",getpid(),getppid(),ret); } }}
fork后的子进程可以访问到父进程全局的变量数据,也就是父进程的数据,默认能被子进程看到并访问
默认输入的指令都是给父进程bash的,然后父进程bash会通过输入的指令字符串,给子程序执行相应的功能
环境变量
当我们执行我们的可执行程序时,我们发现我们的可执行程序要带路径,比如说这个程序res.exe, 我们执行它时,必须要加./来运行,像linux里面的指令,它可以直接运行,不用加./这是为什么呢??
是因为,linux里面有些存在全局的设置,告诉命令行解释器,应该去那些路径下去寻找可执行程序
这里不得不讲PATH这个环境变量,他会记录我们linux指令(相当于程序),所在的路径,而执行相关的命令时,他会在这个环境变量里面存的路径里面找对应的指令(相当于程序)
我们可以使用vim来查看路径下的程序
我们找到了一个认识的kill指令
我们可以讲我们可执行程序的路径,拷贝到/usr/bin下面的话,我们就不用在加路径,而是在PATH环境变量中路径里面找可执行程序
我们可以新建一个.c文件,然后将他的路径拷贝到/usr/bin,我们试一试看在执行的时候需不需要带路径
我们将ui.exe拷贝到/usr/bin路径下,我们发现可以不用加./路径就可以运行。
但是我们默认查到的环境变量时内存级的,这种方法会一直保存
将该可执行程序的路径从环境变量中删除,无法在不加路径运行了
以上这种·方式不建议,用起来可以永久使用,但是会污染环境变量
我们还有一种方法将我们的ui.exe的路径放到PATH环境 变量中去
如果我们采用这种方式的话,会将PATH中的原路径进行覆盖式删除,导致我们之前的指令用不了,比如说ls
我们可以通过echo $PATH来看看
那应该如何添加呢??
这样就可以了
但是这种环境变量还是内存级别的。断开在连接的时候,环境变量中就没有你之前添加的路径了(演示如下)
有什么方法可让x-shell断开在连接时,不会修改我们添加进去的路径,我们可以看到
当x-shell打开时,我们的PATH环境变量会直接添加有些程序的路径,本质上这些环境变量存在于配置文件中,而配置文件在家目录的隐藏目录下
我们可以通过ls-la查看到
我们通过vim打开这两个文件
我们通过vim打开
然后断开连接,然后再次连接就可以了
bash在执行命令的时候,需要先找到命令,因为未来要加载。
再次强调:最开始的环境变量不是在内存中的,而是在系统的配置文件中,而环境变量是在配置文件中
其他的环境变量
1.HOME
该环境变量中保存是对应用户的家目录路径,用户登陆默认在家目录,是因为该环境变量中保存着
2.SHELL
用户使用的shell解释器名称
3.HISTSIZE
保存最近使用指令的个数
我们可以通过history指令查看到最近的1000个指令
4.PWD
该环境变量保存着当前路径,会随着路径切换,不断的变化
关于环境变量相关的指令
env | 可以查看所有的环境变量 |
echo $环境变量名 | 查看对应的环境变量 |
export 环境变量名 | 添加环境变量 |
– | – |
unset 环境变量名 | 取消环境变量 |
我们再次env发现找不到了
代码方式获取环境变量
在头文件unistd.h中存在一个变量 char** environ
bash内部这样组织我们的环境变量
我们通过程序将环境变量打印出来
#include<stdio.h> #include<unistd.h> #include<string.h> int main() { extern char** environ; int i; for(i=0;environ[i];i++) { printf("env[%d]->%s\n",i,environ[i]); } }
环境变量具有系统的全局属性,因为环境变量本身会被子进程继承下去
另一种方式
main函数可以通过传参的方式将环境变量表给子进程
代码如下:
#include<stdio.h> #include<unistd.h> #include<string.h> int main (int argc,char *argv[],char *env[]) { int i; for(i=0;env[i];i++) { printf("env[%d]->%s\n",i,env[i]); } }
当bash进程启动时,默认给两张表,一个命令行参数表,一个环境变量表
命令行参数表来源于用户的命令行,而环境变量表来源于配置文件
第三种方法
:通过getenv
头文件:stdlib.h
内建命令
80%的命令是bash创建子进程执行的,而有一部分指令是bash亲自完成的,叫做内建指令,在之前我们通过将PATH环境变量中的路径覆盖后,有些指令就用不了了,有一些可以用,比如说echo ,export(属于内建命令),可能bash通过创建函数的方式实现内建命令。