Linux进程概念及其状态

news2024/10/7 10:14:06

在这里插入图片描述

文章目录

  • 🍇1. 什么是进程
    • 🍈1.1 概念
    • 🍈1.2 理解进程
  • 🍋2. Linux的PCB
  • 🍎3. 查看进程 & 杀死进程
  • 🍒4. 系统调用获取进程标识符
    • 🍓4.1 进程PID
    • 🍓4.2 父进程PPID
  • 🍆5. 系统调用创建进程:fork函数
  • 🌶6. 进程状态
    • 🫑6.1 操作系统进程状态
      • 🥒6.11 运行状态
      • 🥒6.12 阻塞状态
      • 🥒6.13 挂起状态
    • 🫑6.2 Linux状态如何维护
      • 🥬6.21 R状态
      • 🥬6.22 S状态
      • 🥬6.23 D状态
      • 🥬6.24 T & t状态
      • 🥬6.25 X状态
      • 🥬6.26 Z状态
  • 🥦7. 进程优先级
    • 🧄7.1 什么是优先级
    • 🧄7.2 为什么要有优先级
    • 🧄7.3 查看优先级
    • 🧄7.4 修改优先级
  • 🥜8. Linux内核O(1)调度算法

🍇1. 什么是进程

🍈1.1 概念

我们的一个程序要运行起来,要先加载到内存当中,如果这个程序已经加载到了内存当中,那么这就叫一个进程。

如图演示一个简单的进程:

image-20230830152140991

Windows所打开的一些进程:

image-20230830151600582

我们每次在开机的时候,需要等个几秒钟,这个过程其实就是将操作系统从外设加载内存当中

🍈1.2 理解进程

我们每次使用电脑的时候,往往都会打开很多进程,要是不用了,要么放在后台不管,要么就直接关闭了。

所以在操作系统中一般都不是只有一个进程在运行,多个进程运行,肯定会这有着不同的状态,这就需要操作系统将这些进程给管理起来,这个理念就和操作系统管理软硬件的理念一样:先描述、再组织

在形成进程的时候,操作系统就会先创建描述进程的属性结构体对象——PCB(process control block)进程控制块,而操作系统是用C语言写的,C语言描述一个对象使用的是struct

image-20230830164615678

当我们创建进程时,因为操作系统已经将进程描述好了,所以当这个进程出现时就会根据该进程的PCB类型,为改进程创建对应的PCB对象。当一个进程要运行,还需要将该进程的代码和数据加载到内存当中。

那么我们就能知道:进程=内核的PCB数据结构对象+我们所写的代码和数据

image-20230830170154333

操作系统要管理进程,本质上就是管理整个PCB数据结构对象,我们可通过这个结构体里面的指针信息从而找到这个进程的代码和数据。而多个进程,其实就是将这些结构体对象链接起来,这样就转换成了对于单链表的增删改查

image-20230830170809093

🍋2. Linux的PCB

上面讲的即是对于所有操作系统的实现原理,但原理归原理,具体的落实,不同的操作系统还是会有一些差别。

对于Linux具体的PCB叫做task_struct,它是一个大型的结构体,里面包含了Linux内核中描述进程所以的属性。

task_struct属于PCB的一种

task_struct内容分类:

  • 标识符:进程的pid
  • 状态:任务的状态
  • 优先级:相当于其他进程的优先级
  • 内存指针:找到代码和数据
  • 程序计数器:程序中即将被指向的下一条指令的地址
  • 记账信息:进程所占用的资源
  • … … …

在之后的描述中,不再说PCB,而是采用task_struct方式,用双向链表组织。

🍎3. 查看进程 & 杀死进程

查看指定进程:

ps ajx | head -1 && ps ajx | grep myprocess

image-20230830174006411

我们发现明明查看的是myprocess的进程,但还多了一个。这其实是grep命令的进程,因为它在过滤进程信息的时候,首先它得把自己变成一个进程,然后才能被cpu调度。如果不想看到这个grep进程,可采用:

ps ajx | head -1 && ps ajx | grep myprocess | grep -v grep

查看所有进程:

ls /proc

image-20230830174954850

这个指令查看的是Linux系统中正在执行的所有进程,里面也包含了我们刚刚所启动的这个“进程”示例

image-20230830175641251

Tips:

为什么我们在创建文件的时候会默认在当前目录呢?

这其实就是因为进程在启动时,有自己的工作目录,当我们创建这个文件的时候,系统会默认将这个路径拼接到该文件前面

