ToB企服应用市场:ToB评测及商务社交产业平台

标题: 【在Linux世界中追寻伟大的One Piece】Linux进程概念 [打印本页]

作者: 河曲智叟    时间: 2024-7-26 12:25
标题: 【在Linux世界中追寻伟大的One Piece】Linux进程概念
目次
1 -> 冯诺依曼体系结构
2 -> 操作体系(operator System)
2.1 -> 概念
2.2 -> 体系调用和库函数
3 -> 进程
3.1 -> 概念
3.2 -> 进程-PCB
3.3 -> 进程状态
3.3.1 -> Z(Zombie)-僵尸进程
3.3.2 -> 孤儿进程
3.4 -> 进程优先级
3.4.1 -> 概念
4 -> 环境变量
4.1 -> 概念
5 -> 地址空间
5.1 -> 程序地址空间 
5.2 -> 进程地址空间 
6 -> ​Linux2.6内核进程调理队列


1 -> 冯诺依曼体系结构

我们常见的计算机,如条记本。我们不常见的计算机,如服务器,大部分都服从冯诺依曼体系。

停止现在,我们所认识的计算机,都是一个个的硬件组件构成

关于冯诺依曼,必须强调的几点:

2 -> 操作体系(operator System)

2.1 -> 概念

任何计算机体系都包含一个根本的程序集合,称为操作体系(OS)。笼统的理解,操作体系包括:

计划OS的目的

定位
在整个计算机软硬件架构中,操作体系的定位是:一款纯正的“搞管理” 的软件

总结
计算机管理硬件
2.2 -> 体系调用和库函数

3 -> 进程

3.1 -> 概念


3.2 -> 进程-PCB

进程信息被放在一个 叫做进程控制块的数据结构中,可以理解为进程属性的集合。PCB(Process Control Block),Linux操作体系下的PCB是:task_struct。
task_struct-PCB的一种

task_struct内容分类

组织进程
可以在内核源代码里找到它。所有运行在体系里的进程都以task_struct链表的形式存在内核里。
查看进程
进程的信息可以通过 /proc 体系文件夹查看



  1. #define _CRT_SECURE_NO_WARNINGS 1
  2. #include <stdio.h>
  3. #include <sys/types.h>
  4. #include <Windows.h>
  5. #include <unistd.h>
  6. int main()
  7. {
  8.         while (1)
  9.         {
  10.                 Sleep(1);
  11.         }
  12.         return 0;
  13. }
复制代码
通过体系调用获取进程标示符

  1. #include <stdio.h>
  2. #include <sys/types.h>
  3. #include <Windows.h>
  4. #include <unistd.h>
  5. int main()
  6. {
  7.         printf("pid: %d\n", getpid());
  8.         printf("ppid: %d\n", getppid());
  9.         return 0;
  10. }
复制代码
通过体系调用创建进程(初识fork)

  1. #include <stdio.h>
  2. #include <sys/types.h>
  3. #include <unistd.h>
  4. int main()
  5. {
  6.         int ret = fork();
  7.         printf("hello proc : %d!, ret: %d\n", getpid(), ret);
  8.         sleep(1);
  9.         return 0;
  10. }
复制代码

  1. #include <stdio.h>
  2. #include <sys/types.h>
  3. #include <unistd.h>
  4. int main()
  5. {
  6.         int ret = fork();
  7.         if (ret < 0)
  8.         {
  9.                 perror("fork");
  10.                 return 1;
  11.         }
  12.         else if (ret == 0)
  13.         { //child
  14.                 printf("I am child : %d!, ret: %d\n", getpid(), ret);
  15.         }
  16.         else
  17.         { //father
  18.                 printf("I am father : %d!, ret: %d\n", getpid(), ret);
  19.         }
  20.         sleep(1);
  21.         return 0;
  22. }
复制代码
3.3 -> 进程状态

Linux内核源代码:

