面试常问【进程间通信】最详细解说

news2024/11/25 11:06:35

文章目录

  • 目录

    文章目录

    一、进程间有哪几种的通信方式

    1.为什么需要进程间通信?

    2.进程通信的几种方式

    3.进程间通信的目的

    二、管道

    1.匿名管道

    2.命名管道

    3.管道总结

    三、消息队列

    四、共享内存

    1.共享内存的原理

    2.共享内存的接口

    1.创建共享内存

    2.将共享内存附加到进程

    3.将共享内存和进程分离

    4.操作共享内存

    5.删除共享内存

    3.共享内存总结

    五、信号量(难点)

    六、信号

    七、socket通信

    1. UDP-socket编程

     2.tcp-socket编程

    八、总结


    前言

我们要了解 进程间是如何通信的,那么就需要了解进程是什么。

进程:其实进程就是运行起来的程序,程序运行起来需要被加载到内存中。进程和可执行文件很像(文件名.exe)->这就是可执行文件.但是他们又有所不同.可执行文件就像是静态的,躺在我们的硬盘中,但是,我们在任务管理器中可以明显的看到我们的进程是动态的,是在内存中不断被加载的.


 我们可以清楚的看到,进程他是一个运行的程序,是在内存中不断被加载的。那么这些进程是如何通信的?

一、进程间有哪几种的通信方式

1.为什么需要进程间通信?

         每一个进程都拥有自己的独立的进程虚拟地址空间,造成了进程独立性,从而进程间通信技术就是为了各个进程之间可以很好的交换数据或者进程控制等应运而生的
·目前,我们所见到的最大的进程间通信技术是 : 网络

2.进程通信的几种方式

  进程间的通信有  管道 消息队列 共享内存 信号  套接字。

我们常见的面经都会说到进程间通信主要包括管道系统IPC(包括消息队列、信号量、信号、共享内存)、套接字socket

  1. 管道:包括无名管道和命名管道,无名管道半双工,只能用于具有亲缘关系的进程直接的通信(父子进程或者兄弟进程),可以看作一种特殊的文件;命名管道可以允许无亲缘关系进程间的通信。

  2. 系统IPC

    消息队列:消息的链接表,放在内核中。消息队列独立于发送与接收进程,进程终止时,消息队列及其内容并不会被删除;消息队列可以实现消息的随机查询,可以按照消息的类型读取。

    信号量semaphore:是一个计数器,可以用来控制多个进程对共享资源的访问。信号量用于实现进程间的互斥与同步。

    信号:用于通知接收进程某个事件的发生。

    内存共享:使多个进程访问同一块内存空间。

  3. 套接字socket:用于不同主机直接的通信。

3.进程间通信的目的

数据传输:一个进程需要将它的数据发送给另一个进程

资源共享:多个进程之间共享同样的资源。

通知事件:一个进程需要向另一个或一组进程发送消息,通知它(它们)发生了某种事件(如进程终止 时要通知父进程)。

进程控制:有些进程希望完全控制另一个进程的执行(如Debug进程),此时控制进程希望能够拦截另 一个进程的所有陷入和异常,并能够及时知道它的状态改变。

但是我们该如何理解。

二、管道

1.匿名管道

$ ps auxf | grep mysql      了解linux的朋友肯定熟悉   【|】这个符号  其实竖线就是一个管道,管道传输数据是单向的,如果想相互通信,我们需要创建两个管道才行,这种管道是没有名字,所以【|】表示的管道称为匿名管道,用完了就销毁。

  匿名管道的创建:

int pipe(int fd[2])

          这里表示创建一个匿名管道,并返回了两个描述符,一个是管道的读取端描述符 fd[0],另一个是管道的写入端描述符 fd[1]。注意,这个匿名管道是特殊的文件,只存在于内存,不存于文件系统中。

 

