当前位置: 首页 > news >正文

Linux:理解信号量以及内核中的三种通信方式

文章目录

  • 共享内存的通信速度
  • 消息队列
    • msgget
    • msgsnd
    • msgrcv
    • msgctl
  • 信号量
    • semget
    • semctl
  • 内核看待ipc资源
    • 单独设计的模块
    • ipc资源的维护
  • 理解信号量
  • 总结

本篇主要是基于共享内存,延伸出对于消息队列和信号量,再从内核的角度去看这三个模块实现进程间通信

共享内存的通信速度

共享内存是所有进程间通信里面速度最快的,并且是没有质疑的,何以见得这个结论呢?

要说这个结论,还要从管道说起,对于进程来说,如果想要使用管道进行通信,那么首先要有进程的PCB和对应的进程地址空间,其次如果选择管道进行通信,那么就会建立管道之后,将用户自己的数据交给另外一个进程,通过write的系统调用写到缓冲区中,本质上就是把用户级的缓冲区直接经过系统调用拷贝到内核的管道中,对于读端来说,就是用read系统调用再读取信息,相当于是把内核中的数据经过read系统调用,把数据拷贝到用户空间内,此时就完成了从进程a到进程b数据通信的目的

而对于共享内存来说,创建好共享内存之后,经过挂接在自己的地址空间的共享区中就能找到这个区域,之后就可以在这个共享内存中进行写入,写入后共享内存这里就会立刻有这段信息,在这个过程中是没有任何系统调用的,写的信息立刻就能看到,没有任何延误,换而言之,这两个进程之间不需要通过内核进行拷贝,甚至都不需要用户定义的缓冲区,直接向共享内存中写入信息,用户立刻就能看到,可以理解为是把用户级别的缓冲区合并成一个缓冲区,只要进程a向里面写,进程b立刻就能看到,有效的减少了数据通信过程中经由数据拷贝造成的消耗问题

因此我们说,对于共享内存来说,它的效率体现在可以减少数据拷贝,不需要经过用户到内核,内核到用户这样的拷贝,而是直接在用户的层面上,进程a向共享内存写信息,进程b就能看到,甚至可能都不需要缓冲区的概念,所以是高效的

那能减少多少次拷贝呢?

对于这个问题来说,这里简单做个解释,具体可能会因为实际场景和配置略有差距

假设现在有输入的函数需求,数据也从键盘输入了,那么从文件的角度来讲,就相当于从文件里面读取了信息,而从文件中读取信息也算是一层拷贝,而现在有输出的需求,需要把数据输出到显示器上,而现在的数据是存储到内存或者是缓冲区中,而从内存刷新到外设这个过程,也算是一层拷贝,而实际上,CPU只认识内存,所以想要让CPU处理数据,就必须把外设的信息加载到内存中,再从内存处理后刷新到外设上,而这个过程实际上就是把数据从一个设备拷贝到另外一个设备上,凡是数据迁移,都可以看成是拷贝,所以对于管道文件来说,不管是write系统调用还是read系统调用,都是和内核进行交互,从内核交互的角度来讲,这两个函数其实就是一个拷贝的函数,那么来回进行数据的拷贝,效率自然不能和共享内存比

那么回到这个问题,到底可以减少多少次拷贝?假设现在有硬件,那么从键盘到显示器这个过程,输入数据是需要用户自己提供缓冲区,把数据从键盘读取到缓冲区中,再把数据从缓冲区拷贝到管道当中,而最终目标是要打印到显示器上,那显示器也是有对应的缓冲区的,所以就把数据再拷贝到缓冲区中,最终就能写到显示器中,这么一套理论,保守的来讲都有四次的拷贝过程,如果把语言本身提供的缓冲区也加上,只会比这个过程更多

在这里插入图片描述

那如果对于共享内存来说呢?从键盘中读取的数据,直接写到共享内存中,读取的进程只需要把共享内存中的数据显示到显示器上就可以了,此时就相当于第一次拷贝,将数据从外设写到共享内存中,第二次把共享内存中的数据写到显示器上,两次拷贝就刷新过去了,直接就省去了内核之间的拷贝过程,就算不考虑语言级别的缓冲区,也能减少两次拷贝