image-20230830180321003

我们查看进程就能获取到这个进程的PID,我们可通过kill指令来杀死这个进程

kill -9 PID

kill proc

🍒4. 系统调用获取进程标识符

我们要在程序中查看当前进程的标识符,可通过getpid接口来获取。

  • 进程id:PID
  • 父进程id:PPID

image-20230830195200390

🍓4.1 进程PID

先将代码稍微改一下

image-20230830200244691

然后我们采用指令对该进程进行监控

while :; do ps ajx | head -1 ; ps ajx | grep myprocess | grep -v grep; echo "-----------------"; sleep 1; done

然后运行这个进程:

getpid

我们可以看到用ps工具查看的PID和我们自己所输出的PID是一样的

🍓4.2 父进程PPID

我们调用查看父进程标识符的接口发现,每次杀死进程之后,然后又重新启动这个进程,该进程的id会变,可是父进程的id却始终不变

ppid

ps指令查看发现,这个父进程是bashbash是命令行解释,也是就是属于“媒婆”的角色,我们所输入的一些指令进程,基本上都是bash的子进程

ps ajx | head -1 && ps ajx | grep 1680

image-20230830201357449

🍆5. 系统调用创建进程:fork函数

通过man手册查看fork函数,我们发现如果创建成功,它有2个返回值,将子进程的PID返回给父进程,然后再返回0给子进程。

image-20230830204509231

我们之前所学的函数或者自己写的函数,一般都是只有1个返回值,而这里有2个,我们可通过代码验证一下。

#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
int main()
{
  printf("begin:PID:%d PPID:%d\n",getpid(),getppid());
  pid_t id = fork();
  if(id == 0)
  {
    //子进程
    while(1)
    {
      printf("子进程,PID:%d PPID:%d\n",getpid(),getppid());
      sleep(1);
    }
  }
  else if(id > 0)
  {
    //父进程
    while(1)
    {
     printf("父进程,PID:%d PPID:%d\n",getpid(),getppid());
     sleep(1);
    }
  }
  else
  {
    //error
    
  }
    return 0;
}

运行发现,确实是同时走了2个不同的判断语句,而通过父子关系进程查看,fork函数创建出了一个子进程

fork

  • 那这里fork函数为什么要给子进程返回0,给父进程返回子进程的PID呢?

    这是为了区分让不同的执行流去执行不同的代码块,fork之后的代码是共享的;

    而这里给父进程返回子进程的PID是为了让父进程区分子进程,用来标记子进程;子进程只有一个父亲,直接调用getppid就能获得父进程的PID。

  • 那这又是如何做到返回2次呢?

    我们前面提到,进程是由内核的数据结构+代码和数据所组成的,而我们fork创建子进程之后,系统会为这个子进程创建task_struck,可是这个子进程并没有自己的代码和数据结构,所以这个子进程只能访问和父进程一样的代码,fork之后父子进程代码共享。

    image-20230830211928194

    当走到结束的时候,子进程返回一次,父进程返回一次,所以这就有了2个返回值

    image-20230831100052586

    另外呢,由于这两个进程是独立的,这也就意味着父子进程的数据,不是共享的。子进程的数据修改不影响父进程,父进程的数据修改不影响子进程。在这个过程中,如果操作系统发现子进程需要用到代码里面的数据,就会单独开一块空间给子进程,这个空间里面就有子进程所需要的数据,这种方式称为数据层面的写时拷贝

  • 那为什么要创建子进程呢?

    这是为了让父子进程执行不同的代码块,从而协同起来。

  • 如果父子进程创建好了,fork()之后,谁先运行呢?

    这个谁先运行,其实也说不准,因为这个是由系统的调度器决定的

如果我们不使用fork创建子进程,我们所写的程序,它自己也是一个子进程,它的父进程是bash,这也就说明了,bash肯定是使用到了fork

image-20230831110542144

🌶6. 进程状态

🫑6.1 操作系统进程状态

🥒6.11 运行状态

系统中会有多个进程,这些进程由双链表链接起来,而系统只需要找到这个链表的头部,即可遍历到所以的进程。

而进程是十分多的,但CPU只有一个,所以这些进程是需要去竞争这个CPU的资源的。CPU会去维护runqueue运行队列

struct runqueu
{
    //运行队列
    struct task_struct* head;
    struct task_struct* tail;
    //...
}

如果CPU要运行某个进程,就直接在运行队列当中挑选一个进程加载到内存。

image-20230831112940798