我们可以看到,管道就是一端写入数据,另一端读取。 所谓的管道,就是内核里面的一串缓存。从管道的一段写入的数据,实际上是缓存在内核中的,另一端读取。

 看到这,你可能会有疑问,两个进程都在一个进程里,怎么实现通信的。

我们可以使用 fork 创建子进程,创建的子进程会复制父进程的文件描述符,这样就做到了两个进程各有两个「 fd[0] 与 fd[1]

 这样就实现了不同进程之间的通信,但是问题又来了,因为管道是只能一端写一段读,如果两个进程同时写的话,那岂不是会混乱。

所以我们一般会这样:

  • 父进程关闭读取的 fd[0],只保留写入的 fd[1];
  • 子进程关闭写入的 fd[1],只保留读取的 fd[0];

 通过以上我们知道:匿名管道是一个半双工的通信方式,就是一个发 另一个读。并且只能在具有亲缘关系的进程之间通信。很不方便。于是我们有了命名管道

2.命名管道

命名管道:需要通过 mkfifo 命令来创建并指定好名字。相当提前创建了一个类型为管道的设备文件,在进程里只要使用这个设备文件,就可以相互通信。

$ mkfifo Pipe

Pipe就是这个管道的名字。

接下来,我们往 Pipe 这个管道写入数据:

$ echo "hello" > Pipe  // 将数据写进管道
                         // 停住了 ...

你操作了后,你会发现命令执行后就停在这了,这是因为管道里的内容没有被读取,只有当管道里的数据被读完后,命令才可以正常退出。

于是,我们执行另外一个命令来读取这个管道里的数据:

$ cat < Pipe  // 读取管道里的数据
hello

可以看到,管道里的内容被读取出来了,并打印在了终端上,另外一方面,echo 那个命令也正常退出了。

3.管道总结

我们发现,不管是匿名管道还是命名管道,都是半双工的通信方式,并且只能一方写,另一方读。通信效率非常低下,所以我们又引出了消息队列


三、消息队列

我们说到,管道的通信效率低下,不适合进程间频繁的交流,于是消息队列很好的解决了这个问题。

       消息队列,就是一个消息的链表,是一系列保存在内核中消息的列表。当一个进程需要通信的时候,只需要将数据写入这个消息列表当中,就可以正常退出干其他事情了,另一个进程需要数据的时候只需去读取数据就行了。

         消息队列是保存在内核中的消息链表,在发送数据时,会分成一个一个独立的数据单元,也就是消息体(数据块),消息体是用户自定义的数据类型,消息的发送方和接收方要约定好消息体的数据类型,所以每个消息体都是固定大小的存储块,不像管道是无格式的字节流数据。如果进程从消息队列中读取了消息体,内核就会把这个消息体删除。

1.队列的特性是先进先出,消息队列也是满足先进先出的特性的,内核当中实现消息队列的时候,是采用链表这个结构体
2.消息队列当中的元素是有类型的,每一种类型是有优先级概念的

同一类型保证先进先出的特性;

int msgget(key_t key, int msgflg):
int msgsnd(int msqid, const void *msgp, size t msgsz, int msgflg);


//msgflg :
//       IPC NOWAIT : 非阻塞模式
//                 0: 阻塞模式

ssize_t msgrcv(int msqid, void *msgp, size t msgsz, long msgtyp,int msgflg);

//  long msgtype : 数据类型
//  ==0:取队列当中的第一个
//  > 0:取和msgtypc相等的元素
//  < 0: 先取绝对值,然后在范围内去最小的优先级的元素

int msgctl(int msqid, int cmd, struct msqid ds *buf);

结论:消息队列的生命周期也是跟随内核.

 消息队列的常用函数如下表:

        消息队列与管道通信相比,其优势是对每个消息指定特定的消息类型,接收的时候不需要按照队列次序,而是可以根据自定义条件接收特定类型的消息。声明周期跟随内核,如果不去释放或者关闭操作系统,它会一直存在。

          综上说所,消息队列很好的解决了管道不能频繁交流的问题,但是也存在了不足,就是交流不及时,还有大小受限制。并且在通信的过程中,会存在大量频繁的用户态和内核态的不断转换,进程写入消息时,会发生用户态拷贝数据到内核态的过程,同理另一进程读取内核中的消息数据时,会发生从内核态拷贝数据到用户态的过程。