在这里插入图片描述

消息队列

消息队列提供一个进程给另外一个进程发送数据块的能力,那如何理解这句话呢?先画出下面的示意图

在这里插入图片描述
这是对于消息队列画出的最基本的示意图,在ipc资源当初被设计的时候,能够在内核层面上创建共享内存,创建对应的结构来管理这样的共享内存,那在操作系统层面上也可以创建一个队列,这个队列的名字就叫做msg_queue,这个队列刚开始是空的,但是用户是有数据的,通过一定的接口传递到队列中,此时就会在队列的底层形成一个一个的节点,这里可以理解成是链队列,把这个链队列链入到这个队列之中

消息队列的本质是要进行进程间通信,而只要涉及到进程间通信就离不开的话题是让两个不同的进程看到同一份资源,这也是在先前已经建立起来的思想观念,那么基于这个原因,进程a发送的消息队列中的内容必然是需要让进程b见到的,所以就有了接口:

msgget

在这里插入图片描述
它其实和共享内存是很相似的,也是从系统V中获取一个消息队列的标识符,只要调用了这个接口,那么就可以在内核中创建出一个消息队列,这个消息队列就可以使用了

那随之而来的下一个问题是,现在进程a创建出对应的消息队列,也满足了让不同的进程看到同一份资源这样的一个基本的条件,但是现在面临的问题是,如果进程a向消息队列中写信息,进程b也向消息队列中写信息,那么如何去进行区分呢?消息队列中的节点对于不同的进程来说想要看到的信息当然是不一样的,所以必然有对应的标识符,由进程a创建的数据节点中就会带有进程a的标识符,由进程b创建的节点就会有进程b的标识符,这样不同的进程在识别到某个资源中没有自己所对应的标识符就不会识别了,而是只会识别到自己对应的标识符

消息队列由于和共享内存一样,都是隶属于系统V内部的结构,所以它们之间必定会遵循一定的标准,所以从接口或是其他的层面上都几乎相似,因此消息队列的生命周期也是随内核的,而操作系统中各种各样的进程也都会有通信的需求,如果创建出各种各样的消息队列,那么操作系统也必然会为这一个一个的消息队列进行维护,所以从逻辑上讲,消息队列和共享内存基本上是一样的,所以对于消息队列的管理,就转换成了对于描述该消息队列的数据结构对象的增删查改,这样就把消息队列管理起来了

msgsnd

在这里插入图片描述
发送数据块到消息队列

msgrcv

在这里插入图片描述
获取消息队列中的数据块

msgctl

在这里插入图片描述
这个接口也和共享内存基本一致,这里不再过多描述

信号量

信号量本质上就是一种计数器,用来保护共享资源,未来可能会有多个线程看到同一个公共资源,那在执行和访问共享资源的过程中,就可能会产生问题,例如一个进程正在写信息,另外一个进程就已经来读了,那么就会产生数据干扰,这当然是不被操作系统认可的行为,为了避免这样的问题导致内部数据紊乱,所以就引入了信号量的概念,来保护操作系统内部的公共资源,这是对于信号量最初步的理解

semget

在这里插入图片描述
这是信号量的创建接口

semctl

在这里插入图片描述
这是信号量的控制接口,和消息队列以及共享内存不太一样的是,多了一个可变参数,所以对于信号量的控制相比起其他来说要略复杂一些

消息队列和共享内存都有具体管理的数据结构对象,所以对于信号量也不例外,肯定有其对应的管理对象,所以也会有对应的struct ipcperm结构体对象

对于信号量之后的其余内容,放在之后的内容里,这个模块本身主要是要对于共享内存的理解,但由于消息队列和信号量都是ipc资源,所以拿来一谈,之后对于信号量还有更多的内容补充

内核看待ipc资源

下面进行的模块是,内核是如何对待ipc资源的

