树莓派IO口驱动代码的编写、微机总线地址、物理地址、虚拟地址、BCM2835芯片手册

地址总线:

  • 百度百科解释: 地址总线 (Address Bus;又称:位址总线) 属于一种电脑总线 (一部份),是由CPU 或有DMA
    能力的单元,用来沟通这些单元想要存取(读取/写入)电脑内存元件/地方的实体位址。

  • CPU寻找外部的内存单元靠的是地址总线传输的数据。 如果CPU有8根地址总线,每根线上传输0或1,那么传输的数据范围为00000000~ 11111111,每一个数值都对应内存中的一个内存单元,所以可以找到编号为00000000~ 11111111号的内存单元。如果传输的数据为 00110011,那么就会找到00110011号内存单元,如果传输的数据为10110111,那么就会找到10110111号内存单元。编号不在[00000000,11111111]范围内的CPU就寻找不到,例如100000000号内存单元,CPU就寻找不到。寻址能力就是计算CPU能寻找多少个内存单元,00000000~11111111号内存单元,一共有256个,一个内存单元的大小为1byte,这256个内存单元的大小为256byte。

  • CPU是通过地址总线来指定存储单元的。

  • 地址总线决定了cpu所能访问的最大内存空间的大小。eg: 10根地址线能访问的最大的内存为1024(2的10次方)位二进制数据(1B)

  • 地址总线是地址线数量之和。若CPU的地址总线宽度是32位,那么CPU的寻址范围是4G(2的32次方位),所以最多支持4G内存。

  • 比如有一个现象:装了32位的win7系统,明明内存条8G,可系统只是别了3.8G ,装了64位才能识别到8G。装了32位的操作系统CPU的访问范围是2^32 bit,就是4194304kbit,就是4096Mbit,等于4G。树莓派也是32位 ,一个G的内存,但它只能访问949M剩下的挪作他用。

数据总线:

  • CPU通过地址总线寻址,然后通过数据总线与外部设备互换信息。
  • 是CPU与内存或其他器件之间的数据传送的通道。
  • 数据总线的宽度决定了CPU和外界的数据传送速度。
  • 每条传输线一次只能传输1位二进制数据。eg: 8根数据线一次可传送一个8位二进制数据(即一个字节)。
  • 数据总线是数据线数量之和,数据总线的位数决定CPU单次通信能交换的信息数量。

数据总线的宽度对CPU的性能的影响:

  • 首先,总线的速度(即:CPU的主频,CPU的性能指标之一)决定CPU和外设互换信息的速度。
  • 其次,数据总线的宽度也是表示CPU性能的参数之一(通常,我们说“64位的CPU”是指CPU的数据总线的宽度是64位)。 如:64位数据总线的CPU一次就能取出64bit的数据,8位数据总线的CPU一次只能取出8bit的数据,在相同频率的情况下,8位数据总线的CPU就得连续取8次数据,数据量才能和64位数据总线一次取出的数据量相同,单就比较取数据的性能就相差8倍。况且,通常CPU中的寄存器的位数与数据总线的宽度一样,所以在数据处理方面,64位的CPU又比8位的CPU快很多。
  • CPU的地址总线位数和数据总线可以不同(典型代表就是51单片机),但是一般都相同。16位机有16根数据总线,20根地址总线,能访问1M(2的20次方),32位机有32根数据总线,32根地址总线,能访问4G(2的32次方),64位机确实有64根数据总线。

物理地址(PA):

  • 百度百科解释: 网卡物理地址存储器中存储单元对应实际地址称物理地址,与逻辑地址相对应。网卡的物理地址通常是由网卡生产厂家写入网卡的EPROM(一种闪存芯片,通常可以通过程序擦写),它存储的是传输数据时真正赖以标识发出数据的电脑和接收数据的主机的地址。
  • 这里说的物理地址是内存中的内存单元实际地址,不是外部总线连接的其他电子元件的地址!物理地址属于比较好理解的,物理地址就是内存中每个内存单元的编号,这个编号是顺序排好的,物理地址的大小决定了内存中有多少个内存单元,物理地址的大小由地址总线的位宽决定!

