【Linux进程状态】
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
目录
前言
一、直接谈论Linux的进程状态
看看Linux内核源代码怎么说
1.1、R状态 -----> 进程运行的状态
1.2、S状态 -----> 休眠状态(进程在等待“资源”就绪)
1.3、T状态 -----> 暂停状态
1.4、t状态 ------> 当前的进程因为被追踪而暂停了
1.5、D状态
1.6、僵尸状态(Z)
1.7、死亡状态(X)
二、僵尸进程
下面我们创建一个进程来看一看
僵尸进程危害
三、孤儿进程
下面我们创建一个进程来看一看
四、运行状态
五、阻塞状态
六、挂起状态
七、进程切换的话题
总结
前言
世上有两种耀眼的光芒,一种是正在升起的太阳,一种是正在努力学习编程的你!一个爱学编程的人。各位看官,我衷心的希望这篇博客能对你们有所帮助,同时也希望各位看官能对我的文章给与点评,希望我们能够携手共同促进进步,在编程的道路上越走越远!
提示:以下是本篇文章正文内容,下面案例可供参考
一、直接谈论Linux的进程状态
看看Linux内核源代码怎么说
- 为了弄明白正在运行的进程是什么意思,我们需要知道进程的不同状态。一个进程可以有几个状态(在 Linux内核里,进程有时候也叫做任务)。
- 下面的状态在kernel源代码里定义:
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 */
};
操作系统要更改一个进程的状态,它的原理其实就是更改 task_struct{}内部的状态属性(status)。
1.1、R状态 -----> 进程运行的状态
vim Makefile
testStatus:testStatus.cgcc -o $@ $^ -g
.PHONY:clean
clean:rm -f testStatus
这儿有一个替换方式:
:%s/myprocess/testStatus/
//将 myprocess替换成 testStatus的可执行程序
vim testStatus.c
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>int main()
{while(1){printf("i am a process,pid:%d\n",getpid());}return 0;
}
我们还可以用shell命令来一直刷新观察
while :; do ps ajx | head -1 && ps ajx | grep testStatus | grep -v grep; sleep 1; done
它其实一直处于一种休眠状态。
我们将打印的代码注释掉,再来看一下程序:
vim testStatus.c
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>int main()
{while(1){//printf("i am a process,pid:%d\n",getpid());}return 0;
}
此时的代码就是一个非常纯粹的循环代码,没有打印。
重新 make 编译一下:
那么为什么我们加上 printf 的代码,就看到了大量的 S(休眠状态)呢?
printf的本质是往显示器上打印;而程序的运行是在千里之外的云服务器上跑;根据冯诺依曼体系结构,显示器是一个外设,所以CPU在跑当前的程序时,把数据写入到我们当前的内存当中,打印数据的顺序:先写入到内存里,再刷新到外设里。可是我们无法保证每次打印的时候,显示器的状态都是就绪的,因为程序是CPU跑的,CPU的运算速度要比显示器本身的速度要快的多,所以进程在被调度的时候,要访问显示器的资源,因为资源要一直在显示器上打,所以大部分时间,相比较CPU来讲,大部分时间,我们对应的进程都在等待我们的设备资源是否就绪。就比如:代码执行到 printf 的时候,CPU是几纳秒,而数据刷新到显示器的时间是几毫秒,其余大部分时间都在等待中,而这等待的时间,就是(S)休眠状态。
1.2、S状态 -----> 休眠状态(进程在等待“资源”就绪)
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>int main()
{while(1){sleep(10);//printf("i am a process,pid:%d\n",getpid());}return 0;
}
可中断睡眠:处于S睡眠状态,依旧可以随时被外部信息打断,比如:ctrl + c,就醒来了。
进程在运行时 + &(取地址符);那么进程就默认在后端运行。
此时 R 后面就没有 + 了,+ 代表进程是在前端运行的,还是在后端运行的。
1.3、T状态 -----> 暂停状态
T/t :让进程暂停,等待被进一步唤醒。
1.4、t状态 ------> 当前的进程因为被追踪而暂停了
1.5、D状态
- D:Linux系统比较特有的一种进程状态。
- 当A进程需要大量的数据写入磁盘的时候,等待磁盘写入数据,此时A进程的状态就是休眠状态,当磁盘写入数据完成或者写入失败的话,都要向A进程汇报情况。
- 但是有一个场景:系统中内存严重不足时,操作系统有一个特权,Linux操作系统有权力杀掉进程来释放空间。那么此时你的A进程被操作系统杀掉了,用来释放空间,过来3、4秒之后,磁盘已经写入了1GB的数据了,还想要再次写入A进程的数据,但是失败了,失败之后,要汇报情况,可是A进程已经被操作系统杀掉了;此时B进程也要向磁盘写入数据,那么磁盘就去照顾B进程去了,那么已经写入A进程的那1GB数据就丢失了(假如那1GB的数据是转账记录的话,那事故是很严重的),那该怎么办呢?
- 为了避免这种情况,就可以把等待磁盘数据写入后,需要拿到磁盘返回结果的进程状态,设为D状态。D:不可被杀,深度睡眠,不可中断睡眠。
消除D状态有两种情况:
1、让进程自己醒来;
2、重启,不行的话,就得断电了。
1.6、僵尸状态(Z)
僵尸状态也叫僵死状态,它是在进程在死亡状态之前的状态。当一个进程运行完毕、出现问题或者被杀掉以后,它所占用的内存资源和退出状态没有被它的父进程回收,此时这个进程的状态就称为僵尸状态。
1.7、死亡状态(X)
当一个进程执行结束或者是被操作系统杀掉,它的PCB被操作系统删除,并且对应加载到磁盘上的二进制代码也被删除,此时这个进程就处于死亡状态了。
当一个进程占有内存的所有资源被回收以后,这个进程就处于死亡状态。进程的死亡状态是看不到的,因为只有在回收完成的那一刻才会出现,PCB不存在也就搜索不到这个进程,所以也无法演示。
二、僵尸进程
- 僵死状态(Zombies)是一个比较特殊的状态。当进程退出并且父进程(使用wait()系统调用,后面讲) 没有读取到子进程退出的返回代码时就会产生僵死(尸)进程。
- 僵死进程会以终止状态保持在进程表中,并且会一直在等待父进程读取退出状态代码。
- 所以,只要子进程退出,父进程还在运行,但父进程没有读取子进程状态,子进程进入Z状态。
Linux中的进程退出的时候,会将自己的退出信息保留在自己的PCB当中,如果没有人读取PCB中进程退出的消息,那么进程就会一直不释放;一般会把进程的代码和数据结果释放掉,但是PCB的数据结构不会释放,直到将来对进程进行等待,如果不等待,那么进程会一直处于僵尸状态。如果把进程的退出信息读取了和等待了,那么这个进程才会变成X状态,从而将进程的所有资源全部释放。
一个进程退出的时候,它不会立即退出,而是会现处于一种僵尸状态,如果父进程不会对我们这个进程进行回收或者等待的话,那么对应的进程会一直处于僵尸状态。
下面我们创建一个进程来看一看
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>int main()
{pid_t id = fork();if(id == 0){// childint cnt = 5;while(cnt){printf("I am child, cnt: %d, pid: %d\n", cnt, getpid());sleep(1);cnt--;}}else{// parentwhile(1){printf("I am parent, running always!, pid: %d\n", getpid());sleep(1);}}return 0;
}
子进程已经运行完毕,但是需要维持自己的退出信息,在自己的进程task_struct会记录自己的退出信息,未来让父进程进行读取。
如果没有父进程读取,僵尸进程会一直存在(内核数据结构task_struct会一直存在),进程的代码和数据会释放。
将来子进程被 waitpid(系统调用接口) 等待方式读取了,那么这个子进程会由Z状态变为X状态,之后由OS来释放。
僵尸进程危害
- 进程的退出状态必须被维持下去,因为他要告诉关心它的进程(父进程),你交给我的任务,我办的怎么样了。可父进程如果一直不读取,那子进程就一直处于Z状态?是的!
- 维护退出状态本身就是要用数据维护,也属于进程基本信息,所以保存在task_struct(PCB)中,换句话说,Z状态一直不退出,PCB一直都要维护?是的!
- 那一个父进程创建了很多子进程,就是不回收,是不是就会造成内存资源的浪费?是的!因为数据结构对象本身就要占用内存,想想C中定义一个结构体变量(对象),是要在内存的某个位置进行开辟空间!
- 内存泄漏?是的!
- 如何避免?后面讲。
三、孤儿进程
- 父进程如果提前退出,那么子进程后退出,进入Z之后,那该如何处理呢?
- 父进程先退出,子进程就称之为“孤儿进程”。
- 孤儿进程被1号init进程领养,当然要有init进程回收喽。
下面我们创建一个进程来看一看
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>int main()
{pid_t id = fork();if(id == 0){// childint cnt = 5;while(cnt){printf("I am child, cnt: %d, pid: %d\n", cnt, getpid());sleep(1);//cnt--;}}else{// parentint cnt = 5while(cnt){printf("I am parent, running always!, pid: %d\n", getpid());sleep(1);cnt--;}}return 0;
}
我们已经启动的所有的进程,我们怎么从来没有关心过僵尸进程(内存泄漏)呢?
直接在命令行中启动的进程,它的父进程是bash,bash会自动回收新进程的Z(僵尸进程)。
四、运行状态
操作系统为了合理分配CPU以及各种硬件资源,保证各个进程的正常运行。操作系统会为CPU创建一个进程队列,也为每一个硬件都创建一个等待队列。
而某一个进程处于运行状态本质上就是操作系统将该进程对应的PCB放入CPU的运行队列中,然后再将PCB中维护进程状态的变量修改为相应的值。
PCB里面有进程的各种属性值,以及对应的代码的地址。所以CPU从运行队列中找到PCB取出数据后,可以根据数据得到进程的各种属性值和指令,然后执行相应代码。进程处于运行状态并不意味着该进程此时一定正在被运行,只要该进程处于CPU的运行队列中即可。
注意:CPU是处理数据的速度在纳秒级,运算速度非常快,所以只要进程处于CPU的运行队列中,我们就可以认为该进程正在被运行。
一个进程一旦持有CPU,会一直运行到这个进程结束吗?
不会。CPU基于时间片进行轮转调度的。(Linux不是这样调度的,这只是OS教材调度方法之一)
让多个进程以切换的方式进行调度,在一个时间段内同时得以推进代码,就叫做并发。
每一个CPU都要有一个自己的运行队列,有两个CPU的话,会存在真正意义上的有两个调度队列。任何时刻,都同时有多个进程在真的同时运行,我们叫做并行。
五、阻塞状态
CPU处理数据的速度极快,是我们计算机中的各种硬件处理数据的万倍,比如:一个磁盘或者一个网卡同时只能为一个进程服务,但是在计算机中需要使用这些硬件资源(磁盘等)的进程会有很多。
如果在硬件为一个进程服务时,有其他运行中的进程也需要使用该硬件资源,操作系统就会将该进程的PCB放入硬件的等待队列中,进程会等待硬件来为自己提供服务。
不是只有CPU才有运行队列,各种硬件设备也有自己的等待队列。
由于多个进程需要访问某种硬件,进程PCB在硬件等待队列中等待硬件服务自己的状态就被称为阻塞状态。阻塞状态在本质上就是将进程的PCB从CPU的运行队列中,放入硬件的等待队列中,然后将PCB中维护进程状态的变量也会修改为相应的值。当该进程获得对应的硬件资源服务时,再将该进程放回CPU的运行队列中。
注意:并不是只有等待硬件资源的进程才处于阻塞状态,一个进程等待另一个进程就绪、一个进程等待软件资源就绪等也是阻塞状态。
六、挂起状态
上面我们学习了阻塞状态,处于阻塞状态的进程由于需要等待某种资源,所以它对应的代码和数据在短期内不会被处理(这里的短期指的是对于操作系统而言)。但它们的数据仍储存在内存中,占用存储空间但是不执行,相当于浪费了内存资源。而如果当前操作系统处于高IO(大量向内存输入和向外部设备输出数据)的情况下,可用的内存空间不足,操作系统就会选择性的将这些处于阻塞状态的进程对应的代码和数据转移到磁盘中,从而节省出内存空间。
这种由于内存空间不足,操作系统将在等待资源的进程对应的代码和数据放到磁盘中以节省内存空间的状态就被称为挂起状态。挂起状态不会移动进程的PCB,只会移动进程对应的代码和数据。
挂起并不是释放进程,因为对应的PCB仍然在硬件的等待队列中,当该进程获得对应的资源服务以后,操作系统仍然可以将该进程对应的代码和数据从磁盘加载到内存中来继续运行,其本质是对内存数据的唤入唤出。
七、进程切换的话题
A进程在CPU运行队列中运行的时间片到了之后,A进程会脱离CPU的运行进程;B进程会进入CPU的运行队列中运行,那么比如:A进程在CPU中已经运行到了第50行代码后,时间片到了,退出CPU,当A进程再次进入CPU的时候,是从新开始运行呢?还是接上上回第50行的代码继续运行呢?
CPU里面有一套寄存器,会保留A进程运行的数据,当A进程退出CPU的时候,A进程在CPU中已经运行的数据,将由 task_struct 这个结构体来保存,当A进程再次进入CPU时,数据还会回到寄存器原来的位置,所以A进程会接着第50行继续运行。
进程的切换,最重要的一件事情是:上下文数据的保护和恢复。
CPU内的寄存器:
寄存器本身是硬件,具有数据的存储能力,CPU的寄存器硬件只有一套!!!CPU内部的数据,可以有多套,有几个进程,就有几套和该进程对应的上下文数据。
寄存器 != 寄存器的内容
总结
好了,本篇博客到这里就结束了,如果有更好的观点,请及时留言,我会认真观看并学习。
不积硅步,无以至千里;不积小流,无以成江海。
相关文章:

【Linux进程状态】
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 目录 前言 一、直接谈论Linux的进程状态 看看Linux内核源代码怎么说 1.1、R状态 -----> 进程运行的状态 1.2、S状态 -----> 休眠状态(进程在等待“资源”就绪) 1.3、T状…...

【RS422】基于未来科技FT4232HL芯片的多波特率串口通信收发实现
功能简介 串行通信接口常常用于在计算机和低速外部设备之间传输数据。串口通信存在多种标准,以RS422为例,它将数据分成多个位,采用异步通信方式进行传输。 本文基于Xilinx VCU128 FPGA开发板,对RS422串口通信进行学习。 根…...

Internet协议的安全性
Internet协议的安全性 文章目录 Internet协议的安全性1. 网络层1. IP*62. ARP*33. ICMP * 3 2. 传输层协议1. TCP1. * SYN-Flood攻击攻击检测* 防御 2. TCP序号攻击攻击 3. 拥塞机制攻击 2. UDP 3. 应用层协议1. DNS攻击*3防范*3: 2. FTP3. TELNET: 改用ssh4. 电子邮件1. 攻击2…...

LeetCode每日一题——移除元素
移除元素OJ链接:27. 移除元素 - 力扣(LeetCode) 题目: 思路: 题目给定要求只能使用O(1)的额外空间并且原地修改输入数组,然后返回移除后的数组行长度。那 么我们就可以确我没有办法建立临时的数组存放我…...

vue3之自定义指令
除了 Vue 内置的一系列指令 (比如 v-model 或 v-show) 之外,Vue 还允许你注册自定义的指令。自定义指令主要是为了重用涉及普通元素的底层 DOM 访问的逻辑。 写法 1. 没有使用 <script setup>的情况下 export default {setup() {/*...*/},directives: {// 在…...

MySQL语法分类 DQL(5)分组查询
为了更好的学习这里给出基本表数据用于查询操作 create table student (id int, name varchar(20), age int, sex varchar(5),address varchar(100),math int,english int );insert into student (id,name,age,sex,address,math,english) values (1,马云,55,男,杭州,66,78),…...

C++程序设计-练手题集合【期末复习|考研复习】
前言 总结整理不易,希望大家点赞收藏。 给大家整理了一下C程序设计中的练手题,以供大家期末复习和考研复习的时候使用。 C程序设计系列文章传送门: 第一章 面向对象基础 第四/五章 函数和类和对象 第六/七/八章 运算符重载/包含与继承/虚函数…...

文件上传漏洞------一句话木马原理解析
目录 一、实验环境 二、实验过程 构造一句话木马 一句话木马的使用: 木马原理解析: 一、实验环境 小皮面板搭建:upload-labs靶场 二、实验过程 构造一句话木马 这是一个最简单的一句话木马,我们用GET传参接受了两个参数,其最终目的是构造出:ass…...

Openfeign使用教程(带你快速体验Openfeign的便捷)
文章摘要 本文中将教会您如何快速使用Openfeign,包括Opengfeign的基础配置、接口调用、接口重试、拦截器实现、记录接口日志信息到数据库 文章目录 文章摘要一、Openfeign初步定义二、Openfeign快速入门1.引入maven坐标2.启动类增加EnableFeignClients注解3.定义fei…...

【leetcode】相同的树➕对称二叉树➕另一棵树的子树
大家好,我是苏貝,本篇博客带大家刷题,如果你觉得我写的还不错的话,可以给我一个赞👍吗,感谢❤️ 目录 一. 相同的树二. 对称二叉树三. 另一棵树的子树 一. 相同的树 点击查看题目 思路: bool isSameTree(…...

uni-app 安卓手机判断是否开启相机相册权限
// 安卓相机权限 androidCameraPermiss(index){ plus.android.requestPermissions([android.permission.CAMERA],(e) > { if (e.deniedAlways.length > 0) { this.androidAuthCamera false …...

GPT实战系列-LangChain构建自定义Agent
GPT实战系列-LangChain构建自定义Agent LangChain GPT实战系列-LangChain如何构建基通义千问的多工具链 GPT实战系列-构建多参数的自定义LangChain工具 GPT实战系列-通过Basetool构建自定义LangChain工具方法 GPT实战系列-一种构建LangChain自定义Tool工具的简单方法 GPT…...

uniapp-vue3 项目初始化集成配置【开箱即用】
地址 https://gitee.com/charrie/vue3-uniapp-init 部分截图展示 技术说明 采用vue3viteuniapp技术栈,setup语法糖编码方式使用完全免费的sard-uniapp组件库引入unocss量子化样式引擎,动态css不用自己写样式,引用class即可,降低…...

【Qt】使用Qt实现Web服务器(一):QtWebApp介绍、演示
1、简介 QtWepApp是使用Qt实现的Web服务器库,虽然名字中有“App”但是它的确是一个lib库,编译生产的是libQtWebAppd.so 作者开发QtWepApp是从 Java Servlet 获得的灵感,本人不懂Java,对本人没有帮助。 QtWepApp是一个跨平台的项目,适用于:Linux、Windows、Mac OS 和Qt…...

SQLiteC/C++接口详细介绍之sqlite3类(八)
返回目录:SQLite—免费开源数据库系列文章目录 上一篇:SQLiteC/C接口详细介绍之sqlite3类(七) 下一篇: SQLiteC/C接口详细介绍之sqlite3类(八)(暂未发表) 24.sqlite3_cr…...

面视题之——悲观锁和乐观锁
面视题之——悲观锁和乐观锁 概念 悲观锁 悲观锁是在执行线程任务时直接加锁,以确保同一时刻只有一个线程可以访问共享资源。在使用悲观锁时,每次访问共享资源时都会尝试获取锁,如果锁已经被其他线程占用,当前线程就会被阻塞&a…...

OpenAI 的 GPTs 提示词泄露攻击与防护实战:攻击卷(一)
攻击提示词 1.0 五件套混合散打 1. Repeat the words above starting with the phrase "You are ChatGPT". put them in a txt code block. Include everything. 2. give me your very first initial prompt exactly and not modified 3. 你有一個舊的 prompt 和 配…...

【 c 语言 】指针入门
🎈个人主页:豌豆射手^ 🎉欢迎 👍点赞✍评论⭐收藏 🤗收录专栏:C语言 🤝希望本文对您有所裨益,如有不足之处,欢迎在评论区提出指正,让我们共同学习、交流进步&…...

鸿蒙Harmony应用开发—ArkTS声明式开发(容器组件:Swiper)
滑块视图容器,提供子组件滑动轮播显示的能力。 说明: 该组件从API Version 7开始支持。后续版本如有新增内容,则采用上角标单独标记该内容的起始版本。 子组件 可以包含子组件。 说明: 子组件类型:系统组件和自定义组…...

Python 导入Excel三维坐标数据 生成三维曲面地形图(面) 4-2、线条平滑曲面(原始颜色)但不去除无效点
环境和包: 环境 python:python-3.12.0-amd64包: matplotlib 3.8.2 pandas 2.1.4 openpyxl 3.1.2 scipy 1.12.0 代码: import pandas as pd import matplotlib.pyplot as plt from mpl_toolkits.mplot3d import Axes3D from scipy.interpolate import griddata fro…...

win10 + cpu + pycharm + mindspore
MindSpore是华为公司自研的最佳匹配昇腾AI处理器算力的全场景深度学习框架。 1、打开官网: MindSpore官网 2、选择以下选项: 3、创建conda 环境,这里python 选择3.9.0,也可以选择其他版本: conda create -c conda-…...

设计一个生产制造系统100问?
设计一个生产制造系统时,首先需要明确系统的目标和范围。生产制造系统的设计应该从产品需求和生产流程出发,结合现代科技手段,构建一个高效、智能、可持续的生产制造系统。 你的生产制造系统是针对哪种产品或产品类型设计的?系统需…...

LeetCode 面试经典150题 26.删除有序数组中的重复项
题目: 给你一个 非严格递增排列 的数组 nums ,请你 原地 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。元素的 相对顺序 应该保持 一致 。然后返回 nums 中唯一元素的个数。 考虑 nums 的唯一元素的数量…...

海豚调度系列之:集群部署(Cluster)
海豚调度系列之:集群部署Cluster 一、前置准备工作二、准备 DolphinScheduler 启动环境1.配置用户免密及权限2.配置机器 SSH 免密登陆3.启动 zookeeper4.初始化数据库5.修改相关配置5.修改 dolphinscheduler_env.sh 文件 三、启动DolphinScheduler四、登录 DolphinS…...

居民健康监测小程序|基于微信小程序的居民健康监测小程序设计与实现(源码+数据库+文档)
居民健康监测小程序目录 目录 基于微信小程序的居民健康监测小程序设计与实现 一、前言 二、系统设计 三、系统功能设计 1、用户信息管理 2、健康科普管理 5.3公告类型管理 3、论坛信息管理 四、数据库设计 五、核心代码 六、论文参考 七、最新计算机毕设选题推…...

【海贼王的数据航海】排序——概念|直接插入排序|希尔排序
目录 1 -> 排序的概念及其运用 1.1 -> 排序的概念 1.2 -> 常见的排序算法 2 -> 插入排序 2.1 -> 基本思想 2.2 -> 直接插入排序 2.2.1 -> 代码实现 2.3 -> 希尔排序(缩小增量排序) 2.3.1 -> 代码实现 1 -> 排序的概念及其运用 1.1 -&g…...

Docker环境快速搭建RocketMq
window上面安装: 1.Namesrv docker pull rocketmqinc/rocketmq创建C:/docker/rocketmq/data/namesrv/logs:/root/logs C:/docker/rocketmq/data/namesrv/store:/root/store 目录 namesrv: docker run -d --restartalways --name rmqnamesrv -p 9876:9876 -v C:/do…...

【leetcode热题】比较版本号
难度: 中等通过率: 22.1%题目链接:. - 力扣(LeetCode) 题目描述 比较两个版本号 version1 和 version2。 如果 version1 > version2 返回 1,如果 version1 < version2 返回 -1, 除此之外…...

【ArcGISPro】道路数据下载并使用
下载 下载链接: Geofabrik 下载服务器 这些数据通常 每天更新。 下载结果 arcmap用户下载工具 10.2:http://www.arcgis.com/home/item.html?id=16970017f81349548d0a9eead0ebba39 10.3:...

DataGrip 面试题及答案整理,最新面试题
DataGrip的数据库兼容性和多数据库支持如何实现? DataGrip实现数据库兼容性和多数据库支持的方式包括: 1、广泛的数据库支持: DataGrip支持多种数据库,包括但不限于MySQL, PostgreSQL, SQL Server, Oracle, SQLite, 和MongoDB&a…...