上述有了三种共享资源,有共享内存,消息队列,信号量,由于这三个模块都是遵循一套标准做出来的,所以也是比较相似,例如接口的设计,数据结构的管理方式,以及返回的id值,那对于操作系统来说,是不是应该把这些也进行统一的管理呢?答案是肯定的

单独设计的模块

第一个想要输出的结论是,这个模块是操作系统内部单独设计出的模块,对于模块的概念,大体上可以细分为进程管理,内存管理,文件管理,驱动管理,这是操作系统的四大管理模块,而对于ipc资源的管理也是一个模块,只不过是下属的细分模块,不属于最大的四个管理模块,有了这个概念之后,那么在操作系统内部是如何进行管理的呢?

ipc资源的维护

ipc资源是如何在内核中进行维护的呢?现在有三种共享资源,这三种共享资源又有它们对应的id,key值,这些分散的数据理应被管理起来,事实上操作系统也确实把他们管理起来了

在这里插入图片描述
那在内核中是如何进行数据维护的呢?在操作系统内部存在这样的结构

struct ipc_id_ary
{int size;struct kern_ipc_perm *p[0];
}

这个结构体中存储的是数组元素个数以及一个柔性数组,在上图中也有对应,这里单独将其拿出分析

那生成这样的一个结构,存放的数据类型是kern_ipc_perm的一个结构体类型的指针,这个指针会指向一个指针数组,这个指针数组中存储的不是其他信息,存储的是具体的ipc资源的结构体的开头的第一个元素,这也就是为什么在内核中不管是消息队列还是共享内存还是信号量,它们的第一个元素都是一个perm类型的字样,就是为了方便于将这个内容统一管理到这样的一个结构体中,这样就能把所有的ipc资源统一用指针数组来管理起来

那这有什么用?用处就是未来可以通过这个内容找到对应的内容,在实际的使用中,可以通过数组中的一个指针,找到它对应的属于哪个共享资源,然后转换成对应的类型,有了起始地址和偏移量,整个数组内的对应元素的各种内容也就都有了,这样就能做到进行数据的访问过程

因此有了这样的结构,之后再管理所有的ipc资源的时候,在设计模式中就将所有内核结构的第一个成员设计成一样的,都是key值,未来在辨别这些ipc资源是否存在的时候,只需要遍历这个数组指针,在这个数组中找到各个内容中的key值,然后判断这个key值是否存在就可以了,如果不存在就进行创建,因此往后就可以统一用数组的方式访问对应的资源,如果想要找到对应资源中的其他信息也可以做出指针对类型做强转来定位到具体的位置

整个流程其实有些类似于C++中的多态,多态的概念已经不是第一次提出了,再对于外设作为文件系统的篇章中,已经讲述了虚拟文件系统就有些类似于多态,而在这里也是第二次提出对于多态的概念,多态就是令子类去继承基类,那么对应到ipc的模式中,每一个具体的ipc资源填充不同的属性,但是开头的元素都一样,再定义一个指针数组,指针数组都会指向一个具体的ipc资源,这就是一个典型的多态的过程

在Linux内核当中,管理System V版本的ipc资源,虽然内部实现的差异比较大,但是利用抽象的方式还是用c语言实现了多态,最终把所有的ipc资源都收拢在了一个数组中,这样对于ipc资源的管理就转换成了对于这个数组的增删查改,这样就做到了管理好共享资源

理解信号量

前面对于信号量的初步认知是,信号量是一个计数器,这里开始要对于信号量有一个更加具体的认知

首先,对于信号量的引入是,要让不同的进程看到同一份资源,这也是进程通信的本质,所以信号量的引入本质上是要让多个执行流看到同一份资源,这部分资源就被叫做公共资源,无论是命名管道还是匿名管道,本质上都是一份公共的缓冲区资源,而这些共享资源都是操作系统提供的,这也是前面已经拥有的概念,如果这个公共资源是由某个特定进程提供的,那么就会违背进程的独立性,所以在这样的情况下,又会诞生的新的问题是,当有很多的进程同时挂接到这块公共资源后,去进行多进程并发访问这块资源的时候,很可能会出现覆盖的情况,导致出数据不一致这样的问题出现,那么基于这个问题的解决方案是,必须要把这部分内容保护起来,把这部分内容保护起来就需要引入的两个概念叫做互斥和同步