虚拟地址(VA):

  • 虚拟地址是Windows程序时运行在386保护模式下,这样程序访问存储器所使用的逻辑地址称为虚拟地址,与实地址模式下的分段地址类似,虚拟地址也可以写为“段:偏移量”的形式,这里的段是指段选择器。而linux没有各种保护模式,本来用的就是虚拟地址。
  • 虚拟地址是CPU保护模式下的一个概念,保护模式是80286系列和之后的x86兼容CPU操作模式,在CPU引导完操作系统内核后,操作系统内核会进入一种CPU保护模式,也叫虚拟内存管理,在这之后的程序在运行时都处于虚拟内存当中,虚拟内存里的所有地址都是不直接的,所以你有时候可以看到一个虚拟地址对应不同的物理地址,比如A进程里的call函数入口虚拟地址是0x001,而B也是,但是它俩对应的物理地址却是不同的,操作系统采用这种内存管理方法。
  • 是防止程序对物理地址写数据造成一些不可必要的问题,比如知道了A进程的物理地址,那么向这个地址写入数据就会造成A进程出现问题,在虚拟内存中运行程序永远不知道自己处于内存中那一段的物理地址上!现在操作系统运行在保护模式下即便知道其他进程的物理地址也不允许向其写入!但是可以通过操作系统留下的后门函数获取该进程上的虚拟地址空间所有控制权限并写入指定数据。
  • 虚拟内存管理采用一种拆东墙补西墙的形式,所以虚拟内存的内存会比物理内存要大许多。在进入虚拟模式之前CPU以及Bootloader(BootLoader是在操作系统内核运行之前运行。可以初始化硬件设备、建立内存空间映射图,从而将系统的软硬件环境带到一个合适状态,以便为最终调用操作系统内核准备好正确的环境),操作系统内核均运行在实模式下,直接对物理地址进行操作!虚拟内存中也有分页管理,这种管理方法是为了确保内存中不会出现内存碎片,当操作系统内核初始化完毕内存中的分页表后CPU的分页标志位会被设置,这个分页标志位是给MMU看的!
  • MMU是Memory Management Unit的缩写,中文名是内存管理单元,它是中央处理器(CPU)中用来管理虚拟存储器、物理存储器的控制线路,同时也负责虚拟地址映射为物理地址,以及提供硬件机制的内存访问授权,多用户多进程操作系统。作用有两点,地址翻译和内存保护。MMU将虚拟地址翻译为物理地址。

分页管理:

  • 内存分页其实就是我们所说的4G空间,内存的所有内存被操作系统内核以4G为每页划分开,当我们程序运行时会被加载到内存中的4G空间里,其实说是有4G其实并没有真正在的4G空间,4G空间中有一小部分被映射到了物理内存中,或者被映射到了硬盘的文件上(fopen),或者没有被映射,还有一部分在内存当中就会被划分栈,堆,其中有大片大片的内存是没有被映射的,同样物理内存也是被分页了用来与虚拟内存产生映射关系。将虚拟地址映射为物理地址有一个算法(页表)决定了将虚拟地址映射到物理地址的哪个位置,页表是通过MMU(分页内存管理单元)来管理的,就是设计完页表后通过MMU来执行将虚拟地址映射为物理地址。
  • 其实真正情况下只有3G用户空间,假如你的内存是4G的那么其中有1G是给操作系统内核使用的,所谓的4G空间只是操作系统基于虚拟内存这种拆东墙补西墙的形式给你一种感觉每个进程都有4G的可用空间一样!这里来说一下拆东墙补西墙,当我们程序被加载进4G空间时其实根本用不了所谓的4G空间,其中有大片内存被闲置,那么这个时候呢,其他程序被加载进来时发现内存不够了,就把其他程序里的4G空间里闲置部分拿出来给这个进程用,换之这个进程内存不够时就会把其他进程里闲置的空间拿过来给该进程使用。银行也是如此!
  • 当我们要对物理地址做操作时比如if语句要根据CPU的状态标志寄存器来做不同的跳转,那么这个时候就要对CPU额状态寄存器做操作了就必须知道它的物理地址,内存中有一个电子元件叫MMU负责从操作系统已经初始化好的内存映射表里查询与虚拟地址对应的物理地址并转换,比如mov
    0x4h8这个是虚拟地址,当我们要对这个虚拟地址里写数据时那么MMU会先判断CPU的分页状态寄存器里的标志状态是否被设定,如果被设定那么MMU就会捕获这个虚拟地址物理并在操作系统内核初始化好的内存映射表里查询与之对应的物理地址,并将其转换成真正的实际物理地址,然后在对这个实际的物理地址给CPU,在由CPU去执行对应的命令,相反CPU往内存里读数据时比如A进程要读取内存中某个虚拟地址的数据,A进程里的指令给的是虚拟地址,MMU首先会检查CPU的分页状态寄存器标志位是否被设置,如果被设置MMU会捕获这个虚拟地址并将其转换成相应的物理地址然后提交给CPU,在由CPU到内存中去取数据!