凡是处于运行队列里面的进程,我们称之为R态(运行态),但这里可能会有疑惑,明明这些进程没有运行,为什么会称为R态呢?

这是因为,这些进程已经准备好了,随调随用。

如果一个进程放到了CPU上运行,并不是一定到等到它执行完毕才会停止,每个进程都有一个时间片,比如说这个进程只能运行10ms,如果超过了时间,那么CPU会将这个进程放到队列的尾部,重新排队。

有了时间片,这就可以让队列里面的进程,在一段时间内,都会被运行,这种就称为并发执行。

将进程放入CPU,然后再拿下来切换新的进程,这个动作称为进程切换

🥒6.12 阻塞状态

操作系统管理硬件设备,是通过先描述,再组织进行管理,那么这些管理这些硬件设备也是有对应的结构体对象。

我们C语言或者C++,在使用scanf或者cin的时候,如果我们键盘不输入数据,那么这个进程就会进入键盘的等待队列,不会进入系统的运行队列,如果有其他进程也需要从键盘读取数据,那同样,进入等待队列排队。每个设备可能都会被不同的进程所访问,如果这个设备就绪了,就能读取,如果没有就绪,就进入这个设备的等待队列。如果这个设备就绪时,那么这个进程就会被唤醒,进入运行队列

这种在等待特定设备的进程,称之为该进程处于阻塞状态

image-20230831120301122

🥒6.13 挂起状态

假设有很多个进程在等待键盘这个设备输入,可是这个键盘一直不输入,然后后面进入等待队列的进程越来越多,这就会导致操作系统内部的内存资源被占用过多,导致资源不足。那操作系统就在保证正常运行的状况下,节省出资源。在排队的进程,占用了内存,但并没有做什么操作,所以操作系统会将这些进程的PCB保留,而进程的数据和代码,重新放入外设。意思就是只留一个PCB在这排队,到时候轮到这个进程了,再重新放入内存。这个过程叫做换入和换出,也就是挂起状态。这也操作系统就能节省出一大部分空间。

🫑6.2 Linux状态如何维护

kernel源码定义:

/*
* 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 */
}; 

🥬6.21 R状态

这两段代码,明明都是在运行,可是一个是S状态、一个是R状态。

image-20230831140404143

这里我们其实小看了CPU的处理速度:

  • 第一段代码,只是进行了判断,这点判断对应CPU来说并不算什么

  • 而第二段代码,需要进行输出,也是就是需要访问显示屏这个硬件设备,所以我们这个进程可能在不停地访问硬件设备,这就有极大的可能进入硬件的等待队列

  • 这里的R或者S后面跟了一个+号,这其实是说明此时的运行进程是前台运行,意思就是这个进程在运行的时候,我们输入不了其他的指令,这就是前台运行。

    image-20230831141217198

R状态就是Linux里面的运行状态

🥬6.22 S状态

C语言里面的scanf,标准输入就是一个很直观的让程序进入S状态的例子:

#include<stdio.h>
int main()
{
    int a = 0;
    printf("Enter:");
    scanf("%d",&a);
    printf("a = %d\n",a);
    return 0;
}

s

在Linux中的阻塞状态就是S状态

许多进程都是处于S状态image-20230831142214751

🥬6.23 D状态

D状态在Linux里面也是一种阻塞状态,我们称为深度睡眠。上面的S状态是浅度睡眠,浅度睡眠的进程是可以被用户或者操作系统直接唤醒的(运行或者杀死)。

而深度睡眠,是不响应操作系统的任何请求,打个比方:

进程要向磁盘写入1GB的数据,可是访问磁盘的效率十分低下,需要花费一定的时间。

在写的过程中,这个进程就一直需要等待磁盘写完,然后反馈结果。可是操作系统并不知道在写数据,感觉这个进程长时间没有响应,就把这个进程杀掉了。这就导致数据还没写完进程就被杀死了,这时候写的数据要么存起来、要么直接丢弃,大部分都是直接丢弃。那如果这个数据十分重要,则需要把这个进程设为深度睡眠状态,也就是拿一“免死金牌”,不响应系统的请求。

如果系统里面出现了几个的D状态,这就说明系统已经快崩溃了。

🥬6.24 T & t状态

T状态我们称为暂停状态,可采用指令kill -19 PID,让这个进程暂停;如果要继续运行,采用指令kiil -18 PID

image-20230831151003587

stop

我们在测试代码时用的调试,就是一个很典型的T状态:

🥬6.25 X状态