互斥和同步是解决数据不一致的两种解决方式,那具体是如何解决的?结论是用户自己来解决,或者是操作系统来帮用户解决,对于匿名管道,命名管道,消息队列这样的通信方式,其实就是操作系统来帮助用户来解决的,因为管道是自带同步机制,消息队列也是由操作系统帮助用户进行维护,唯独是这块共享内存,操作系统从本质上来说没有做任何事,它只是帮助用户从内存中拿到了这块区域,开辟出来了这块区域,但是内核没有对于共享内存做出任何的保护,因为共享内存是通信速度中最快的,所以操作系统没有对于共享内存做出保护,那么对于共享内存来说该如何进行数据的保护工作?那么就因此有了下面的话题

在谈下面的话题前,先对于几个有一个基本的认知:

互斥和同步

互斥:任何一个时刻只允许一个执行流访问公共资源,加锁完成
同步:多个执行流执行的时候,按照一定的顺序执行

临界资源和临界区

对于要被保护起来的公共资源,这部分资源就叫做临界资源,比如对于操作系统来说,它会把管道保护起来,因为管道就是一种临界资源

而访问临界资源的这些代码和操作,就被叫做是临界区,由此就引出了临界资源和临界区的概念,与之对应的还有非临界资源和非临界区两个概念,比如对于访问键盘,显示器,或是定义变量这样的行为,就属于是非临界区的操作,但更重要的概念是,临界资源的访问是要通过代码访问的,也就是说是通过临界区去访问的,所以用户要对临界资源进行保护,其实只需要保护好临界区就可以,换句话说就是把代码保护好,写好代码,这样就保护好了临界资源,于是就有了加锁的概念,对于这个概念不是这里的重点,在之后再进行学习

原子性

原子性的概念虽然是一个新的词语,但是却并不是第一次提及,所谓原子性,就是说操作一件事没有中间状态,要不然把这件事都做完,要不然压根不做,对于这件事只有两个状态,完成或是未完成,这样的性质就叫原子性

信号量

有了上述的概念,就引出了信息量的概念

在操作系统内部有很多的公共资源,这里假设有一个具体的值,假设现在有一份公共资源,其中可以允许有100个进程同时进行访问,那么此时就有一个计数器来了,计数器就负责保护这块公共资源,它只允许最多有100个进程来访问这块资源,每当有一个进程对于这块公共资源进行占用,计数器的值就减去1,当这个进程离开这块公共资源,这个计数器就加上1,表示可以访问的进程又多了一个,所以说,对于这种用来衡量公共资源的数目,最终达到对公共资源分配的这样一个目的,这种计数器就叫做信号量,所以信号量的本质就是一个计数器,这是在最初就引出的概念,信号量的操作规则就是对于某一个执行流,向要访问公共资源中的某一个资源,不是直接去访问,而是要让执行流去申请信号量资源,这样信号量资源的计数器就能做出对应的改变,只要申请成功了就能去访问,这种就叫做预定机制,对于信号量的大体框架就搭建完毕了,这也就是信号量最初始的理解