为了更好的解决这一问题,于是我们就有了 共享内存.........

四、共享内存

1.共享内存的原理

1. 首先在物理内存当中创建了一块内存
1. 不同的进程通过页表映射, 将同一块物理内存映射到自己的虚拟地址空间
1.3不同的进程, 操作进程虚拟地址, 通过页表的映射, 就相当于操作同一块内存,从而完成了数据交换

 

2.共享内存的接口


1.创建共享内存


int shmget(key_t key, size t size, int shmflg);
/*
    key :共享内存标识符, 这个标识符相当于共享内存的身份证 程序员在第一次创建的时候, 可以随意给 值, 只要和当前操作系统当中的其他的共享内存标识符不重复

//     eg : 0x99999999 0x88888888 0x12345678
//     size : 共享内存的大小,单位字节
//  shmrlg :
//       IPC CREAT : 如果共享内存不存在, 则创建共享内存
//        IPC_EXCL: 需要搭配IPC CREAT一起使用, 这样的宏在搭配使用的时候,  还是采用按位或的方式 
//其实核心的思想就是位图

//     eg :IPC CREAT IPC EXCL:
//           如果想要获取的共享内存,已经存在, 则报错如果想要过去的共享内存,  是刚刚新创建出来的 
  //共享内存,则返回舞作句柄


       总结:使用shmget这个函数的时候一定更自己刚刚创建出来的共享内存
       返回值:返回值是返回共享内存的操作句柄
    共享内存的标识符和共享内存的操作句柄区别是什么?

     标识符:是用来标识共享内存的, 相当于共享内存的身份证,意味者不同的进程可以通过标识符找到这个共享内存
     操作句柄:进程可以通过操作句柄来对共享内存进行操作(附加, 分离, 删除)
*/

2.将共享内存附加到进程

void *shmat (int shmid, const void *shmaddr, int shmflg);
/*

    shmid : 共享内存操作句柄
    shmaddr : 附加到共享内存的什么虚拟地址, 允许传递NULL值, 让操作系统帮我们选择附加到共享区当中的那个地址, 这个地址通过该函数的返回值返回给我们

    shmflgt:
    SHM RDONLY 规定当前进程只能对共享内存进行读操作
    0 : 可读可写

    返回值: 返回共享内存附加到共享区的地址
*/

3.将共享内存和进程分离

int shmdt (const void *shmaddr) ;
//shmaddr : shmat的返回值

4.操作共享内存

int shmctl (int shmid, int emd, struct shmid ds *buf) ;
/*
    shmid : 共享操作句柄
    
    cmd : 告诉shmct1函数需要做什么操作
         IPC STAT :获取当前共享内存的属性信息,放在buf当中, buf是出参
          IPC SET :设置共享内存的属性信息, 是用buf来进行设置的, bur是入参
          IPC RMID :删除共享内存, buf可以直接传递为NULL
    buf : 共享内存的结构体

    
*/

5.删除共享内存

1.   当使用shmct1或者使用ipcrm,删除共享内存之后, 共享内存就实际被释
放掉了

2.   当共享内存被群放掉之后, 共享内存的标识符会被设置成为0x00000000.表示其他进程不能通过之前的标识符找到该共享内存, 并且共享内存的状态会被设置成为dest (destroy)
3.   当共享内存被群放掉之后了, 但是还是有进程在附加着共享内尺,当前描述共享内存的结构体并没有被释放, 直到当前共享内存的附加进程数量为0的时候才会被释放掉