下面的状态在kernel源代码里界说:
  1. /*
  2. * The task state array is a strange "bitmap" of
  3. * reasons to sleep. Thus "running" is zero, and
  4. * you can test for combinations of others with
  5. * simple bit tests.
  6. */
  7. static const char * const task_state_array[] = {
  8. "R (running)", /* 0 */
  9. "S (sleeping)", /* 1 */
  10. "D (disk sleep)", /* 2 */
  11. "T (stopped)", /* 4 */
  12. "t (tracing stop)", /* 8 */
  13. "X (dead)", /* 16 */
  14. "Z (zombie)", /* 32 */
  15. };
复制代码

进程状态查看
   ps aux / ps axj 命令 
  

3.3.1 -> Z(Zombie)-僵尸进程


来创建一个维持30秒的僵死进程例子:
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. int main()
  4. {
  5.         pid_t id = fork();
  6.         if (id < 0)
  7.         {
  8.                 perror("fork");
  9.                 return 1;
  10.         }
  11.         else if (id > 0)
  12.         { //parent
  13.                 printf("parent[%d] is sleeping...\n", getpid());
  14.                 sleep(30);
  15.         }
  16.         else
  17.         {
  18.                 printf("child[%d] is begin Z...\n", getpid());
  19.                 sleep(5);
  20.                 exit(EXIT_SUCCESS);
  21.         }
  22.         return 0;
  23. }
复制代码
僵尸进程的危害

3.3.2 -> 孤儿进程


#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; }
  3.4 -> 进程优先级

3.4.1 -> 概念


查看体系进程
在Linux或unix体系中,用ps -l命令则会类似输出以下几个内容:
   

  很容易留意到其中的几个重要信息:

PRI and NI

PRI vs NI

查看进程优先级的命令
用top命令更改已存在进程的nice:

其他概念

4 -> 环境变量

4.1 -> 概念

环境变量(environment variables)一样平常是指在操作体系中用来指定操作体系运行环境的一些参数如:我们在编写C/C++代码的时候,在链接的时候,从来不知道我们的所链接的动态静态库在那边,但是还是可以链接成功,天生可实行程序,原因就是有相关环境变量资助编译器进行查找。环境变量通常具有某些特殊用途,还有在体系当中通常具有全局特性。
常见的环境变量

查看环境变量的方法
echo $NAME //NAME:你的环境变量名称。 
测试PATH
1. 创建hello.c文件
  
  1. #include <stdio.h>
  2. int main()
  3. {
  4.         printf("hello world!\n");
  5.         return 0;
  6. }
复制代码
2. 对比./hello实行和之间hello实行。
3. 为什么有些指令可以直接实行,不需要带路径,而我们的二进制程序需要带路径才能实行?
4. 将我们的程序所在路径加入环境变量PATH当中, export PATH=$PATH:hello程序所在路径。
5. 对比测试。
6. 还有什么方法可以不用带路径,直接就可以运行呢?
测试HOME 
用root和普通用户,分别实行 echo $HOME ,对比差异
. 实行 cd ~; pwd ,对应 ~ 和 HOME 的关系
和环境变量相关的命令
环境变量的组织方式


每个程序都会收到一张环境表,环境表是一个字符指针数组,每个指针指向一个以’\0’结尾的环境字符串。
通过代码如何获取环境变量

  
  1. #include <stdio.h>
  2. int main(int argc, char* argv[], char* env[])
  3. {
  4.         int i = 0;
  5.         for (; env[i]; i++)
  6.         {
  7.                 printf("%s\n", env[i]);
  8.         }
  9.         return 0;
  10. }
复制代码

  
  1. #include <stdio.h>
  2. int main(int argc, char* argv[])
  3. {
  4.         extern char** environ;
  5.         int i = 0;
  6.         for (; environ[i]; i++)
  7.         {
  8.                 printf("%s\n", environ[i]);
  9.         }
  10.         return 0;
  11. }