下面截取树莓派芯片手册的一张图:
在这里插入图片描述
BCM2835是树莓派3B CPU的型号,是ARM-cotexA53架构,cpu Bus是地址总线,00000000~FFFFFFFF是CPU寻址的范围。DMA是高速拷贝单元,CPU可以发动DMA直接让DMA进行数据拷贝,直接内存访问单元。

  • 通过芯片手册了解树莓派的GPIO:有54条通用I/O GPIO行,分为两行,备用功能通常是外围IO并且可以在每个银行中出现一个外围设备,以允许灵活地选择IO电压。
    在这里插入图片描述
  • GPIO有41个寄存器,所有访问都是32位的。Description是寄存器的功能描述。GPFSEL0(寄存器名) GPIO Function Select 0(功能选择:输入或输出);GPSET0 (寄存器名) GPIO Pin Output Set 0(将IO口置0);GPSET1(寄存器名) GPIO Pin Output Set 1(将IO口置1);GPCLR0(寄存器名) GPIO Pin Output Clear 0 (清0)下图的地址是:总线地址(并不是真正的物理地址)
    在这里插入图片描述
  • FSELn表示GPIOn,下图给出第九个引脚的功能选择示例,对寄存器的29-27进行配置,进而设置相应的功能。根据图片下方的register 0表示0~9使用的是register 0这个寄存器。
    在这里插入图片描述
    在这里插入图片描述
  • 输出集寄存器用于设置GPIO管脚。SET{n}字段定义,分别对GPIO引脚进行设置,将“0”写入字段没有作用。如果GPIO管脚为在输入(默认情况下)中使用,那么SET{n}字段中的值将被忽略。然而,如果引脚随后被定义为输出,那么位将被设置根据上次的设置/清除操作。分离集和明确功能取消对读-修改-写操作的需要。GPSETn寄存器为了使IO口设置为1,set4位设置第四个引脚,也就是寄存器的第四位。
    在这里插入图片描述
  • 输出清除寄存器用于清除GPIO管脚。CLR{n}字段定义要清除各自的GPIO引脚,向字段写入“0”没有作用。如果的在输入(默认),然后在CLR{n}字段的值是忽略了。然而,如果引脚随后被定义为输出,那么位将被定义为输出根据上次的设置/清除操作进行设置。分隔集与清函数消除了读-修改-写操作的需要。GPCLRn是清零功能寄存器。
    在这里插入图片描述
  • 配置树莓派的pin4引脚为输出引脚: 只需要将GPFSL0这个寄存器的14~12位设置为001就可以了。只需要将0x6(对应的2进制是110)左移12位·然后取反再与上GPFSL0就可以将13、14这两位配置为0,然后再将0x6(对应2进制110)左移12位,然后或上GPFSL0即可将12位置1。
  • 可使用copy_from_user()这个函数在驱动代码里面读取用户输入的指令,使用copy_to_user()这个函数让引脚反馈现在的状态,也就是让用户读取到。

ioremap、iounmap:

一、 一般我们的外设都是通过读写设备上的寄存器来进行的,通常包括控制寄存器、状态寄存器、数据寄存器三大类。外设的寄存器通常被连续编址,并且根据CPU的体系架构不同CPU对IO端口的编制方式有两种:

  • IO映射方式(IO-mapped):比较典型的有X86处理器为外设专门实现了一个单独的地址空间,称为“IO端口空间”或者“IO地址空间”,此时CPU可以通过专门的指令(比如X86的IN和OUT)来访问这个“IO端口空间”。
  • 内存映射方式(memory-mapped):RISC指令系统的CPU一般只实现一个物理地址空间,外设IO端口成为内存的一部分。此时CPU可以访问外设的IO端口,就像访问自己的内存一样方便,不必再设置专门的指令来访问。在驱动开发过程中一般使用内存映射方式。