但是这远远不够,对于信号量还有很多地方没有解释清楚,下面选出一部分来进行解析

  1. 信号量的本质是一种计数器,执行流就是进程,当有进程要访问公共资源中的某一个资源的时候,要先申请信号量,只要把信号量申请成功,就已经完成了对应的预定功能,在合适的时候就可以进入来访问了,在申请不成功的时候怎么办呢?对应的执行流就要进行阻塞,正式因为有了信号量的概念,从此之后这部分公共资源就能被保护起来了,而不是被多个进程随意的读取数据,导致数据残缺的问题出现,换而言之,操作系统中由于有信号量的存在,所以整个公共资源的访问上限被决定好了,也就意味着访问的上限是可控的,信号量的本质是用来描述公共资源中资源的数量,申请信号量的本质是对公共资源的一种预定机制,当进程申请信号量的时候,申请成功就可以访问,申请失败就要被阻塞挂起,直到申请成功后,就能继续访问这块内容
  2. 如果这个计数器的最大值为1呢?也就是说,同一时刻只允许有一个进程来对这块区域进行访问,多余的内容都不允许访问,那么本质上就实现了一个互斥的功能,同一时刻只允许一个执行流访问公共资源,也就是一种加锁,这样就实现了一个互斥锁,这样的内容就被叫做是二元信号量,用信号量的方式实现了加锁和解锁的过程,完成了互斥的效果,所以未来,在操作系统的内部有一份公共的资源,所有的进程都能看到这块资源,但是如果想要使用这块资源,就假设现在这块资源可以被拆成很多小资源,例如现在有一块共享内存有16kb,现在把这块内存拆成16个小块,一个小块是1kb,这样就能做到允许16个进程同时进来,访问的是不同的数据块,就可以做到并发访问了,想要访问这个数据块就必须先经过信号量,申请成功就访问,申请失败就挂起
  3. 有了信号量的存在,最终形成的效果是,在访问公共资源前要先访问信号量,有了信号量的运行才能访问公共资源,所以两个进程之间创建了一个共享内存,并且两个进程都相互挂接到了这个共享内存上,因为信号量的存在,所以在进行内存访问前,要先向信号量申请,再对内存进行访问,在这个过程中,其实也能看出,信号量也是一种公共的资源,这两个进程都能看到这块资源,也算是完成了一种进程间通信,因为让不同的资源都看到了信号量的存在,信号量是由操作系统提供的,操作系统如何让两个进程看到同一个信号量?因此操作系统就把信号量也纳入了进程的ipc体系中,所以对应不同的进程,每申请一份资源,就意味着可以被申请的资源少了一份,其他的进程也都能知道,这就得益于信号量的存在,因此对于数据通信来说,它的目的并不一定都是为了进程数据传输,也可以通过一个计数器来帮助更合理的完成其他的进程间通信,因此才有了创建信号量需要一个key值,只要有同一个key值,才能做到让不同的进程看到同一份资源
  4. 下一个问题是,每一个进程在访问公共资源之前,都要先申请信号量,所以也就意味着信号量本身也是一种公共资源,那假设现在有1000个进程要访问同一块空间,这个空间只允许10个进程进行访问,在一瞬间有1000个进程同时访问信号量要申请空间,信号量只允许10个,所以一瞬间就申请结束了,因此信号量本身作为管理公共资源的资源,它自己也变成了公共资源,那这怎么办呢?结论是,得益于它内部的实现方式遵循原子性,对于一个进程来说,要不然不申请,要申请就必须有一个结果,可以申请或者被挂起

信号量的基本结构

因此对于未来操作系统中的信号量来说,它里面至少要存储的信息有一个计数器count,还需要有一个PCB对应的指针来维护需要被挂起的进程,这个就叫做等待队列,当一个进程要申请信号量,就让count–,如果count变成0了,就把这个进程从运行队列中剥离出来,把状态改成阻塞,再放到等待队列中进行等待,当有一个进程结束了自己的工作,从公共资源中出来了,此时就把等待队列中的进程唤醒,让它从等待队列中出来,再放到运行队列中,这样就实现了操作系统的调度功能

总结

信号量本质上也是需要被多个进程看到的,所以说信号量本身也是一种公共资源,它是一种资源的预定机制,属于进程间通信的一种,信号量由于也是一种公共资源,所以要保证自身的安全性,因此在对于资源申请的pv操作(p操作指的是申请资源,count–,v操作指的是释放资源,count++)必须要有原则,这也就是为什么信号量有pv操作,究其原因是它也要保证自身的安全

相关文章:

Linux:理解信号量以及内核中的三种通信方式