3.共享内存总结

      共享内存的机制,就是拿出一块虚拟地址空间来,映射到相同的物理内存中。这样这个进程写入的东西,另外一个进程马上就能看到了,都不需要拷贝来拷贝去,传来传去,大大提高了进程间通信的速度。但是也引入了一个问题,就是如果多个进程同时修改同一个共享内存,很有可能就冲突了。为了解决这一问题我们又引入了信号量的概念

五、信号量(难点)

       为了防止多个进程同时访问公共资源,造成数据混乱,必须想一个保护机制,使得共享的资源,在任意时刻只能被一个进程访问。于是便提出了信号量。

      信号量(semaphore)它是一个计数器。信号量用于实现进程间的互斥与同步,而不是用于存储进程间通信数据。

控制信号量的方式有两种原子操作:

        一个是 P 操作,这个操作会把信号量减去 -1,相减后如果信号量 < 0,则表明资源已被占用,进程需阻塞等待;相减后如果信号量 >= 0,则表明还有资源可使用,进程可正常继续执行。
        另一个是 V 操作,这个操作会把信号量加上 1,相加后如果信号量 <= 0,则表明当前有阻塞中的进程,于是会将该进程唤醒运行;相加后如果信号量 > 0,则表明当前没有阻塞中的进程;

 

进程互斥访问资源   初始化信号量为 1

      假如有A B两个进程  信号量初始化为 1, 现在A 要访问共享内存 进行了p操作,信号量变为 0,说明还有可用资源,A就可以顺利访问共享资源了。

      结果这会B进程又要来访问资源,进行了p操作,信号量变成了-1,说明资源已经有进程占用了,那么B进程就会阻塞等待。 

      A进程这会访问完了,出来了,进行了V操作,信号量变成了 0 ,看见了B在哪等待,于是唤醒了B,说你可以进去了,于是B就可以访问了,访问完之后,进行V操作,信号量又变回了1。

可以发现,信号初始化为 1,就代表着是互斥信号量,它可以保证共享内存在任何时刻只有一个进程在访问,这就很好的保护了共享内存。

进程同步访问资源  初始化信号量为 0

    我们都知道,进程是抢占式占用资源,但是我们有时想让多个进程相互合作,实现同一个任务,比如先让A进程生产数据,B进程才能读取数据。但是我们不知道到底那个进程先抢占了资源。假如A还没有生产数据呢,但是B进程又要读取,我们该如何做?

于是我们边有了进程同步:

   我们可以初始化信号量为 0:

      如果进程 B 比进程 A 先执行了,那么执行到 P 操作时,由于信号量初始值为 0,故信号量会变为 -1,表示进程 A 还没生产数据,于是进程 B 就阻塞等待;
      接着,当进程 A 生产完数据后,执行了 V 操作,就会使得信号量变为 0,于是就会唤醒阻塞在 P 操作的进程 B;
      最后,进程 B 被唤醒后,意味着进程 A 已经生产了数据,于是进程 B 就可以正常读取数据了。
 

可以发现,信号初始化为 0,就代表着是同步信号量,它可以保证进程 A 应在进程 B 之前执行。

总结:信号量不是用来通信的,信号量是和共享内存结合,来限制多进程同时访问共享资源的。防止冲突的一种保护机制

六、信号

我们上述说到的都是正常情况下的进程通信,那么如果进程出现异常了呢,这个时候我们就需要用信号通知的方式,实现通信。

使用 kill -l 命令可以罗列信号 

 总共定义了62个信号。

非实时信号: 非可靠信号

特点: 信号可能会丢失

1~31信号

实时信号:可靠信号

特点: 信号不会丢失

33~64

说到这里,我们发现上面的所有进程间通信方式都只是局限在本机,那我我们自然而然的就引出了网络Socket 通信,实现不同电脑中的进程通信。

七、socket通信

这里如果网络不太了解的话可以看看这篇博客https://blog.csdn.net/qq_45615577/article/details/117260949?spm=1001.2014.3001.5501

1. UDP-socket编程

编程流程:

服务端:创建套接字,绑定地址信息