二、 在驱动开发过程中,一般来说外设的IO内存资源的物理地址是已知的,由硬件的设计决定。但是CPU不会为这些已知的外设IO内存资源预先指定虚拟地址的值,所以驱动程序不可以直接就通过外设的物理地址访问到IO内存,而必须要将其映射到虚拟地址空间(通过页表),然后才能根据内核映射过后的虚拟地址来通过内存指令访问这些IO内存,并对其进行操作。

三、 在Linux内核的io.h头文件中声明了ioremap()函数,用来将IO内存资源映射到核心虚拟地址空间(3Gb~4GB)中,当然不用了可以将其取消映射iounmap()。这两个函数在mm/ioremap.c文件中:

开始映射:void* ioremap(unsigned long phys_addr , unsigned long size , unsigned long flags)
//用map映射一个设备意味着使用户空间的一段地址关联到设备内存上,这使得只要程序在分配的地址范围内进行读取或写入,实际上就是对设备的访问。
第一个参数是映射的起始地址
第二个参数是映射的长度
第二个参数怎么定啊?
====================
这个由你的硬件特性决定。
比如,你只是映射一个32位寄存器,那么长度为4就足够了。
(这里树莓派IO口功能设置寄存器、IO口设置寄存器都是32位寄存器,所以分配四个字节就够了)比如:GPFSEL0=(volatile unsigned int *)ioremap(0x3f200000,4);GPSET0 =(volatile unsigned int *)ioremap(0x3f20001C,4);GPCLR0 =(volatile unsigned int *)ioremap(0x3f200028,4);
这三行是设置寄存器的地址,volatile的作用是作为指令关键字
确保本条指令不会因编译器的优化而省略,且要求每次直接读值
ioremap函数将物理地址转换为虚拟地址,IO口寄存器映射成普通内存单元进行访问。解除映射:void iounmap(void* addr)//取消ioremap所映射的IO地址
比如:iounmap(GPFSEL0);iounmap(GPSET0);iounmap(GPCLR0); //卸载驱动时释放地址映射

树莓派IO口四的驱动代码:

#include <linux/fs.h>            //file_operations声明
#include <linux/module.h>    //module_init  module_exit声明
#include <linux/init.h>      //__init  __exit 宏定义声明
#include <linux/device.h>        //class  devise声明
#include <linux/uaccess.h>   //copy_from_user 的头文件
#include <linux/types.h>     //设备号  dev_t 类型声明
#include <asm/io.h>          //ioremap iounmap的头文件static struct class *pin4_class;
static struct device *pin4_class_dev;static dev_t devno;                //设备号
static int major =231;                     //主设备号
static int minor =0;                       //次设备号
static char *module_name="pin4";   //模块名volatile unsigned int* GPFSEL0 = NULL;
volatile unsigned int* GPSET0   = NULL;
volatile unsigned int* GPCLR0   = NULL;
//这三行是设置寄存器的地址,volatile的作用是作为指令关键字,确保本条指令不会因编译器的优化而省略,且要求每次直接读值//led_open函数
static int pin4_open(struct inode *inode,struct file *file)
{printk("pin4_open\n");  //内核的打印函数和printf类似*GPFSEL0 &=~(0x6 <<12); // 把13 、14置为0*GPFSEL0 |=~(0x6 <<12); //把12置为1return 0;}
//read函数
static int pin4_read(struct file *file,char __user *buf,size_t count,loff_t *ppos)
{printk("pin4_read\n");  //内核的打印函数和printf类似return 0;
}//led_write函数
static ssize_t pin4_write(struct file *file,const char __user *buf,size_t count, loff_t *ppos)
{int usercmd;printk("pin4_write\n");  //内核的打印函数和printf类似copy_from_user(&usercmd,buf,count); //将应用层用户输入的指令读如usercmd里面if(usercmd == 1){printk("set 1\n");*GPSET0 |= 0x01 << 4;}else if(usercmd == 0){printk("set 0\n");*GPCLR0 |= 0x01 << 4;}else{printk("undo\n");}return 0;
}static struct file_operations pin4_fops = {.owner = THIS_MODULE,.open  = pin4_open,.write = pin4_write,.read  = pin4_read,
};//static限定这个结构体的作用,仅仅只在这个文件。
int __init pin4_drv_init(void)   //真实的驱动入口
{int ret;devno = MKDEV(major,minor);  //创建设备号ret   = register_chrdev(major, module_name,&pin4_fops);  //注册驱动  告诉内核,把这个驱动加入到内核驱动的链表中pin4_class=class_create(THIS_MODULE,"myfirstdemo");//让代码在dev下自动>生成设备pin4_class_dev =device_create(pin4_class,NULL,devno,NULL,module_name);  //创建设备文件GPFSEL0=(volatile unsigned int *)ioremap(0x3f200000,4);GPSET0 =(volatile unsigned int *)ioremap(0x3f20001C,4);GPCLR0 =(volatile unsigned int *)ioremap(0x3f200028,4);printk("insmod driver pin4 success\n");return 0;
}void __exit pin4_drv_exit(void)
{iounmap(GPFSEL0);iounmap(GPSET0);iounmap(GPCLR0); //卸载驱动时释放地址映射device_destroy(pin4_class,devno);class_destroy(pin4_class);unregister_chrdev(major, module_name);  //卸载驱动
}
module_init(pin4_drv_init);  //入口,内核加载驱动的时候,这个宏会被调用,去调用pin4_drv_init这个函数
module_exit(pin4_drv_exit);
MODULE_LICENSE("GPL v2");

上层测试代码:

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
void main()
{int fd,data;fd = open("/dev/pin4",O_RDWR);printf("plese input 1 or 0 //1:high 0:low\n ");scanf("%d",&data);if(fd<0){printf("open fail\n");perror("reson:");}else{printf("open successful\n");}fd=write(fd,&data,1);
}
  • 首先使用指令:ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- KERNEL=kernel7 make modules对驱动模块进行编译生成.ko文件,然后将编译后的驱动发送到树莓派:scp pin4drive.ko pi@192.168.43.136:/home/pi,然后再将上层代码进行编译:arm-linux-gnueabihf-gcc pin4test.c -o pin4test,然后再将测试代码传到树莓派:scp pin4test pi@192.168.43.136:/home/pi/
  • 然后在树莓派上面使用指令:insmod pin4drive.ko进行加载驱动(然后lsmod即可查看到该驱动),然后使用指令:sudo chmod 666 /dev/pin4给予pin4这个设备可访问权限,还可以在虚拟机上面使用mk5sum查看驱动文件的值,并在树莓派上面使用该指令进行查看该驱动文件的值,看是否一致。dmesg查看内核打印的信息,如下图所示:
    在这里插入图片描述
  • 然后运行测试代码,在新建一个窗口,使用指令gpio readall可以看到BCM下面的4号引脚模式是输出模式,电平是低电平或高电平(根据输入的上层代码而定,输入0就是低电平,输入1就是高电平),这里我输入的是0,如下图所示:
    在这里插入图片描述
  • 然后的dmesg查看内核打印的消息,如下图所示:
    在这里插入图片描述

有关驱动代码里面GPIO口地址的问题:
在这里插入图片描述在这里插入图片描述

  • 7Ennnnn意思是7E00000到7EFFFFFF,F2000000是3F000000映射的虚拟地址,然后7E00000和F200000对应,芯片手册里面使用的是和虚拟地址F200000有着对应关系的地址——7E00000,芯片手册上面地址偏移多少物理地址就偏移多少
  • 根据上方图片描述,外设的物理地址范围是m 0x3F000000 to 0x3FFFFFFF,所以你看到的7E200000对应的实际物理地址应该是0x3F000000 + (7E200000-7E000000)
  • GPFSEL0=(volatile unsigned int *)ioremap(0x3f200000,4);
    GPSET0 =(volatile unsigned int *)ioremap(0x3f20001C,4);
    GPCLR0 =(volatile unsigned int *)ioremap(0x3f200028,4);
  • 0x3f200000,0x3f20001C,0x3f200028是物理地址,树莓派的外设空间的起始地址是0x3f000000,根据芯片手册可知对应寄存器的偏移量为,比如GPFSEL0寄存器的实际地址是0x3f200000=0x3F000000 + (7E200000-7E000000)
  • 然后通过数据手册可以看到,树莓派相关寄存器的总线地址(和映射的虚拟地址有某种对应关系的地址)进而可得知偏移量,如下图所示:
    在这里插入图片描述

有关各种地址介绍的博文:

  • 物理地址、虚拟地址、总线地址
  • 物理地址和总线地址区别

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

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

相关文章

夺命雷公狗---DEDECMS----26dedecms面包屑导航的实现

我们在很多项目里面都会用到面包屑导航&#xff0c;而dedecms里面也是给我们封装好面包屑导航的了,如下图所示&#xff1a; 在dede里面实现面包屑导航主要用到{dede:field.position/}标签&#xff0c;我们首先来修改下article_movie.htm内容页的模版文件&#xff1a; 我们修改成…

rust油桶用什么打_草莓用什么膨大素好?草莓膨大剂什么时间打?草莓用什么肥料膨大...

农资365公众号&#xff0c;了解更多生根、根腐、重茬、土传、枯黄萎、根烂病、防治根结线虫、微生物菌肥、膨大坐果、抗病增产的防治方法&#xff01;草莓含有丰富的营养&#xff0c;并且种植效益较高&#xff0c;其种植范围也比较广。草莓种植期间有很多因素影响草莓果实膨大&…

docker安装kafka,超级简单的

简介 kafka是一个分布式消息队列。具有高性能、持久化、多副本备份、横向扩展能力。生产者往队列里写消息&#xff0c;消费者从队列里取消息进行业务逻辑。一般在架构设计中起到解耦、削峰、异步处理的作用。 kafka对外使用topic的概念&#xff0c;生产者往topic里写消息&…

Linux中常见的环境变量笔记

1、变量&#xff1a;BASHBash Shell的全路径比如&#xff1a;echo $BASH2、变量&#xff1a;BASH_VERSIONBash Shell的版本号3、变量&#xff1a;EUID记录当前用户的UID。root用户值为0。4、FUNCNAME在用户函数体内部&#xff0c;记录当前函数体的函数名。5、变量&#xff1a;H…

消防信号二总线有没电压_春晓161#地块人防工程消防电源监控系统的设计与应用...

涂志燕安科瑞电气股份有限公司&#xff0c;上海 嘉定 201801&#xff1b;摘要&#xff1a;本文简述了消防设备电源的组成原理&#xff0c;分析了消防设备电源监控系统在应用中的设计依据和相关规范。通过安科瑞消防设备电源监控系统在春晓161#地块项目的实例介绍&#xff0c;阐…

大学慕课数据结构单元测试——华中科技大学

第一章绪论单元测试 一、单选(2分) 1、​___C__ 是数据的最小单位。 A.信息项 B.数据元素 C.数据项 D.表元素 2、​以下说法不正确的是 ___B___。 A.数据元素是数据的基本单位 B.数据项可由若干个数据元素构成 C.数据可由若干个数据元素构成 D.数据项是不可分割的最小…

RocketMQ同步刷盘和异步刷盘

刷盘机制 同步刷盘和异步刷盘 在broker配置文件里修改参数配置是同步还是异步

vim模式下报错E37: No write since last change No write since last change for buffer “ “

报错如下图所示&#xff1a; 网上的解决方法&#xff1a; 文件为只读文件&#xff0c;无法修改。使用命令:w!强制存盘即可在vim模式下&#xff0c;键入以下命令&#xff1a;:w&#xff01;存盘后在使用vim命令检查是否保存&#xff0c;如未保存&#xff0c;编辑后重复以上操作…

Linux中Shell中取消变量和特殊变量的笔记

1、取消变量取消变量也就是将变量从内存中释放出去&#xff0c;可以使用unset 后面加变量名即可&#xff0c;当然函数的释放同样可以采用该方式处理。比如&#xff1a;name"123"echo ${name}输出&#xff1a;123unset nameecho ${name}输出&#xff1a;#取消函数示例…

光华科技光刻胶_【收藏】6天5板!21只光刻胶概念(名单)“出炉”!

连板数量21家中迪投资5板&#xff0c;宁波联合 神驰机电 神马电力4板&#xff0c;汉缆股份 华盛昌 浙江鼎力3板&#xff0c;海航投资世联行 飞龙股份 安洁科技 京威股份 三丰智能 容大感光 晶瑞股份 奥飞数据 光大嘉宝 电子城博天环境 兆易创新 聚辰股份2板二、科技股&#xff…

20159302 《网络攻击与防范》第四周学习总结

本节学习内容为网络攻击环境的配置。在此过程中&#xff0c;我们至少需要一台靶机&#xff0c;一台攻击机。在此选用windows server 2000为靶机&#xff0c;kali系统为攻击机。 一、系统的安装 根据之前发布的kali系统的安装过程&#xff0c;依据此流程进行windows server的安装…

智能家居项目开发准备工作

智能家居功能细节拆分&#xff1a; 控制端支持语音设备的输入&#xff08;用到之前所学习的LD3320语音识别模块&#xff09;或者是socket客户端&#xff08;这个客户端可以是ftp项目的客户端也可以是Android的app&#xff09;&#xff0c;主控芯片是树莓派&#xff0c;既接收语…

catia曲面扫掠命令详解_Mastercam快捷键命令,附中英文功能讲解!值得收藏!

组合键式快捷键功能快 捷 键功能Alt 0设置Z向控制深度Alt 1设置绘制图形的颜色Alt 2设置当前层Alt 3与Alt 2功能相同Alt 4设置刀具面(Tplane)Alt 5设置绘图面(Cplane)Alt 6设置视图面(Gview)Alt A进入自动存文件对话框快 捷 键功能Alt B工具条的显示/关闭Alt C选择执…

RocketMQ中主从复制

生产者把消息发送到master&#xff0c;不会发送到slave 消费者可以从maste也可以从slave消费消息 如果master挂了&#xff0c;那就从slave消费数据&#xff0c;那么slave怎么拿到master中的数据 这就要用到主从复制

智能家居代码构建编写、简单工厂模式、树莓派摄像头视频监控功能实现

根据上一节内容的代码框架开始编写代码&#xff1a; 首先编写controlDevices.h这个头文件里面的代码&#xff0c;这个是设备工厂每一个结点的结构体类型&#xff0c;而且还要在这个头文件里面进行函数的声明&#xff0c;也就是创建的那些设备.c文件里面的函数&#xff08;为了…

Linux中Shell的命令替换用法笔记

命令替换主要是指将命令的标准输出值赋值给某个变量。命令替换属于Shell编程中非常重要的功能&#xff0c;需要熟悉掌握。 命令替换的方式 1、反引号:命令 2、$()&#xff1a;$(命令) 用法说明&#xff1a;date1date #将date命令值给date1变量 date2$(date) #将date命令值给dat…

采样次数不同平均值不一样_网络推广采取的方式不同,效果也不一样

网络推广是基于互联网而进行&#xff0c;针对目标客户群体&#xff0c;加之以有效的网络链接形式使其形成迅速大的购买力的一种营销模式&#xff0c;是目前很多企业已经认识到的&#xff0c;但是在推广过程之中往往会出现不同的状况&#xff0c;比如说模式是完全不一样的。 …

rocketmq 同步刷盘和异步刷盘以及主从复制之同步复制和异步复制你理解了吗

同步刷盘、异步刷盘 RocketMQ的消息是存储到磁盘上的&#xff0c;这样既能保证断电后恢复&#xff0c;又可以让存储的消息量超出内存的限制。 RocketMQ为了提高性能&#xff0c;会尽可能地保证磁盘的顺序写。消息在通过Producer写入RocketMQ的时候&#xff0c;有两种 写磁盘方…

七层网络模型、TCP/IP四层模型、网络数据包、交换机路由器区别

七层网络模型&#xff08;简称OSI模型&#xff09;&#xff1a; OSI 模型(Open System Interconnection model)是一个由国际标准化组织提出的概念模型,试图供一个使各种不同的计算机和网络在世界范围内实现互联的标准框架。它将计算机网络体系结构划分为七层,每层都可以提供抽…