复制代码
libc中界说的全局变量environ指向环境变量表,environ没有包含在任何头文件中,所以在使用时 要用extern声明。
通过体系调用获取或设置环境变量 
  
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. int main()
  4. {
  5.         printf("%s\n", getenv("PATH"));
  6.         return 0;
  7. }
复制代码
常用getenv和putenv函数来访问特定的环境变量。
环境变量通常是具有全局属性的

  
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. int main()
  4. {
  5.         char* env = getenv("MYENV");
  6.         if (env)
  7.         {
  8.                 printf("%s\n", env);
  9.         }
  10.         return 0;
  11. }
复制代码
直接查看,发现没有结果,说明该环境变量根本不存在

实验

5 -> 地址空间

研究背景

5.1 -> 程序地址空间 


​来段代码感受一下。
  
  1. #include <stdio.h>
  2. #include <unistd.h>
  3. #include <stdlib.h>
  4. int g_val = 0;
  5. int main()
  6. {
  7.         pid_t id = fork();
  8.         if (id < 0)
  9.         {
  10.                 perror("fork");
  11.                 return 0;
  12.         }
  13.         else if (id == 0)
  14.         { //child
  15.                 printf("child[%d]: %d : %p\n", getpid(), g_val, &g_val);
  16.         }
  17.         else
  18.         { //parent
  19.                 printf("parent[%d]: %d : %p\n", getpid(), g_val, &g_val);
  20.         }
  21.         sleep(1);
  22.         return 0;
  23. }
复制代码
输出:
      parent[2995]: 0 : 0x80497d8        child[2996]: 0 : 0x80497d8    我们发现,输出出来的变量值和地址是一模一样的,很好理解呀,由于子进程按照父进程为模版,父子并没有对变量进行进行任何修改。但是将代码稍加改动:​
  
  1. #include <stdio.h>
  2. #include <unistd.h>
  3. #include <stdlib.h>
  4. int g_val = 0;
  5. int main()
  6. {
  7.         pid_t id = fork();
  8.         if (id < 0)
  9.         {
  10.                 perror("fork");
  11.                 return 0;
  12.         }
  13.         else if (id == 0)
  14.         { //child,子进程肯定先跑完,也就是子进程先修改,完成之后,父进程再读取
  15.                 g_val = 100;
  16.                 printf("child[%d]: %d : %p\n", getpid(), g_val, &g_val);
  17.         }
  18.         else
  19.         { //parent
  20.                 sleep(3);
  21.                 printf("parent[%d]: %d : %p\n", getpid(), g_val, &g_val);
  22.         }
  23.         sleep(1);
  24.         return 0;
  25. }
复制代码
输出:
      child[3046]: 100 : 0x80497e8        parent[3045]: 0 : 0x80497e8    我们发现,父子进程,输出地址是一致的,但是变量内容不一样!能得出如下结论:

OS必须负责将 虚拟地址 转化成 物理地址 。
5.2 -> 进程地址空间 

​所以之前说‘程序的地址空间’是禁绝确的,准确的应该说成 进程地址空间 ,那该如何理解呢?看图:
分页 & 虚拟地址空间​

​说明:
上面的图就足矣说名题目,同一个变量,地址相同,实在是虚拟地址相同,内容差异实在是被映射到了差异的物理地址!
6 -> ​Linux2.6内核进程调理队列



   上图是Linux2.6内核中进程队列的数据结构。   
一个CPU拥有一个runqueue
 如果有多个CPU就要考虑进程个数的负载均衡题目。
优先级

运动队列

逾期队列

active指针和expired指针​

总结
在体系当中查找一个最合适调理的进程的时间复杂度是一个常数,不随着进程增多而导致时间成本增加,我们称之为进程调理O(1)算法。



感谢各位大佬支持!!!

互三啦!!!




免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。




欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/) Powered by Discuz! X3.4