大家好我是程序员小青蛙今天介绍进程状态和环境变量欢迎大家学习。一、进程状态的本质进程状态的核心逻辑进程的不同状态本质是进程被放在不同的队列中等待不同的资源运行队列一个 CPU 对应一个运行队列struct runqueue进程进入运行队列本质是将该进程的task_struct结构体对象放入队列R运行状态只要进程的 PCB 在运行队列中就是 R 状态不代表进程一定正在 CPU 上运行等待队列阻塞队列每个外设键盘、显示器、网卡、磁盘等都有自己的等待队列进程需要等待某个资源时会从运行队列移出放入对应外设的等待队列资源就绪后进程会被唤醒重新放回运行队列挂起状态当内存空间不足时操作系统会将暂时不运行的进程的代码和数据保存到磁盘释放这部分内存给其他进程使用进程需要运行时再将代码和数据从磁盘加载回内存二、Linux 内核定义的 7 种进程状态cstatic 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 */ };Rrunning运行状态进程在运行队列中Ssleeping可中断睡眠浅度睡眠等待事件完成可被信号唤醒Ddisk sleep不可中断睡眠深度睡眠等待磁盘 IO 结束无法被 OS 杀死只能等待 IO 完成或断电Tstopped停止状态可通过SIGSTOP信号暂停SIGCONT信号恢复ttracing stop追踪停止状态进程被调试器暂停如 gdbXdead死亡状态进程已彻底结束PCB 已被回收任务列表中不可见Zzombie僵尸状态进程已退出但父进程未回收其 PCB进程状态的查看ps aux / ps axj命令三、僵尸进程与孤儿进程1. 僵尸进程Z成因子进程退出后父进程没有读取子进程的退出返回代码特点进程以终止状态保持在进程表中一直等待父进程读取退出状态危害PCBtask_struct会一直占用内存造成内存泄漏解决父进程调用wait()系统调用回收或父进程退出后由 init 进程回收编写一个进程查看僵尸进程#include stdio.h #include stdlib.h int main() { pid_t id fork(); if(id 0){ perror(fork); return 1; } else if(id 0){ //parent printf(parent[%d] is sleeping...\n, getpid()); sleep(30); }else{ printf(child[%d] is begin Z...\n, getpid()); sleep(5); exit(EXIT_SUCCESS); } return 0; }可以看到都是处在S状态休眠状态最后处在Z状态属于僵死状态子进程Z状态Z僵尸进程状态进程已经执行完毕退出但 PCB进程控制块还没被父进程回收同样表示前台进程关键标记defunct英文意为 “失效的 / 死亡的”在 Linux 中专门表示僵尸进程僵尸进程是已经执行完毕退出但父进程没有调用wait()/waitpid()回收其退出状态的子进程。进程的代码和数据已经被释放但 PCBtask_struct仍然留在系统中等待父进程读取退出码。尸进程不占用 CPU 和内存数据段但会永久占用 PCB 结构体存放在内存中如果大量产生僵尸进程会占用内核进程表资源导致系统无法创建新进程PID耗尽解决方法父进程调用wait()/waitpid()回收子进程if(id 0) { printf(parent[%d] is sleeping...\n, getpid()); wait(NULL); // 阻塞回收子进程避免僵尸进程 sleep(30); }父进程退出父进程结束后子进程会被init进程PID 1领养并自动回收信号处理父进程通过SIGCHLD信号异步回收子进程2. 孤儿进程成因父进程先退出子进程还在运行处理子进程会被1 号 init 进程领养由 init 进程负责回收无危害父进程先退出子进程还在运行这个子进程就叫孤儿进程。核心机制父进程死了子进程还活着操作系统会把孤儿进程过继给 1 号进程init/systemd由 1 号进程负责回收子进程不会产生僵尸#include stdio.h #include unistd.h #include stdlib.h int main() { pid_t id fork(); if(id 0){ perror(fork); return 1; } else if(id 0){//child printf(I am child, pid : %d\n, getpid()); sleep(10); }else{//parent printf(I am parent, pid: %d\n, getpid()); sleep(3); exit(0); } return 0; }父进程已死子进程还在称为孤儿进程孤儿进程会被回收不会造成内存泄露四、进程优先级UID :代表执行者的身份PID :代表这个进程的代号PPID代表这个进程是由哪个进程发展衍生而来的亦即父进程的代号PRI代表这个进程可被执行的优先级其值越小越早被执行NI代表这个进程的nice值基本概念CPU 资源分配的先后顺序优先级高的进程优先执行PRI 与 NIPRI进程的优先级值越小优先级越高NInice 值优先级的修正值范围-20 ~ 19计算公式PRI(new) PRI(old) nicenice 为负值 → PRI 变小 → 优先级提高需要强调一点的是进程的nice值不是进程的优先级他们不是一个概念但是进程nice值会影响到进程的优先级变化。可以理解nice值是进程优先级的修正修正数据查看与修改查看ps -l命令PRI列和NI列修改top命令 → 按r→ 输入进程 PID → 输入 nice 值本质优先级本质是 PCBtask_struct里面的一个整数字段竞争性:系统进程数目众多而CPU资源只有少量甚至1个所以进程之间是具有竞争属性的。为了高效完成任务更合理竞争相关资源便具有了优先级独立性:多进程运行需要独享各种资源多进程运行期间互不干扰并行:多个进程在多个CPU下分别同时进行运行这称之为并行并发:多个进程在一个CPU下采用进程切换的方式在一段时间之内让多个进程都得以推进称之为并发五、进程切换CPU 寄存器CPU 内部只有一套寄存器硬件被所有进程共享寄存器中保存的数据只属于当前正在运行的进程上下文保护与恢复进程切换时需要保存当前进程的上下文数据寄存器中的数据到 PCB 中然后恢复下一个进程的上下文数据从 PCB 加载到 CPU 寄存器类比离开学校时保留学籍回来时恢复学籍上下文就是进程的 学籍六、环境变量基本概念操作系统中用来指定运行环境的一些参数具有全局特性常见环境变量PATH指定命令的搜索路径HOME指定用户的主工作目录SHELL当前使用的 Shell通常是/bin/bash常用命令echo $NAME显示某个环境变量的值export NAMEvalue设置并导出一个新的环境变量env显示所有环境变量unset NAME清除环境变量set显示本地定义的 shell 变量和环境变量代码获取方式main 函数第三个参数int main(int argc, char *argv[], char *env[])全局变量extern char **environ系统调用getenv(NAME)、setenv(NAME, value, 1)核心特性环境变量可以被子进程继承普通变量不使用export导出则无法被子进程继承查看命令设置一个新的环境变量使用set可以发现pwd是咋样的找到之前设置的环境变量这时候我们可以将我们写的文件配置成环境变量就可以不用进行指令执行了比如我们写好一个程序并且编译完成后需要指令./进行执行才能完成运行下面我们将hello文件设置为环境变量然后可以完成不需要指令就可以运行下面是执行步骤export PATH$PATH:/home/zzy/lesson//后面这里是你当前所在的路径如何取消输入取消export PATH$(echo $PATH | sed s#:/home/zzy/lesson##g)也可以重置PATHPATH/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin解释一下通过代码如何获取环境变量int main(int argc, char *argv[], char *env[])在main 函数中定义了环境变量当进入时自动调用环境变量命令行第三个参数#include stdio.h int main(int argc, char *argv[], char *env[]) { int i 0; for(; env[i]; i){ printf(%s\n, env[i]); } return 0; }通过第三方变量environ获取#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; }通过系统调用进行获取和设置环境变量#include stdio.h #include stdlib.h int main() { printf(%s\n, getenv(PATH)); return 0; }环境变量通常是具有全局属性的环境变量通常具有全局属性可以被子进程继承下去#include stdio.h #include stdlib.h int main() { char * env getenv(MYENV); if(env){ printf(%s\n, env); } return 0; }直接查看发现没有结果说明该环境变量根本不存在解决导出环境变量export MYENVhello world再次运行程序发现结果有了说明环境变量是可以被子进程继承下去的想想为什么说明父进程设置的环境变量被子进程继承了父进程当前的bash终端进程子进程你运行的./test程序进程解答环境变量是父进程地址空间的一部分fork()创建子进程时会完整复制因此可以被子进程继承。如果只进行MYENV“helloworld”,不调用export导出在用我们的程序查看会有什么结果为什么程序中调用getenv(MYENV)会返回 NULL也就是什么也查不到不会输出helloworld。核心原因是MYENVhelloworld定义的只是 Shell 的「局部变量」不是环境变量不会被子进程继承。不加export只有shell自己用不会传给子进程。谢谢大家观看有不对的地方评论指出
【linux学习】linux下进程状态和环境变量的解析
大家好我是程序员小青蛙今天介绍进程状态和环境变量欢迎大家学习。一、进程状态的本质进程状态的核心逻辑进程的不同状态本质是进程被放在不同的队列中等待不同的资源运行队列一个 CPU 对应一个运行队列struct runqueue进程进入运行队列本质是将该进程的task_struct结构体对象放入队列R运行状态只要进程的 PCB 在运行队列中就是 R 状态不代表进程一定正在 CPU 上运行等待队列阻塞队列每个外设键盘、显示器、网卡、磁盘等都有自己的等待队列进程需要等待某个资源时会从运行队列移出放入对应外设的等待队列资源就绪后进程会被唤醒重新放回运行队列挂起状态当内存空间不足时操作系统会将暂时不运行的进程的代码和数据保存到磁盘释放这部分内存给其他进程使用进程需要运行时再将代码和数据从磁盘加载回内存二、Linux 内核定义的 7 种进程状态cstatic 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 */ };Rrunning运行状态进程在运行队列中Ssleeping可中断睡眠浅度睡眠等待事件完成可被信号唤醒Ddisk sleep不可中断睡眠深度睡眠等待磁盘 IO 结束无法被 OS 杀死只能等待 IO 完成或断电Tstopped停止状态可通过SIGSTOP信号暂停SIGCONT信号恢复ttracing stop追踪停止状态进程被调试器暂停如 gdbXdead死亡状态进程已彻底结束PCB 已被回收任务列表中不可见Zzombie僵尸状态进程已退出但父进程未回收其 PCB进程状态的查看ps aux / ps axj命令三、僵尸进程与孤儿进程1. 僵尸进程Z成因子进程退出后父进程没有读取子进程的退出返回代码特点进程以终止状态保持在进程表中一直等待父进程读取退出状态危害PCBtask_struct会一直占用内存造成内存泄漏解决父进程调用wait()系统调用回收或父进程退出后由 init 进程回收编写一个进程查看僵尸进程#include stdio.h #include stdlib.h int main() { pid_t id fork(); if(id 0){ perror(fork); return 1; } else if(id 0){ //parent printf(parent[%d] is sleeping...\n, getpid()); sleep(30); }else{ printf(child[%d] is begin Z...\n, getpid()); sleep(5); exit(EXIT_SUCCESS); } return 0; }可以看到都是处在S状态休眠状态最后处在Z状态属于僵死状态子进程Z状态Z僵尸进程状态进程已经执行完毕退出但 PCB进程控制块还没被父进程回收同样表示前台进程关键标记defunct英文意为 “失效的 / 死亡的”在 Linux 中专门表示僵尸进程僵尸进程是已经执行完毕退出但父进程没有调用wait()/waitpid()回收其退出状态的子进程。进程的代码和数据已经被释放但 PCBtask_struct仍然留在系统中等待父进程读取退出码。尸进程不占用 CPU 和内存数据段但会永久占用 PCB 结构体存放在内存中如果大量产生僵尸进程会占用内核进程表资源导致系统无法创建新进程PID耗尽解决方法父进程调用wait()/waitpid()回收子进程if(id 0) { printf(parent[%d] is sleeping...\n, getpid()); wait(NULL); // 阻塞回收子进程避免僵尸进程 sleep(30); }父进程退出父进程结束后子进程会被init进程PID 1领养并自动回收信号处理父进程通过SIGCHLD信号异步回收子进程2. 孤儿进程成因父进程先退出子进程还在运行处理子进程会被1 号 init 进程领养由 init 进程负责回收无危害父进程先退出子进程还在运行这个子进程就叫孤儿进程。核心机制父进程死了子进程还活着操作系统会把孤儿进程过继给 1 号进程init/systemd由 1 号进程负责回收子进程不会产生僵尸#include stdio.h #include unistd.h #include stdlib.h int main() { pid_t id fork(); if(id 0){ perror(fork); return 1; } else if(id 0){//child printf(I am child, pid : %d\n, getpid()); sleep(10); }else{//parent printf(I am parent, pid: %d\n, getpid()); sleep(3); exit(0); } return 0; }父进程已死子进程还在称为孤儿进程孤儿进程会被回收不会造成内存泄露四、进程优先级UID :代表执行者的身份PID :代表这个进程的代号PPID代表这个进程是由哪个进程发展衍生而来的亦即父进程的代号PRI代表这个进程可被执行的优先级其值越小越早被执行NI代表这个进程的nice值基本概念CPU 资源分配的先后顺序优先级高的进程优先执行PRI 与 NIPRI进程的优先级值越小优先级越高NInice 值优先级的修正值范围-20 ~ 19计算公式PRI(new) PRI(old) nicenice 为负值 → PRI 变小 → 优先级提高需要强调一点的是进程的nice值不是进程的优先级他们不是一个概念但是进程nice值会影响到进程的优先级变化。可以理解nice值是进程优先级的修正修正数据查看与修改查看ps -l命令PRI列和NI列修改top命令 → 按r→ 输入进程 PID → 输入 nice 值本质优先级本质是 PCBtask_struct里面的一个整数字段竞争性:系统进程数目众多而CPU资源只有少量甚至1个所以进程之间是具有竞争属性的。为了高效完成任务更合理竞争相关资源便具有了优先级独立性:多进程运行需要独享各种资源多进程运行期间互不干扰并行:多个进程在多个CPU下分别同时进行运行这称之为并行并发:多个进程在一个CPU下采用进程切换的方式在一段时间之内让多个进程都得以推进称之为并发五、进程切换CPU 寄存器CPU 内部只有一套寄存器硬件被所有进程共享寄存器中保存的数据只属于当前正在运行的进程上下文保护与恢复进程切换时需要保存当前进程的上下文数据寄存器中的数据到 PCB 中然后恢复下一个进程的上下文数据从 PCB 加载到 CPU 寄存器类比离开学校时保留学籍回来时恢复学籍上下文就是进程的 学籍六、环境变量基本概念操作系统中用来指定运行环境的一些参数具有全局特性常见环境变量PATH指定命令的搜索路径HOME指定用户的主工作目录SHELL当前使用的 Shell通常是/bin/bash常用命令echo $NAME显示某个环境变量的值export NAMEvalue设置并导出一个新的环境变量env显示所有环境变量unset NAME清除环境变量set显示本地定义的 shell 变量和环境变量代码获取方式main 函数第三个参数int main(int argc, char *argv[], char *env[])全局变量extern char **environ系统调用getenv(NAME)、setenv(NAME, value, 1)核心特性环境变量可以被子进程继承普通变量不使用export导出则无法被子进程继承查看命令设置一个新的环境变量使用set可以发现pwd是咋样的找到之前设置的环境变量这时候我们可以将我们写的文件配置成环境变量就可以不用进行指令执行了比如我们写好一个程序并且编译完成后需要指令./进行执行才能完成运行下面我们将hello文件设置为环境变量然后可以完成不需要指令就可以运行下面是执行步骤export PATH$PATH:/home/zzy/lesson//后面这里是你当前所在的路径如何取消输入取消export PATH$(echo $PATH | sed s#:/home/zzy/lesson##g)也可以重置PATHPATH/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin解释一下通过代码如何获取环境变量int main(int argc, char *argv[], char *env[])在main 函数中定义了环境变量当进入时自动调用环境变量命令行第三个参数#include stdio.h int main(int argc, char *argv[], char *env[]) { int i 0; for(; env[i]; i){ printf(%s\n, env[i]); } return 0; }通过第三方变量environ获取#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; }通过系统调用进行获取和设置环境变量#include stdio.h #include stdlib.h int main() { printf(%s\n, getenv(PATH)); return 0; }环境变量通常是具有全局属性的环境变量通常具有全局属性可以被子进程继承下去#include stdio.h #include stdlib.h int main() { char * env getenv(MYENV); if(env){ printf(%s\n, env); } return 0; }直接查看发现没有结果说明该环境变量根本不存在解决导出环境变量export MYENVhello world再次运行程序发现结果有了说明环境变量是可以被子进程继承下去的想想为什么说明父进程设置的环境变量被子进程继承了父进程当前的bash终端进程子进程你运行的./test程序进程解答环境变量是父进程地址空间的一部分fork()创建子进程时会完整复制因此可以被子进程继承。如果只进行MYENV“helloworld”,不调用export导出在用我们的程序查看会有什么结果为什么程序中调用getenv(MYENV)会返回 NULL也就是什么也查不到不会输出helloworld。核心原因是MYENVhelloworld定义的只是 Shell 的「局部变量」不是环境变量不会被子进程继承。不加export只有shell自己用不会传给子进程。谢谢大家观看有不对的地方评论指出