linux进程

课本概念:程序的⼀个执行实例,正在执行的程序等
内核观点:担当分配系统资源(CPU时间,内存)的实体。

进程信息被放在一个叫做进程控制块的数据结构中,可以理解为进程属性的集合.课本上称之为PCB(process control block),Linux操作系统下的PCB是: task_struct

task_struct-PCB的⼀种
• 在Linux中描述进程的结构体叫做task_struct。
• task_struct是Linux内核的一种数据结构,它会被装载到RAM(内存)里并且包含着进程的信息。

可以在内核源代码里找到它。所有运行在系统里的进程都以task_struct链表的形式存在内核里。 

 内容分类

  • 标示符: 描述本进程的唯一标示符,用来区别其他进程。
  • 状态: 任务状态,退出代码,退出信号等。
  • 优先级: 相对于其他进程的优先级。
  • 程序计数器: 程序中即将被执行的下⼀条指令的地址。
  • 内存指针: 包括程序代码和进程相关数据的指针,还有和其他进程共享的内存块的指针
  • 上下文数据: 进程执行时处理器的寄存器中的数据[休学例子,要加图CPU,寄存器]。
  • I∕O状态信息: 包括显示的I/O请求,分配给进程的I∕O设备和被进程使用的文件列表。
  • 记账信息: 可能包括处理器时间总和,使用的时钟数总和,时间限制,记账号等。
  • 其他信息

 查看进程


1. 进程的信息可以通过 /proc 系统文件夹查看

2.top或者ps

ps aux / ps axj 命令
  •  a:显示⼀个终端所有的进程,包括其他用户的进程。
  • x:显示没有控制终端的进程,例如后台运行的守护进程。
  • j:显示进程归属的进程组ID、会话ID、父进程ID,以及与作业控制相关的信息
  • u:以用户为中心的格式显示进程信息,提供进程的详细信息,如用户、CPU和内存使用情况等

3.通过系统调用获取进程标示符

  • 进程id(PID)
  • 父进程id(PPID)
  1 #include <stdio.h>2 #include <sys/types.h>3 #include <unistd.h>4 int main()5 {6 while(1){7 8 sleep(1);9 printf("我是一个进程!我的pid:%d,我的ppid:%d \n",getpid(),getppid());                                   10  }11 return 0;12 }
~

 创建进程

 通过系统调用创建进程-fork初识

  • fork有两个返回值
  • 父子进程代码共享,数据各自开辟空间,私有⼀份(采用写时拷贝)
  • 常用if父子分流

操作成功,0返回给子进程把pid返回给父进程,返回给父进程-1是创建失败

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
int ret = fork();
if(ret < 0){
perror("fork");
return 1;
}
else if(ret == 0){ //child
printf("I am child : %d!, ret: %d\n", getpid(), ret);
}else{ //father
printf("I am father : %d!, ret: %d\n", getpid(), ret);
}
sleep(1);
return 0;
}

 所以我们可以用if来分流,让父子执行不同的操作

  1 #include <stdio.h>2 #include <sys/types.h>3 #include <unistd.h>4 int main()5 {6    printf("程序开始运行pid:%d,",getpid());7 8 pid_t id = fork();9 if(id < 0){                                                                                                                                                                              10 perror("fork");11 return 1;12 }13 else if(id == 0)14 {15     //child16     while(1)17     {18       sleep(1);19       printf("我是一个子进程,我的pid:%d ,我的父进程pid:%d\n",getpid(),getppid());20     }21 }22 else{23    //father24    while(1)25    {26      sleep(1);27       printf("我是一个父进程,我的pid:%d ,我的父进程pid:%d\n",getpid(),getppid());28 29    }30 }31 return 0;32 }

 

fork为什么会有两个返回值?

fork也是函数,它是一个系统调用,fork创建子进程,父子各一次return


两个返回值各种给父子如何返回?

fork也是函数,它是一个系统调用,fork创建子进程后,子进程进行写实拷贝父进程,父进程里的id写给子进程

写实拷贝

创建子进程中,子进程拷贝父进程的代码和页表等的数据后各自独立一份单独执行,从这里也可以间接印证,数据并不是存在实际的地址中,一个变量怎么可能会有两个值呢?只不过是一种映射方式,涉及到虚拟地址后面再谈