文章目录 共享内存的通信速度消息队列msggetmsgsndmsgrcvmsgctl 信号量semgetsemctl 内核看待ipc资源单独设计的模块ipc资源的维护 理解信号量总结 本篇主要是基于共享内存,延伸出对于消息队列和信号量,再从内核的角度去看这三个模块实现进程间通信 共享…...

【ArcGIS微课1000例】0100:ArcGIS for CAD软件下载与安装(附安装包)

ArcGIS for CAD软件下载与安装(附安装包)。 文章目录 一、ArcGIS for CAD概述1. ArcGIS for CAD介绍2. 主要功能二、ArcGIS for CAD下载三、ArcGIS for CAD安装1. 安装CAD2. 安装ArcGIS for CAD3. 配置一、ArcGIS for CAD概述 1. ArcGIS for CAD介绍 ArcGIS for CAD是Esri提…...

Django模型(一)

一、介绍 模型,就是python中的类对应数据库中的表 1.1、ORM ORM 就是通过实例对象的语法,完成关系型数据库的操作的技术,是"对象-关系映射"(Object/Relational Mapping) 的缩写 ORM 把数据库映射成对象 1.…...

【ChatGPT】文本向量化与余弦相似度:揭开文本处理的神秘面纱(5)

1、引言 在这个数字化的时代,我们每天都会面对大量的文本信息,从社交媒体到新闻报道,文本无处不在。但是,计算机要如何理解和处理这些文字呢?本文将为大家揭开其中的一些奥秘,详细解释文本向量化的概念&am…...

计算机中丢失mfc100u.dll怎么解决,详细解析mfc100u.dll丢失的解决方法

遭遇“无法找到mfc100u.dll”的错误不必过分担忧,这是一个普遍现象。许多用户在启动某些软件或游戏的时候可能会碰到这样的情况。通常,这个错误信息表明你的计算机系统中缺失了一个关键的动态链接库(DLL)文件,它可能会妨碍应用程序的顺利启动…...

代码随想录 Leetcode617.合并二叉树