客户端:创建套接字,不推荐绑定地址信息 (可以绑定 )

 

创建套接字的含义:

将进程和网卡进行绑定,进程可以从网卡当中接收数据,也可以通过网卡发送数据。

绑定地址信息的含义:

绑定ip,绑定端口,是为了在网络当中可以标识出来一台主机和一个进程。
对于接收方而言:发送数据的人就知道接收方在那台机器那个进程了

对于发送方而言:能标识网络数据从那台机器那个进程发送出去的

 2.tcp-socket编程

服务端: 创建套接字,绑定地址信息,监听,获取新连接,收发数据,关闭连接。

客户端: 创建套接字, 不推荐绑定地址信息 (可以绑定),发起连接,收发数据,关闭连接。

 监听的含义:监听tcp客户端新的链接, 同客户端建立tcp连接。 注意: 这个时候,TCP连接的建立在内核当中就完成了。

获取新连接的含义:获取新连接的套接字描述符, 每一个TCP连接会产生一个套接字描述符。

目前,我们所见到的最大的进程间通信技术是 : 网络

八、总结

如果面试官问你说说,进程间是如何通信的,你该做一下回答。

首先  进程间的通信有  管道 消息队列 共享内存 信号  套接字。

管道分为 匿名管道和命名管道。

匿名管道 :他是一个半双工的通信方式,就是一个发 另一个读。并且只能在具有亲缘关系的进程之间通信。很不方便,于是便有了命名管道: 同理命名管道也是一个半双工的通信方式,一个发 另一个读。但是可以实现不同进程之间的通信了。

这又产生了问题,就是这个通信不迅速,效率低下又成了问题,于是又产生出了消息队列。

消息队列:消息队列是保存在内核中的消息链表,比如,A 进程要给 B 进程发送消息,A 进程把数据放在对应的消息队列后就可以正常返回了,B 进程需要的时候再去读取数据就可以了。同理,B 进程要给 A 进程发送消息也是如此。这样就是像写信一样,一来一封,我回一封,而且消息队列在内核当中,我们去读取的时候,还会有用户态和内核态之间的转换,效率虽然说有所改变,但是还是不够及时。所以共享内存又出来了。

共享内存:就是两个进程,各拿出一块虚拟内存空间,映射到相同的物理空间这样一个进程在进行读写操作的时候,另外一个进程立马就可以看到。通信的效率大大提高,但是又带来了新的问题。就是如果遇到两个或者多个进程同时给这个空间写东西,就会产生冲突。

那么为了防止多个进程同时共享资源,就提出了信号量,使得在任意时刻资源只能被一个进程访问。信号量说白了就相当于一个计数器,有两个操作:(他不能通信只能配合共享内存)

P操作:相当于每来一个进程要访问的时候,先给信号量减1,减1之后  如果信号量还>>=0  说明这会这个资源还没有占用可以访问,如果减一之后<0,说明有其他进程正占用着资源,需要等待。

V操作:每个进程要走的时候,先给信号量+1  如果说+1了之后,还<<=0,说明前面还有排队的进程,这个时候就会把前面排队的进程唤醒,说我走了,你可以去访问资源了。如果+1>0   说明前面没有排队的进程,也就是没有阻塞的进程,如果后面有进程要访问的话,直接就可以访问。

说了这些都是在一台主机上的进程间的通信,那么也产生出了Socket,也就是套接字。这实现了不同电脑上的进程通信。

最后还有个  信号: 信号就是可以给进程发送一个命令,进程就会做相应的工作。

说到这里就结束了,有问题欢迎大家指正。

 

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

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

相关文章

HBase---Hbase安装(单机版)

Hbase安装单机版 文章目录Hbase安装单机版Master/Slave架构安装步骤配置Hbase1.上传压缩包解压更名修改hbase-env.sh修改hbase-site.xml配置HBase环境变量配置Zookeeper复制配置文件修改zoo.cfg配置文件修改myid配置Zookeeper环境变量刷信息配置文件启动hbase步骤hbase shellMa…