1 #include <stdio.h>2 #include <sys/types.h>3 #include <unistd.h>4 int a=100;5 int main()6 {7    printf("程序开始运行pid:%d,",getpid());8 9 pid_t id = fork();10 if(id < 0){11 perror("fork");12 return 1;13 }14 else if(id == 0)15 {16     //child17     while(1)18     {19       a++;                                                                                                                                                                               20       sleep(1);21       printf("我是一个子进程,我的pid:%d ,我的父进程pid:%d,a:%d\n",getpid(),getppid(),a);22     }23 }24 else{25    //father26    while(1)27    {28      sleep(1);29       printf("我是一个父进程,我的pid:%d ,我的父进程pid:%da:%d\n",getpid(),getppid(),a);30 31    }32 }33 return 0;34 }
~

进程状态

运行&&阻塞&&挂起

运行:进程在调度队列中,进程的状态都是R

阻塞:等待某种设备或资源就绪

挂起:把运行队列中的进程交换到磁盘的swap交换分区里面

 创建子进程是为了使子进程执行某种命令,子进程回返回父进程一种状态(可以用ps命令来查)

/*
*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): 意味着进程在等待事件完成(这里的睡眠有时候也叫做可中断睡眠(interruptible sleep))。
  • D磁盘休眠状态(Disk sleep)有时候也叫不可中断睡眠状态(uninterruptible sleep),在这个状态的进程通常会等待IO的结束。
  • T停止状态(stopped): 可以通过发送 SIGSTOP 信号给进程来停止(T)进程。这个被暂停的进程可以通过发送 SIGCONT 信号让进程继续运行。
  • X死亡状态(dead):这个状态只是⼀个返回状态,你不会在任务列表里看到这个状态。

 僵尸进程

创建子进程是为了使子进程执行某种命令,僵尸进程是进程状态的一种,子进程一种维持在一种状态而父进程迟迟获取不到子进程具体的退出信息,容易内存泄漏!

  1 #include <stdio.h>2 #include <stdlib.h>3 #include<unistd.h>4 #include<sys/types.h>5 int main()6 {7 pid_t id = fork();8 if(id < 0){9 perror("fork");10 return 1;                                                                               11 }12 else if(id > 0)13 { //parent14 printf("parent[%d] is sleeping...\n", getpid());15 sleep(30);16 }else{17 printf("child[%d] is begin Z...\n", getpid());18 sleep(5);19 exit(EXIT_SUCCESS);20 }21 return 0;22 }

 僵尸进程的危害

  • 进程的退出状态必须被维持下去,因为他要告诉关心它的进程(父进程),你交给我的任务,我办的怎么样了。可父进程如果⼀直不读取,那子进程就⼀直处于Z状态?是的!
  • 维护退出状态本身就是要用数据维护,也属于进程基本信息,所以保存在task_struct(PCB)中,换句话说,Z状态⼀直不退出,PCB⼀直都要维护?是的!
  • 那一个父进程创建了很多子进程,就是不回收,是不是就会造成内存资源的浪费?是的!因为数据结构对象本身就要占用内存,想想C中定义⼀个结构体变量(对象),是要在内存的某个位置进行开辟空间!

那什么样的进程具有内存泄漏,是比较麻烦的?

进程退出了,内存泄漏就不存在了。但常驻内存的进程是给PCB维护的,释放不归操作系统管,内存释放就交给用户了,容易内存泄漏。

孤儿进程

父进程创建子进程,父进程在子进程未结束之前结束,子进程此时就是孤儿进程,被1号进程领养

#include <stdio.h>2 #include <unistd.h>3 #include <stdlib.h>4 int main()5 {6 pid_t id = fork();7 if(id < 0){8 perror("fork");9 return 1;10 }11 else if(id == 0){//child12   while(1){13 printf("I am child, pid : %dppid:%d\n", getpid(),getppid());14 sleep(1);15 16 }}17 else{//parent18   int cnt=5;19   while(cnt--){20 printf("I am parent, pid: %dppid:%d\n", getpid(),getppid());                            21 sleep(1);22 exit(0);23 }24 }25 return 0;26 }
~

孤儿进程使用ctrl c杀不死,因为ctrl c杀不死他是后台进程 (./cmd &也能变成后台进程)得使用 kill -9

进程优先级

  • cpu资源分配的先后顺序,就是指进程的优先权(priority)。
  • 优先权高的进程有优先执行权利。配置进程优先权对多任务环境的linux很有用,可以改善系统性能。
  • 还可以把进程运行到指定的CPU上,这样⼀来,把不重要的进程安排到某个CPU,可以大大改善系统整体性能。

UID : 代表执行者的身份
• PID : 代表这个进程的代号
• PPID :代表这个进程是由哪个进程发展衍生而来的,亦即父进程的代号
• PRI :代表这个进程可被执行的优先级,其值越小越早被执行默认为80
• NI :代表这个进程的nice值

PRI即进程的优先级,或者通俗点说就是程序被CPU执行的先后顺序,值越小进程的优先级别越高
那NI就是nice值,其表示进程可被执行的优先级的修正数值
PRI值越小越快被执行,那么加入nice值后,将会使得PRI变为:PRI(new)=PRI(old)+nice
这样,当nice值为负值的时候,那么该程序将会优先级值将变小,即其优先级会变高,则其越快
被执行。所以,调整进程优先级,在Linux下,就是调整进程nice值(nice其取值范围是-20至19,⼀共40个级别。)

普通优先级:100〜139(我们都是普通的优先级,想想nice值的取值范围,可与之对应!)
实时优先级:0〜99(不关心)

需要强调一点的是,进程的nice值不是进程的优先级,他们不是一个概念,但是进程nice值会影
响到进程的优先级变化。可以理解nice值是进程优先级的修正修正数据

调整优先级

调整获得cpu资源的先后顺序,NI值越低,优先级越高,反之优先级越低、基于时间片的分时操作系统,考虑公平性,优先级可能会发生变化,但是变化幅度不会相差太大,默认80,每次修改都是从80开始操作nice范围是-20~19,linux进程优先级为60~99

用top命令更改已存在进程的nice:

  • top
  • 进入top后按“r”‒>输入进程PID‒>输入nice值

注意:

  • 其他调整优先级的命令:nice,renice
  • 系统函数:

让它加10,改完变成90,怎么还调低了呢,参考下文优先级队列

优先级设立不合理,会导致优先级低的进程长时间得不到cpu资源,进而导致:进程饥饿

补充概念-竞争、独立、并行、并发

  • 竞争性: 系统进程数目众多,而CPU资源只有少量,甚至1个,所以进程之间是具有竞争属性的。为了高效完成任务,更合理竞争相关资源,便具有了优先级
  •  独立性: 多进程运行,需要独享各种资源,多进程运行期间互不干扰
  • 并行: 多个进程在多个CPU下分别,同时进行运行,这称之为并行
  • 并发: 多个进程在一个CPU下采用进程切换的方式,在一段时间之内,让多个进程都得以推进,称之为并发

进程切换

CPU上下文切换:其实际含义是任务切换, 或者CPU寄存器切换。当多任务内核决定运行另外的任务时, 它保存正在运行任务的当前状态, 也就是CPU寄存器中的全部内容。这些内容被保存在任务自己的堆栈中, 入栈工作完成后就把下一个将要运行的任务的当前状况从该任务的栈中重新装入CPU寄存器,并开始下一个任务的运行, 这一过程就是context switch。

当进程要把自己的进程硬件上下文的数据报存下来,保存到哪里???
保存到tast_steuct里!!!

进程切换,最核心的,就是保存和恢复当前进程的硬件上下文的数据,及cpu寄存器的内容

 linux源码

 时间片:当代计算机都是分时操作系统,没有进程都有它合适的时间片(其实就是⼀个计数
器)。时间片到达,进程就被操作系统从CPU中剥离下来。

Linux2.6内核进程O(1)调度队列

从该结构中,选择⼀个最合适的进程,过程是怎么的呢?
1. 从0下表开始遍历queue[140]
2. 找到第⼀个非空队列,该队列必定为优先级最高的队列
3. 拿到选中队列的第⼀个进程,开始运行,调度完成!
4. 遍历queue[140]时间复杂度是常数!但还是太低效了!

  • bitmap(unsigned int bitmap[5]):用位图二进制0表示没有,1表示该队列节点有进程链,方便快速遍历队列一共140个优先级,一共140个进程队列,为了提高查找非空队列的效率,就可以用5*32个比特位表示队列是否为空,这样,便可以大大提高查找效率!
  • array[0],arry[1]:双队列中进行,若只有一个调度队列的话,只有上一个节点跑完,下一个节点才能得到资源,那这样的话,进程优先级低的长时间就得不到cpu资源防止进程饥饿
  • 一个CPU拥有⼀个runqueue如果有多个CPU就要考虑进程个数的负载均衡问题
  • 时间片还没有结束的所有进程都按照优先级放在该队列 
  • nr_active: 总共有多少个运行状态的进程
  • queue[140]: 一个元素就是一个进程队列,相同优先级的进程按照FIFO规则进行排队调度,所以,数组下标就是优先级!

过期队列和活动队列结构一模一样
• 过期队列和活动队列结构模样
• 过期队列上放置的进程,都是时间片耗尽的进程
• 当活动队列上的进程都被处理完毕之后,对过期队列的进程进行时间片重新计算

 active指针永远指向活动队列,expired指针永远指向过期队列
• 可是活动队列上的进程会越来越少,过期队列上的进程会越来越多,因为进程时间片到期时一直
都存在的。
•在合适的时候,只要能够交换active指针和expired指针的内容,就相当于有具有了一批新的活动进程 

所以来新进程时链入expired队列里,等active队列跑完,然后和expired进行交换,循环往复。调整优先级也是链在expired里调整优先级,所以上文的+10,反而变慢了,在过期队列里进程优先级提高了,但是相比原来的就变慢了

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/1507.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Hadoop•安装JDK

听说这里是目录哦 创建目录❤️‍&#x1f525;上传JDK安装包&#x1f497;查看JDK是否上传成功&#x1f498;安装JDK&#x1f496;配置JDK系统环境变量&#x1f493;验证JDK是否安装成功&#x1f49e;分发JDK安装目录&#x1f48c;分发系统环境变量文件&#x1f49d;若显示没有…

[Deep Learning] Anaconda+CUDA+CuDNN+Pytorch(GPU)环境配置-2025

文章目录 [Deep Learning] AnacondaCUDACuDNNPytorch(GPU)环境配置-20250. 引子1. 安装Anaconda1.1 安装包下载&#xff1a;1.2 启用安装包安装1.3 配置(系统)环境变量1.4 验证Anaconda是否安装完毕1.5 Anaconda换源 2. 安装CUDACuDNN2.1 判断本机的CUDA版本2.2 下载适合自己CU…

网络原理(四)—— 网络层、数据链路层 与 DNS

网络层 网络层这里重点介绍 IP 协议&#xff0c;首先先解析 IP 数据包&#xff1a; 先介绍第一行&#xff1a; 4位版本号是指使用了哪一个版本的 IP 协议&#xff0c;这里有 IPV4 和 IPV6 两种协议&#xff0c;现在主要使用的是 IPV4 这一个版本号&#xff0c; IPV6 在国内也…

Redis快速入门店铺营业状态设置

Redis简介 Redis是一种基于内存的键值对&#xff08;K-V&#xff09;数据库。 这意味着它与MySQL数据库类似&#xff0c;都能够用于存储数据&#xff0c;但两者又有着本质的区别。首先两者存储数据的结构不一样&#xff0c;Redis通过键&#xff08;key&#xff09;和值…

Node.js 如何实现文件夹内文件批量重命名

文章目录 一、引言二、Node.js 简介2.1 是什么2.2 优势 三、Node.js 批量重命名原理3.1 涉及的核心模块3.2 关键函数 四、实战步骤4.1 环境搭建4.2 代码实现4.3 代码解释 五、案例分析5.1 场景描述5.2 解决方案 六、可能遇到的问题与解决方法6.1 常见错误6.2 解决方案 七、总结…

MySQL(高级特性篇) 04 章——逻辑架构

一、逻辑架构剖析 &#xff08;1&#xff09;服务器处理客户端请求 那服务器进程对客户端进程发送的请求做了什么处理&#xff0c;才能产生最后的处理结果呢&#xff1f;这里以查询请求为例展示&#xff1a;下面具体展开看一下&#xff1a;Connectors是MySQL服务器之外的客户…

滚动字幕视频怎么制作

在当今的视频创作领域&#xff0c;滚动字幕被广泛应用于各种场景&#xff0c;为视频增添丰富的信息展示和独特的视觉效果。无论是影视剧中的片尾字幕、新闻节目中的资讯滚动&#xff0c;还是综艺节目中的人员与鸣谢信息展示&#xff0c;滚动字幕都发挥着不可或缺的作用。接下来…

源码编译安装httpd 2.4,提供系统服务管理脚本并测试(两种方法实现)

方法一&#xff1a;使用 systemd 服务文件 sudo yum install gcc make autoconf apr-devel apr-util-devel pcre-devel 1.下载源码 wget https://archive.apache.org/dist/httpd/httpd-2.4.46.tar.gz 2.解压源码 tar -xzf httpd-2.4.46.tar.gz 如果没有安装tar 记得先安装…

计算机视觉算法实战——步态识别(主页有源码)

✨个人主页欢迎您的访问 ✨期待您的三连 ✨ ✨个人主页欢迎您的访问 ✨期待您的三连 ✨ ✨个人主页欢迎您的访问 ✨期待您的三连✨ ​ ​​​​​​​​​​​​​​​​​​ 1. 步态识别简介✨✨ 步态识别&#xff08;Gait Recognition&#xff09;是计算机视觉领域中的一个…

2025 年 UI 大屏设计新风向

在科技日新月异的 2025 年&#xff0c;UI 大屏设计领域正经历着深刻的变革。随着技术的不断进步和用户需求的日益多样化&#xff0c;新的设计风向逐渐显现。了解并掌握这些趋势&#xff0c;对于设计师打造出更具吸引力和实用性的 UI 大屏作品至关重要。 一、沉浸式体验设计 如…

Leetcode - 周赛431

目录 一&#xff0c;3411. 最长乘积等价子数组 二&#xff0c;3412. 计算字符串的镜像分数 三&#xff0c;3413. 收集连续 K 个袋子可以获得的最多硬币数量 四&#xff0c;3414. 不重叠区间的最大得分 一&#xff0c;3411. 最长乘积等价子数组 本题数据范围小&#xff0c;直…

深入Android架构(从线程到AIDL)_30 JNI架构原理_Java与C的对接03

目录 2.4 以C结构表达类(class)&#xff0c;并创建对象(object) 认识C函数指针 范例 2.5 在C函数里存取对象的属性(attribute) 范例 2.4 以C结构表达类(class)&#xff0c;并创建对象(object) 认识C函数指针 struct里不能定义函数本身&#xff0c;但能定义函数指针(func…

论文笔记(四十七)Diffusion policy: Visuomotor policy learning via action diffusion(下)

Diffusion policy: Visuomotor policy learning via action diffusion&#xff08;下&#xff09; 文章概括5. 评估5.1 模拟环境和数据集5.2 评估方法论5.3 关键发现5.4 消融研究 6 真实世界评估6.1 真实世界Push-T任务6.2 杯子翻转任务6.3 酱汁倒入和涂抹任务 7. 实际双臂任务…

EasyExcel - 行合并策略(二级列表)

&#x1f63c;前言&#xff1a;博主在工作中又遇到了新的excel导出挑战&#xff1a;需要导出多条文章及其下联合作者的信息&#xff0c;简单的来说是一个二级列表的数据结构。 &#x1f575;️‍♂️思路&#xff1a;excel导出实际上是一行一行的记录&#xff0c;再根据条件对其…

软件测试面试题整理

一、人格相关问题 1、自我介绍结构 姓名工作年限简单介绍上家公司的行业主要负责内容个人优势短期内的职业规划应聘该岗位的原因 2、对未来的发展方向怎么看&#xff1f; 没有标准答案&#xff0c;职业规划来讲&#xff0c;可以分为技术层面和管理层面去说&#xff0c;技术…

.NET framework、Core和Standard都是什么?

对于这些概念一直没有深入去理解&#xff0c;以至于经过.net这几年的发展进化&#xff0c;概念越来越多&#xff0c;越来越梳理不容易理解了。内心深处存在思想上的懒惰&#xff0c;以为自己专注于Unity开发就好&#xff0c;这些并不属于核心范畴&#xff0c;所以对这些概念总是…

CNN张量输入形状和特征图

CNN张量输入形状和特征图 这个是比较容易理解的张量的解释&#xff0c;比较直观 卷积神经网络 在这个神经网络编程系列中&#xff0c;我们正在逐步构建一个卷积神经网络&#xff08;CNN&#xff09;&#xff0c;所以让我们看看CNN的张量输入。 ​ ​ 在最后两篇文章中&…

【数据可视化-12】数据分析岗位招聘分析

&#x1f9d1; 博主简介&#xff1a;曾任某智慧城市类企业算法总监&#xff0c;目前在美国市场的物流公司从事高级算法工程师一职&#xff0c;深耕人工智能领域&#xff0c;精通python数据挖掘、可视化、机器学习等&#xff0c;发表过AI相关的专利并多次在AI类比赛中获奖。CSDN…

(12)springMVC文件的上传

SpringMVC文件上传 首先是快速搭建一个springMVC项目 新建项目mvn依赖导入添加webMoudle添加Tomcat运行环境.在配置tomcat时ApplicationContext置为"/"配置Artfact的lib配置WEB-INF配置文件&#xff08;记得添加乱码过滤&#xff09;配置springmvc-servlet文件&…

Ubuntu中双击自动运行shell脚本

方法1: 修改文件双击反应 参考: https://blog.csdn.net/miffywm/article/details/103382405 chmod x test.sh鼠标选中待执行文件&#xff0c;在窗口左上角edit菜单中选择preference设计双击执行快捷键&#xff0c;如下图&#xff1a; 方法2: 设置一个应用 参考: https://blo…