题目: 代码(首刷看解析 2024年1月31日): class Solution { public:TreeNode* mergeTrees(TreeNode* root1, TreeNode* root2) {if (!root1) return root2;if (!root2) return root1;root1->val root2->val;root1->left mergeTree…...

知识价值1-github站点域名

github如果访问不上,有一个办法是hosts映射: github.com x.x.x.x github.global.ssl.fastly.net y.y.y.y assets-cdn.github.com z.z.z.z1 assets-cdn.github.com z.z.z.z2 assets-cdn.github.com z.z.z.z3 assets-cdn.github.com z.z.z.z3 那这几个域名…...

【PyRestTest】进行Benchmarking测试

PyRestTest支持通过Curl请求本身收集比较差的网络环境下的性能指标。 基准测试:它们扩展了测试中的配置元素,允许你进行相似的REST调用配置。然而,它们不对HTTP响应情况进行验证,它只收集指标数据。 下列选项被指定用于benchmar…...

cocos creator 调用预设体Prefab中的方法(调用另一个节点的方法)

调用预设体中的方法 通过cc.instantiate(this.star)创建这个预设体实例这个star预设体中添加了一个脚本组件star.ts 获取到这个脚本组件star.getComponent(‘star’).test(),并调用其中的test()方法同理可以用该方式像另一个节点中传值 //星星预设体property(cc.Pr…...

Spring 学习2 --基于xml管理Bean

1、xml管理Bean <?xml version"1.0" encoding"UTF-8"?> <beans xmlns"http://www.springframework.org/schema/beans"xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation"http://www.springfr…...

Java数组遍历深度解析

数组是Java编程中一种非常重要的数据结构&#xff0c;它用于存储相同类型的多个元素。在实际应用中&#xff0c;我们经常需要遍历数组中的所有元素&#xff0c;以进行相应的操作。理解数组的遍历方法对于编写高质量的代码至关重要。本文将深入探讨Java中的数组遍历方法。 一、…...

海洋鱼类检测7种YOLOV8NANO

【免费】海洋鱼类检测&#xff0c;7种类型&#xff0c;YOLOV8训练&#xff0c;转换成ONNX&#xff0c;OPENCV调用资源-CSDN文库 采用YOLOV8NANO训练模型&#xff0c;得到PT模型&#xff0c;然后转换成ONNX&#xff0c;供OPENCV的DNN调用&#xff0c;摆脱PYTORCH依赖&#xff0c…...

Vue2组件注册:全局组件和局部组件

在Vue 2 中&#xff0c;你可以使用全局注册和局部注册两种方式注册组件。以下是两种方式的示例&#xff1a; • 全局注册 全局注册的组件可以在整个应用中使用&#xff0c;适用于高频的通用组件。 // 在 main.js 或者入口文件中 import Vue from vue import App from ./App.v…...

AD24-原理图与PCB交互设置及PCB常用快捷键汇总

一、原理图与PCB交互设置 1、在原理图页&#xff0c;工具-交叉选择模式 2、设置完成后。在原理图页选择器件&#xff0c;然后再PCB页也会相应被选中 3、一般将网络与Pin脚的勾去掉 4、整齐排列 5、TC&#xff1a;查找网络、器件、Pin脚 二、PCB常用快捷键汇总...

CTF-WEB进阶与学习

PHP弱类型 在进行比较的时候&#xff0c;会先判断两种字符串的类型是否相等&#xff0c;再比较 在进行比较的时候&#xff0c;会先将字符串类型转化成相同&#xff0c;再比较 如果比较一个数字和字符串或者比较涉及到数字内容的字符串&#xff0c;则字符串会被转换成数值 并且…...

C++初阶 类和对象(补充)

目录 一、友元 1.1什么是友元&#xff1f; 1.2如何使用友元&#xff1f; 1.3使用友元 1.4使用友元注意事项 二、初始化列表 2.1什么是初始化列表? 2.2为什么要有初始化列表&#xff1f; 2.3使用初始化列表 2.4注意事项 一、友元 1.1什么是友元&#xff1f; 友元是一…...

《HTML 简易速速上手小册》第2章:HTML 的标签和元素(2024 最新版)

文章目录 2.1 文本格式化标签&#xff08;&#x1f3a9;✨&#x1f4dc; 网页的“时尚搭配师”&#xff09;2.1.1 基础示例&#xff1a;一篇博客的格式化2.1.2 案例扩展一&#xff1a;产品介绍页面2.1.3 案例扩展二&#xff1a;个人简历 2.2 链接和锚点&#xff08;&#x1f6a…...

2024斋月大促跨境卖家准备指南

市场覆盖西欧、中东、东南亚、北非地区的跨境电商卖家注意了&#xff0c;2024年的斋月即将开启&#xff0c;较往年日期&#xff0c;今年提前了10天左右&#xff0c;斋月的第一天预测在3月11日星期一到来。 根据Google搜索数据可知&#xff0c;目前已经进入高频“斋月”搜索期&…...

【C++干货铺】哈希结构在C++中的应用

目录 unordered系列关联式容器 unordered_map unordered_map的接口说明 1.unordered_map的构造 2. unordered_map的容量 3. unordered_map的迭代器 4. unordered_map的元素访问 5. unordered_map的查询 6. unordered_map的修改操作 7. unordered_map的桶操作 底层结构 …...

蓝桥杯算法赛第4场小白入门赛强者挑战赛

蓝桥杯算法赛第4场小白入门赛&强者挑战赛 小白1小白2小白3强者1小白4强者2小白5强者3小白6强者4强者5强者6 链接&#xff1a; 第 4 场 小白入门赛 第 4 场 强者挑战赛 小白1 直接用C内置函数即可。 #include <bits/stdc.h> using namespace std;#include <bits…...

【每日一题】6.LeetCode——轮转数组

&#x1f4da;博客主页&#xff1a;爱敲代码的小杨. ✨专栏&#xff1a;《Java SE语法》|《数据结构与算法》 ❤️感谢大家点赞&#x1f44d;&#x1f3fb;收藏⭐评论✍&#x1f3fb;&#xff0c;您的三连就是我持续更新的动力❤️ &#x1f64f;小杨水平有限&#xff0c;欢…...

Java编程练习之类的封装2

1.封装一个股票&#xff08;Stock&#xff09;类&#xff0c;大盘名称为上证A股&#xff0c;前一日的收盘点是2844.70点&#xff0c;设置新的当前值如2910.02点&#xff0c;控制台既要显示以上信息&#xff0c;又要显示涨跌幅度以及点数变化的百分比。运行效果如下&#xff1a;…...

Banana Pi BPI-R4开源路由器开发板快速上手用户手册,采用联发科MT7988芯片设计

介绍 Banana Pi BPI-R4 路由器板采用 MediaTek MT7988A (Filogic 880) 四核 ARM Corex-A73 设计&#xff0c;4GB DDR4 RAM&#xff0c;8GB eMMC&#xff0c;板载 128MB SPI-NAND 闪存&#xff0c;还有 2x 10Gbe SFP、4x Gbe 网络端口&#xff0c;带 USB3 .2端口&#xff0c;M.2…...

C#使用OpenCvSharp4库中5个基础函数-灰度化、高斯模糊、Canny边缘检测、膨胀、腐蚀

C#使用OpenCvSharp4库中5个基础函数-灰度化、高斯模糊、Canny边缘检测、膨胀、腐蚀 使用OpenCV可以对彩色原始图像进行基本的处理&#xff0c;涉及到5个常用的处理&#xff1a; 灰度化 模糊处理 Canny边缘检测 膨胀 腐蚀 1、测试图像lena.jpg 本例中我们采用数字图像处…...

蓝桥杯2024/1/31----第十届省赛题笔记

题目要求&#xff1a; 1、 基本要求 1.1 使用大赛组委会提供的国信长天单片机竞赛实训平台&#xff0c;完成本试题的程序设计 与调试。 1.2 选手在程序设计与调试过程中&#xff0c;可参考组委会提供的“资源数据包”。 1.3 请注意&#xff1a; 程序编写、调试完成后选手…...

CANopen转Profinet网关实现原理与CANopen主站配置方法

CANopen转Profinet网关&#xff08;XD-COPNm20&#xff09;具有Profinet从站功能的设备。CANopen是一种通用的工业网络协议&#xff0c;而Profinet是以太网上的一种通信协议&#xff0c;两者在工业自动化领域具有广泛的应用。CANopen转Profinet网关的主要作用是实现CANopen设备…...

Mysql单行函数练习

数据表 链接&#xff1a;https://pan.baidu.com/s/1dPitBSxLznogqsbfwmih2Q 提取码&#xff1a;b0rp --来自百度网盘超级会员V5的分享 单行函数练习 单行函数(一行数据返回一个结果) #1.显示系统时间(注:日期时间) #2.查询员工工号,姓名,工资以及提高百分之20后的结果(new…...

C++ 11新特性之完美转发

概述 在C编程语言的演进过程中&#xff0c;C 11标准引入了一系列重大革新&#xff0c;其中之一便是“完美转发”机制。这一特性使得模板函数能够无损地传递任意类型的实参给其他函数或构造函数&#xff0c;从而极大地增强了C在泛型编程和资源管理方面的灵活性与效率。 完美转发…...

python222网站实战(SpringBoot+SpringSecurity+MybatisPlus+thymeleaf+layui)-友情链接管理实现

锋哥原创的SpringbootLayui python222网站实战&#xff1a; python222网站实战课程视频教程&#xff08;SpringBootPython爬虫实战&#xff09; ( 火爆连载更新中... )_哔哩哔哩_bilibilipython222网站实战课程视频教程&#xff08;SpringBootPython爬虫实战&#xff09; ( 火…...

【百度Apollo】探索自动驾驶:深入解析Apollo开放平台架构的博客指南

&#x1f3ac; 鸽芷咕&#xff1a;个人主页 &#x1f525; 个人专栏: 《linux深造日志》《粉丝福利》 ⛺️生活的理想&#xff0c;就是为了理想的生活! ⛳️ 推荐 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下…...