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操作,究其原因是它也要保证自身的安全

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

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

相关文章

【教学类-44-04】20240130 print dashed(虚线字体)制作的数字描字帖

作品展示:背景需求: 制作绿色数字的数字描字帖 选用字体:print dashed(虚线字体) 【教学类-44-03】20240111阿拉伯数字字帖的字体(三)——德彪钢笔行书(实线字体)和pri…

如何使用Docker部署JSON Crack

文章目录 1. 在Linux上使用Docker安装JSONCrack2. 安装Cpolar内网穿透工具3. 配置JSON Crack界面公网地址4. 远程访问 JSONCrack 界面5. 固定 JSONCrack公网地址 JSON Crack 是一款免费的开源数据可视化应用程序,能够将 JSON、YAML、XML、CSV 等数据格式可视化为交互…

Kafka运维相关知识

目录 一、基本概念 二、技术特性 三、设计思想 四、运维建议 一、基本概念 Apache kafka 是一个分布式的基于push-subscribe的消息系统,它具备快速、可扩展、可持久化的特点。它的最大的特性就是可以实时的处理大量数据以满足各种需求场景:比如基于h…

Mysql基础篇笔记

数据表 链接:https://pan.baidu.com/s/1dPitBSxLznogqsbfwmih2Q 提取码:b0rp --来自百度网盘超级会员V5的分享 sql的执行顺序 根据顺序 也就是说 select后面的字段别名 只能在order by中使用 mysql不支持sql92的外连接 mysql不支持满外连接 可以…

java反射常用方法

反射思维导图 使用案例 package Reflection.Work.WorkTest01;import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Arrays;public class WorkDe…

10-Nacos-灰度发布配置

用于生产上线后,针对指定主机IP做定向测试 1、在配置编辑中,勾选Beta发布,在文本框中勾选需要下发服务的IP地址,多个用英文逗号分隔。 正式版:这个是针对除了Beta版中指定的IP服务生效Beta版:灰度配置&am…

粤Z车牌申请需要什么条件?

深圳湾口岸: 上一年度纳税数额达到15万元以上的,可以申办1个商务车指标 上一年度的纳税数额达到50万元以上的,可以申办第2个商务车指标; 上一年度的纳税数额达到100万元以上的,可以申办第3个商务车指标; 从申办第4个商务车指标起,…

yolov8:pt 转 onnx

yolov8官方教程 1.安装包 我使用的是虚拟环境,yolov8包已经下载到本地了,因此直接在anaconda prompt 命令行继续安装 首先激活自己的虚拟环境,然后执行安装命令 pip install ultralytics yolov8中没有requirement.txt文件,直接…

RTC实时时钟之读取时间

1. RTC 基本介绍 RTC(Real Time Clock) 即实时时钟,它是一个可以为系统提供精确的时间基准的元器件,RTC一般采用精度较高的晶振作为时钟源,有些RTC为了在主电源掉电时还可以工作,需要外加电池供电 2. RTC 控制器 2.1 RTC的特点是:…

知识点积累系列(四)Kubernetes篇【持续更新】

云原生学习路线导航页(持续更新中) 本文是 知识点积累 系列文章的第四篇,记录日常学习中遇到的 Kubernetes 相关的知识点 1.Kubernetes琐碎知识点 1.1.为什么要有annotations annotation中除了能够记录一些额外信息,还可以解决k…

[BUUCTF]-PWN:cmcc_pwnme2解析

保护 ida 完整exp: from pwn import* context(log_leveldebug) #premote(node5.buuoj.cn,26964) pprocess(./pwnme2) addhome0x8048644 addflag0x8048682 getfile0x80485CB main0x80486F8 pop_ebp0x8048680 ret0x80483f2 pop_ebx0x8048409 pop_edi_ebp0x804867f st…

常见的网络安全威胁和防护方法

随着数字化转型和新兴技术在各行业广泛应用,网络安全威胁对现代企业的业务运营和生产活动也产生了日益深远的影响。常见的网络安全威胁通常有以下几种: 1. 钓鱼攻击 攻击者伪装成合法的实体(如银行、电子邮件提供商、社交媒体平台等&#xf…

超越传统—Clean架构打造现代Android架构指南

超越传统—Clean架构打造现代Android架构指南 1. 引言 在过去几年里,Android应用开发经历了巨大的变革和发展。随着移动设备的普及和用户对应用的期望不断提高,开发人员面临着更多的挑战和需求。传统的Android架构在应对这些挑战和需求时显得有些力不从…

笔记---中国剩余定理

全程学自y总 AcWing.204.表达整数的奇怪方式 给定 2 n 2n 2n 个整数 a a a1, a a a2,…, a a an 和 m m m1, m m m2,…, m m mn,求一个最小的非负整数 x x x,满足 ∀ i ∈ [ 1 , n ] , x ≡ m ∀i∈[1,n],x≡m ∀i∈[1,n],x≡mi ( m o d a (mod a (…

深度强化学习(王树森)笔记11

深度强化学习(DRL) 本文是学习笔记,如有侵权,请联系删除。本文在ChatGPT辅助下完成。 参考链接 Deep Reinforcement Learning官方链接:https://github.com/wangshusen/DRL 源代码链接:https://github.c…

HTML5的新特性

目录 一&#xff0c;新增语义化标签 二&#xff0c;新增的多媒体标签 三&#xff0c;新增input表单 四&#xff0c;新增的表单属性 一&#xff0c;新增语义化标签 二&#xff0c;新增的多媒体标签 1&#xff0c;音频&#xff1a;<audio>.。。用MP3 <audio src…

带自执行安装脚本的ROS包的生成

带自执行安装脚本的ROS包的生成 在打包和安装ROS包时, 会有一些固定的配置需要去人为实现, 比如网络配置, 设备树的管理等, 比较麻烦, 不如一次性解决掉, 所以查了相关文档 过程: # 使用bloom-generate rosdebian生成debian文件夹 bloom-generate rosdebian # 进入debian文件…

ICV:signoff_check_drc脚本

更多学习内容请关注「拾陆楼」知识星球 拾陆楼知识星球入口 往期文章链接: ICV:metal fill insertion流程 ICV:metal fill insertion脚本

Windows XP x86 sp3 安装 Python3.4.4

1 下载 Python3.4.4&#xff0c;下载地址&#xff0c;点击红色部分。 Python Release Python 3.4.4 | Python.org 2 一路 Next&#xff0c;将 C:\Python34 和 C:\Python34\Scripts 加入环境变量。 3 python 查看版本&#xff0c;python -m pip list 查看安装的包。 4 其他(打…

【数据结构】链表的一些面试题

简单不先于复杂&#xff0c;而是在复杂之后。 链表面试题 删除链表中等于给定值 val 的所有结点。OJ链接 //1.常规方法struct ListNode* removeElements(struct ListNode* head, int val) {struct ListNode* cur head, *prev NULL;while(cur){if(cur->val val){//1.头删/…