X状态是一种死亡状态,意思就是这个进程运行完毕了,也就是我们操作系统概念里面的终止状态。

🥬6.26 Z状态

当一个进程在进入死亡的时候,并不会立即进入死亡状态,而是进入Z状态,我们称为僵尸状态。

打个不恰当的比方:

某个人经常性的作息不规律、不好好吃饭,在某个晚上,突然心梗了,一下子没缓过来,死亡了。

这时候,肯定会先拨打120来现场,看看是否能救;打110来现场,检查是否有他杀嫌疑。

这一系列检查完毕,才能正式判定,此人意外心梗死亡。

而在检查的过程,并不确定,我们称为“僵尸”。

一个进程也是如此,当一个进程进入死亡状态时,操作系统会维护这个进程的状态,收集这个进程的信息,以让“关系”这个进程的父进程知道,它的子进程要死亡了。当父进程知道之后,操作系统才会正式回收这个进程,让其进入死亡状态。

我们用下面这段代码来进行测试:

#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>

int main()
{
  pid_t id = fork();
  if(id == 0)
  {
    int cnt = 5;
    while(cnt--)
    {
      printf("子进程:pid:%d ppid:%d cnt = %d\n",getpid(),getppid(),cnt);
      sleep(1);
    }
  }
  else if(id > 0)
  {                                                                                   
    while(1)
    {
      printf("父进程:pid:%d ppid:%d\n",getpid(),getppid());
      sleep(1);
    }
  }
  else
  {
    perror("fork error");
  }
  return 0;
}

僵尸进程

这里我们的父进程并没有对子进程进行处理,所以子进程这里一直是处于Z状态,它的相关信息不能被释放。这就导致了内存泄漏的问题。

那如果父进程先退出,它的子进程还在运行,会导致什么样的结果呢?

single

观察发现,当父进程先退出之后,这个子进程的父进程id变成了1

image-20230831203856870

查阅发现这个进程id为1的是操作系统进程,这种父进程为1号的进程,我们称之为孤儿进程。把这种行为称为该进程被系统所领养

因为这个进程也会退出,需要释放,所以要找“监护人”领养。

🥦7. 进程优先级

🧄7.1 什么是优先级

权限决定着能还是不能;而优先级是在能的基础上,决定着先后顺序。

🧄7.2 为什么要有优先级

打个比方:

我们去食堂吃饭,如果人比较多,是需要对排队的,先来排队的人先打饭,后到的人在后面排队。

这是一个制度,我们都遵守才会有条不紊的运行。

如果没有这个制度,将会一团糟,都想着先打饭,这也到最后谁也吃不成

这也侧面反映,人多,但是窗口不够,所以只能采取排队的方式运行

对于系统也是,大部分电脑都只有一个CPU,而那么多的进程,一个CPU肯定不能一次性全部处理,所以进程之间是有竞争关系的。

为了让这些进程“良心竞争”,所以操作系统需要确认这些进程的优先级。如果一个进程长时间得不到CPU的资源,该进程的代码长时间无法得到推进,那么就会造成进程的饥饿问题

🧄7.3 查看优先级

在Linux中要查看某个进程的优先级,使用指令ps -al查看