BS系统中的安全方案(SSO和Oauth2认证,数据加密)

摘要用户用浏览器打开网站&#xff0c;DNS会根据域名找到相应的服务器IP给到浏览器&#xff0c;仅接着用户的浏览器会与服务器建立连接&#xff0c;通过网路上的各个设备(交换机、路由器、基站、光纤等)&#xff0c;将服务器上的数据发送到用户的电脑上&#xff0c;在浏览器里呈…

运输层概述及web请求

运输层 运输层概述 运输层向高层用户屏蔽了下面网络核心的细节&#xff08;如网络拓扑、所采用的路由选择协议等&#xff09;它使应用进程看见的就好像是在两个运输层实体之间有一条端到端的逻辑通信信道&#xff1b; 根据需求不同&#xff0c;运输层提供两种运输协议 面向连…

buffer它到底做了个啥,源码级分析linux内核的文件系统的缓冲区

最近一直在学习linux内核源码&#xff0c;总结一下 https://github.com/xiaozhang8tuo/linux-kernel-0.11 自己整理过的带注释的源码。 为什么要有buffer ​ 高速缓冲区是文件系统访问块设备中数据的必经要道(PS:如果所有程序结果都不落盘&#xff0c;只是int a, a直接在主存…

TryHackMe-Debug(ez php反序列化)

Debug Linux机器CTF&#xff01;您将了解枚举&#xff0c;查找隐藏的密码文件以及如何利用php反序列化&#xff01; 端口扫描 循例&#xff0c;nmap Web枚举 进到web是apache默认页面&#xff0c;直接开扫 由于题目告诉我们涉及php反序列化&#xff0c;那直接找php文件来看&…

Linux学习第十四节-shell脚本

1.Shell概述 Shell连接了用户和Linux内核&#xff0c;他可以解释用户输入的命令传输给内核&#xff0c;让用户可以更加方便的使用Linux系统&#xff1b; Shell本身并不是内核的一部分&#xff0c;他只是站在内核的基础上编写一个应用程序&#xff1b; Shell具备编程的能力&a…

C++回顾(八)—— 继承

8.1 继承的概念 继承是类与类之间的关系&#xff0c;是一个很简单很直观的概念&#xff0c;与现实世界中的继承类似&#xff0c;例如儿子继承父亲的财产。 继承&#xff08;Inheritance&#xff09;可以理解为一个类从另一个类获取成员变量和成员函数的过程。例如类 B 继承于类…

jconsole远程linux下的tomcat

修改Tomcat的配置 进去 Tomcat 安装目录下的 bin 目录&#xff0c;编辑 catalina.sh vi catalina.sh定位到 ----- Execute The Requested Command ----------------------------------------- vi 编辑模式下&#xff0c;点击 Esc&#xff0c;输入 / &#xff0c;然后粘贴 -…

什么是Netty

一&#xff0e;Netty介绍 1.什么是netty Netty 是由 JBOSS 提供的一个 Java 开源框架。Netty 提供异步的、基于事件驱动的网络应用程序框架&#xff0c;用以快速开发高性能、高可靠性的网络 IO 程序,是目前最流行的 NIO 框架&#xff0c;Netty 在互联网领域、大数据分布式计算…

自动驾驶介绍系列 ———— 看门狗

文章目录硬件看门狗软件看门狗差异分析延申窗口看门狗硬件看门狗 硬件看门狗的本质上是一个定时器电路。通常存在一个输入&#xff0c;输入到MCU的RST端。在正常工作状态下&#xff0c;MCU每隔固定时间间隔会输出一个信号给RST端&#xff0c;实现对看门狗端清零。如果在指定的时…

RK3568驱动OV13850摄像头模组调试过程