image-20230831213729283

  • PRI:进程优先级,值越小,优先级越高

  • NI:优先级修正数值

  • PRI+NI:PRI(new) = PRI(old)+NI,就表示这个进程新的优先级(这个PRI(old)一直都是初始的值

    当NI为负数的时候,就代表改进程的优先级提高了

    使用在Linux中,调整优先级就是调整NI的值

  • NI值的范围:[-20,19],40个级别

🧄7.4 修改优先级

我们采用top修改优先级,首先top查看进程,然后r,会看到提示

image-20230831215132458

输入要修改的进程pid,然后输入修改的值,但如果是普通用户,没有权限修改,所以要提升权限

pri

权限提升之后,我们就可以修改完成

pri2

再次查看进程的pid,修改成功

image-20230831215647257

🥜8. Linux内核O(1)调度算法

每个CPU都要维护一个运行队列

struct runqueue
{
    task_struct* running[140];	//0~99下标给其他种类进程使用
    task_struct* waiting[140];	//100~139下标给我们使用,也就是40个级别
}

这两个是指针数字,内存里面存在2张映射表,里面存放的都是进程task_struct的地址。

假设我们进程的pid是60,然后将这个结构体的地址存进这个映射表,如果之后再来同等优先级的进程,则直接排到这个进程的后面即可;如果来的是进程pid是61,那只需要排到下一个地址即可。

image-20230831222704458

这个过程中,可能还会有新的进程不断加入,然后这里有增加了2给指针,来指向运行队列和等待队列

tast_struct** run;
tast_struct** wait;

当运行队列里面的进程全部加载完毕之后,直接交换runwait指向的内容swap(&run,&wait),这也就又有了一批新的运行队列

image-20230831223152257

这里需要判断队列是否为空,需要遍历整个数组,使用里面还定义了一个成员

bitmap isempty;

用每个位置所对应的比特位是否为空,如果不为空,则找出二进制序列中最近的比特位为1的位置。

image-20230831223652220

这样就能以几乎O(1)的时间复杂度来调度。


那本期的分享就到这里咯,我们下期再见,如果还有下期的话。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

MetInfo5.0文件包含漏洞

MetInfo历史版本与文件 环境在这里下载&#xff0c;使用phpstudy搭建 我们来看到这个index.php&#xff0c;如下图所示&#xff0c;其中定义了fmodule变量与module变量&#xff0c;其中require_once语句表示将某个文件引入当前文件&#xff0c;在这个代码中&#xff0c;通过r…

【JasperReports笔记06】JasperReport报表开发之常见的组件元素(Table、Subreport、Barcode等)

这篇文章&#xff0c;主要介绍JasperReport报表开发之常见的组件元素&#xff08;Table、Subreport、Barcode等&#xff09;。 目录 一、基础组件元素 1.1、StaticText 1.2、TextField 1.3、Image 1.4、Break分页 1.5、Rectangle矩形区域 1.6、Ellipse椭圆区域 1.7、Li…

基于MQTT协议的物联网关

随着工业领域的不断发展&#xff0c;数字化转型已经成为企业迈向未来的必由之路。在这个数字化浪潮中&#xff0c;HiWoo Box以其强大的功能和创新的设计&#xff0c;在工业物联网领域被越来越多的人所熟知。特别是其基于MQTT协议的物联网关能力&#xff0c;也为企业实现智能化数…

喷泉码浅谈

01、喷泉码简介 喷泉码&#xff08;Fountain Code&#xff09;是一种在无线通信、数据传输和网络编码领域中使用的错误纠正技术。它与传统的纠错码和编码方法有所不同&#xff0c;喷泉码被设计用于在不确定信道条件下的高效数据传输。传统的纠错码&#xff08;如海明码、RS码等…

无涯教程-Android - RadioButton函数

RadioButton有两种状态:选中或未选中,这允许用户从一组中选择一个选项。 Radio Button 示例 本示例将带您完成一些简单的步骤,以展示如何使用Linear Layout和RadioButton创建自己的Android应用程序。 以下是修改后的主要Activity文件 src/MainActivity.java 的内容。 packa…

【算法】函数渐近的界基础知识及定理

创作不易&#xff0c;本篇文章如果帮助到了你&#xff0c;还请点赞 关注支持一下♡>&#x16966;<)!! 主页专栏有更多知识&#xff0c;如有疑问欢迎大家指正讨论&#xff0c;共同进步&#xff01; &#x1f525;c系列专栏&#xff1a;C/C零基础到精通 &#x1f525; 给大…

Spring依赖注入(DI)

目录 构造器注入 set注入 拓展注入 bean的作用域 Singleton Prototype Dependency Injection 依赖 : 指Bean对象的创建依赖于容器 . Bean对象的依赖资源 . 注入 : 指Bean对象所依赖的资源 , 由容器来设置和装配 . 构造器注入 具体实现&#xff1a;SpringIOC创建对象的…

电力行业浪涌保护器应用方案

电力行业是一个涉及到高压、大电流、复杂环境的领域&#xff0c;对于电气设备的安全和可靠性有着极高的要求。在电力行业中&#xff0c;浪涌是一种常见的电力质量问题&#xff0c;它指的是在电气系统中出现的瞬时过电压或过电流&#xff0c;可能由雷击、开关操作、短路故障等因…

算法通关村——解析堆的应用

在数组中找第K大的元素 LeetCode21 Medium 我们要找第 K 大的元素&#xff0c;如果我们找使用大堆的话那么就会造成这个堆到底需要多大的&#xff0c;而且哪一个是第 K 的的元素我们不知道是哪一个索引&#xff0c;我们更想要的结果就是根节点就是我们要找的值&#xff0c;所以…

java之SpringBoot基础、前后端项目、MyBatisPlus、MySQL、vue、elementUi

文章目录 前言JC-1.快速上手SpringBootJC-1-1.SpringBoot入门程序制作&#xff08;一&#xff09;JC-1-2.SpringBoot入门程序制作&#xff08;二&#xff09;JC-1-3.SpringBoot入门程序制作&#xff08;三&#xff09;JC-1-4.SpringBoot入门程序制作&#xff08;四&#xff09;…

cocosCreator 之 微信小游戏打包

版本&#xff1a; v3.8.0 环境&#xff1a; Mac 介绍 cocosCreator 支持将游戏发布到多个小游戏平台&#xff0c;并提供了打包等流程处理。 本篇文章主要讲述下微信小游戏的发布流程相关。更多内容参考官方文档&#xff1a; 发布到小游戏平台 微信小游戏的发布相关&#xff…

2023-8-31 Floyd求最短路

题目链接&#xff1a;Floyd求最短路 #include <iostream> #include <algorithm> #include <cstring>using namespace std;const int N 210, INF 1e9;int n, m, Q;int d[N][N];void floyd() {for(int k 1; k < n; k)for(int i 1; i < n; i)for(int …

并发编程的故事——并发之共享模型

并发之共享模型 文章目录 并发之共享模型一、多线程带来的共享问题二、解决方案三、方法中的synchronize四、变量的线程安全分析五、习题六、Monitor七、synchronize优化八、wait和notify九、sleep和wait十、park和unpark十一、重新理解线程状态十二、多把锁十三、ReentrantLoc…

瓜分双十一10亿红包设计:在线分享教程?

在如今激烈的市场竞争中&#xff0c;瓜分红包营销活动成为了各大企业争相使用的一种营销手段。这种活动不仅能够吸引用户的关注和参与&#xff0c;还能够提高用户的粘性和忠诚度。那么&#xff0c;如何自建瓜分红包营销活动呢&#xff1f;下面将为大家详细解析。 首先&#xff…

振动智能监测与设备可靠性:无线技术的契机

在现代工业领域&#xff0c;设备的可靠性和稳定运行对于生产效率和安全性至关重要。然而&#xff0c;由于设备的频繁使用和各种环境影响&#xff0c;设备故障和突发停机仍然是不可避免的挑战。为了有效地应对这些挑战&#xff0c;振动智能监测技术结合无线传感器的应用正在成为…

FPGA可重配置原理及实现(1)——导论

一、概述 可重配置技术是Xilinx提供的用来高效利用FPGA设计资源实现FPGA资源可重复利用的最新的FPGA设计技术&#xff0c;这种技术的发展为FPGA应用提供了更加广阔的前景。 术语“重构”是指FPGA已经配置后的重新编程。FPGA的重构有两种类型&#xff1a;完全的和部分的。完全重…

【附安装包】Substance3D 2022安装教程

软件下载 软件&#xff1a;Substance3D版本&#xff1a;2022语言&#xff1a;简体中文大小&#xff1a;4.0G安装环境&#xff1a;Win11/Win10&#xff08;1809版本以上&#xff09;硬件要求&#xff1a;CPU2.0GHz 内存4G(或更高&#xff0c;不支持7代以下CPU&#xff09;下载通…

即时通讯开发应用中的实时消息推送技术

即时通讯开发领域正以前所未有的速度蓬勃发展&#xff0c;实时消息推送技术成为促进即时通讯应用体验的关键要素。本文将深入探讨即时通讯应用中的实时消息推送技术&#xff0c;为读者呈现这一领域的全貌。 2. 实时消息推送的重要性 在当今数字化时代&#xff0c;人们日益需要…

git 提交错误,回滚到某一个版本

git log 查看版本号 commit 后面跟的就是版本号git reset --hard 版本号 &#xff08;就可以回滚到你要去的版本&#xff09;git push -f &#xff08;因为本地回滚了&#xff0c;所以和远程会差几个版本。所以这时候只有强制推送&#xff0c;覆盖远程才可以&#xff09;

实力认证!OceanBase获“鼎信杯”优秀技术支撑奖

6 月 30 日&#xff0c;2023 “鼎信杯”信息技术发展论坛在京隆重举办第二届“鼎信杯”大赛颁奖典礼。OceanBase 凭借完全自主研发的原生分布式数据库&#xff0c;以及丰富的核心系统国产数据库升级案例&#xff0c;斩获“优秀技术支撑奖”。 论坛上&#xff0c;国内首个基于在…