摄像头介绍品牌&#xff1a;Omnivision型号&#xff1a;CMK-OV13850接口&#xff1a;MIPI像素&#xff1a;1320WOV13850彩色图像传感器是一款低电压、高性能1/3.06英寸1320万像素CMOS图像传感器&#xff0c;使用OmniBSI?技术提供了单-1320万像素&#xff08;42243136)摄像头的…

C++20 协程体验

1 介绍协程是比线程更加轻量级并发编程方式&#xff0c;CPU资源在用户态进行切换,CPU切换信息在用户态保存。协程完成异步的调用流程&#xff0c;并对用户展示出同步的使用方式。协程的调度由应用层决定&#xff0c;所以不同的实现会有不同的调度方式&#xff0c;调度策略比较灵…

麻雀算法SSA优化LSTM长短期记忆网络实现分类算法

1、摘要 本文主要讲解&#xff1a;麻雀算法SSA优化LSTM长短期记忆网络实现分类算法 主要思路&#xff1a; 准备一份分类数据&#xff0c;数据介绍在第二章准备好麻雀算法SSA&#xff0c;要用随机数据跑起来用lstm把分类数据跑起来将lstm的超参数交给SSA去优化优化完的最优参数…

Python可变对象与不可变对象的浅拷贝与深拷贝

前言 本文主要介绍了python中容易面临的考试点和犯错点&#xff0c;即浅拷贝与深拷贝 首先&#xff0c;针对Python中的可变对象来说&#xff0c;例如列表&#xff0c;我们可以通过以下方式进行浅拷贝和深拷贝操作&#xff1a; import copya [1, 2, 3, 4, [a, b]]b a …

小众实用!5款不为人知的Windows软件,让你工作更轻松

分享5款冷门但值得下载的Windows软件&#xff0c;个个都是实用&#xff0c;你可能一个都没见过&#xff0c;但是 我觉得你用过之后可能就再也离不开了。 1.键盘可视化——Keyviz Keyviz是一款免费开源的小工具&#xff0c;它的作用是可以实时展示键盘的操作&#xff0c;就可以…

编程语言分类

目录 ❤ 机器语言 机器语言的编程 ❤ 汇编语言 ❤ 高级语言(编程语言) 编译型 解释型 ❤ 动态语言和静态语言 ❤ 强类型定义语言和弱类型定义语言 ❤ 主流语言介绍 C语言 C java python JavaScript SQL PHP python从小白到总裁完整教程目录:https://blog…

浅入浅出keepalived+mysql实现高可用双机热备

当数据库发生宕机的情况&#xff0c;如果配置了数据库主从同步模式或主主同步模式&#xff0c;则可以从从库中获取数据。 当数据库发生宕机的情况&#xff0c;要求应用系统实现高可用&#xff0c;应用系统不会受到影响&#xff0c;需要对mysql进行双机热备实现数据库的高可用。…

断点调试(debug)

目录 F8案例 ​编辑 debug过程中报错 ​编辑用debug查看方法源码 一层一层查看 Arrays.sort()方法 F9 DebugExercise 介绍&#xff1a;断点调试是指在程序的某一行设置一个断电&#xff0c;调试时&#xff0c;程序运行到这一行就会停住&#xff0c;然后可以一步步往下调试…

微服务引擎 MSE 企业版全新升级

作者&#xff1a;流士 随着企业应用大规模云上迁徙与应用微服务化步伐加快&#xff0c;微服务治理的重要性对企业不言而喻&#xff0c;但微服务治理本身的规范化与标准化尚未形成&#xff0c;导致很多企业在微服务治理方面正经历着痛苦的试错期&#xff0c;甚至难以满足线上环境…

工作日志day03

同时构建静态和动态库 //如果用这种方式&#xff0c;只会构建一个动态库&#xff0c;虽然静态库的后缀是.a ADD_LIBRARY(hello SHARED ${LIBHELLO_SRC}) ADD_LIBRARY(hello STATIC ${LIBHELLO_SRC}) //修改静态库的名字&#xff0c;这样是可以的&#xff0c;但是我们往往希望他…