006——存储设备(基于liteos-a)

news/2024/7/21 11:24:21 标签: harmonyos, 嵌入式, 系统移植

目录

存储设备驱动程序分析

1.1 字符设备和块设备

1.1.1 APP与驱动程序的交互

1. 字符设备驱动程序

2. 块设备驱动程序教

1.1.2 驱动程序结构体

1.1.3 注册函数

1. 字符设备驱动程序注册函数

2. 块设备驱动程序注册函数

1.2 MTD设备

1.3 块设备驱动程序为MTD开了一个后门

怎么用内存模拟Flash

2.1 指定要使用的内存地址、大小

2.2 实现MtdDev结构体

2.3 怎么使用块设备

2.4 移植过程


存储设备驱动程序分析

参考资料:vendor\democom\demochip\driver\mtd\spi_nor\src\common\spinor.c

我们的目录是samsung下的exynos4412不过内容都是这个内容下面需要源码的没给出也是这个

/*
 * Copyright (c) 2013-2019, Huawei Technologies Co., Ltd. All rights reserved.
 * Copyright (c) 2020, Huawei Device Co., Ltd. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this list of
 *    conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice, this list
 *    of conditions and the following disclaimer in the documentation and/or other materials
 *    provided with the distribution.
 *
 * 3. Neither the name of the copyright holder nor the names of its contributors may be used
 *    to endorse or promote products derived from this software without specific prior written
 *    permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "errno.h"
#include "fs/fs.h"
#include "stdio.h"
#include "stdlib.h"
#include "spinor.h"
#include "mtd_common.h"
#include "spinor_common.h"
#include "mtd_dev.h"

struct MtdDev spinor_mtd;
void AddMtdList(char *type, struct MtdDev *mtd);

extern int get_mtd_info(const char *type);

void* GetMtd(const char *type)
{
	(void)type;
	return &spinor_mtd;
}

static int ramnor_erase(struct MtdDev *mtd, UINT64 start, UINT64 len, UINT64 *failAddr)
{
	unsigned char * rambase = (unsigned char *)mtd->priv;

    uint32_t offset = start;
    uint32_t length = len;

    if (offset + length > mtd->size) {
        return -EINVAL;
    }

    if (offset & (mtd->eraseSize - 1)) {
        return -EINVAL;
    }

    if (length & (mtd->eraseSize - 1)) {
        return -EINVAL;
    }

	memset((void *)(rambase+offset), 0xff, length);
    return 0;
}

static int ramnor_write(struct MtdDev *mtd, UINT64 start, UINT64 len, const char *buf)
{
	unsigned char * rambase = (unsigned char *)mtd->priv;
    uint32_t offset = start;
    uint32_t length = len;

    if ((offset + length) > mtd->size) {
        return -EINVAL;
    }

    if (!length) {
        return 0;
    }

    memcpy((void *)(rambase+offset), buf, length);
	return len;
}

static int ramnor_read(struct MtdDev *mtd, UINT64 start, UINT64 len, char *buf)
{
	unsigned char * rambase = (unsigned char *)mtd->priv;
    uint32_t offset = start;
    uint32_t length = len;
	//int i;

    if ((offset + length) > mtd->size) {
		PRINT_RELEASE("%s %s %d, memcpy: 0x%x, 0x%x, 0x%x\n", __FILE__, __FUNCTION__, __LINE__, (unsigned int)buf, (unsigned int)(rambase+start), (unsigned int)len); 	
        return -EINVAL;
    }

    if (!length) {
		PRINT_RELEASE("%s %s %d, memcpy: 0x%x, 0x%x, 0x%x\n", __FILE__, __FUNCTION__, __LINE__, (unsigned int)buf, (unsigned int)(rambase+start), (unsigned int)len); 	
        return 0;
    }

	//PRINT_RELEASE("%s %s %d, memcpy: 0x%x, 0x%x, 0x%x\n", __FILE__, __FUNCTION__, __LINE__, (unsigned int)buf, (unsigned int)(rambase+start), (unsigned int)len);		

    //return spinor->read(spinor, (uint32_t)from, (uint32_t)len, buf);
    memcpy(buf, (void *)(rambase+offset), length);

	return len;
}


void ramnor_register(struct MtdDev *mtd)
{
    //mtd->priv = (void *)DDR_RAMFS_VBASE;

    //mtd->size = DDR_RAMFS_SIZE;
    mtd->eraseSize = 0x10000;

    mtd->type = MTD_NORFLASH;

    mtd->erase = ramnor_erase;
    mtd->read = ramnor_read;
    mtd->write = ramnor_write;

}

/*---------------------------------------------------------------------------*/
/* spinor_node_register- spinor node register */
/*---------------------------------------------------------------------------*/
int spinor_node_register(struct MtdDev *mtd)
{
    int ret = 0;
    ret = register_blockdriver("/dev/spinor", &g_dev_spinor_ops, 0755, mtd);
    if (ret) {
        ERR_MSG("register spinor err %d!\n", ret);
    }

    return ret;
}

int spinor_init(void)
{
/*xin.TODO*/
#if 0
    spinor_mtd.priv = (void *)DDR_RAMFS_VBASE;
	spinor_mtd.size = DDR_RAMFS_SIZE;
#else
    spinor_mtd.priv = (void *)0;
	spinor_mtd.size = 0;
#endif 
    /* ramnor register */
    ramnor_register(&spinor_mtd);
	PRINT_RELEASE("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);		
//    AddMtdList("spinor", &spinor_mtd);
    if (spinor_node_register(&spinor_mtd)) {
        PRINT_RELEASE("spinor node register fail!\n");
        return -1;
    }
    return get_mtd_info("spinor") ;
}

1.1 字符设备和块设备

Linux中设备驱动程序分为3类:字符设备、块设备、网络设备。 所谓字符设备就是LED、按键、LCD、触摸屏这些非存储设备,APP可以直接调用驱动函数去操作它们。 而块设备就是Flash、磁盘这些存储设备,APP读写普通的文件时,最终会由驱动程序访问硬件。 为什么叫块设备?以前的磁盘读写时,是以块为单位的:即使只是读写一个字节,也需要读写一个块。 主要差别在于:

  • 字符设备驱动程序里,可以读写任意长度的数据

  • 块设备驱动程序里,读写数据时以块(扇区)为单位

1.1.1 APP与驱动程序的交互

1. 字符设备驱动程序

        这个是liteos的字符设备驱动模型 

2. 块设备驱动程序教

 这个是liteos的块设备驱动模型 

1.1.2 驱动程序结构体

从上面的图形可以看到,无论是字符设备还是块设备,都要提供open/read/write/ioctl这些函数。 它们的驱动程序核心是类似的:

  • 字符设备驱动程序:file_operations_vfs

  • 块设备驱动程序:block_operations

1.1.3 注册函数

1. 字符设备驱动程序注册函数
int register_driver(FAR const char *path, FAR const struct file_operations_vfs *fops, mode_t mode, FAR void *priv);

示例:

int ret = register_driver("/dev/hello", &g_helloDevOps, 0666, NULL);

2. 块设备驱动程序注册函数
int register_blockdriver(FAR const char *path,
                         FAR const struct block_operations *bops,
                         mode_t mode, FAR void *priv);

示例:

int ret = register_blockdriver("/dev/spinor", &g_dev_spinor_ops, 0755, mtd);

1.2 MTD设备

在各类电子产品中,存储设备类型多种多样,比如Nor Flash、Nand Flash,这些Flash又有不同的接口:比如SPI接口等等。 这些不同Flash的访问方法各有不同,但是肯定有这三种操作:

  • 擦除

那么可以抽象出一个软件层:MTD,含义为Memory Technology Device,它封装了不同Flash的操作。主要是抽象出一个结构体:

struct MtdDev {
    VOID *priv;
    UINT32 type;
​
    UINT64 size;
    UINT32 eraseSize;
​
    int (*erase)(struct MtdDev *mtd, UINT64 start, UINT64 len, UINT64 *failAddr);
    int (*read)(struct MtdDev *mtd, UINT64 start, UINT64 len, const char *buf);
    int (*write)(struct MtdDev *mtd, UINT64 start, UINT64 len, const char *buf);
};

不同的Flash要提供它自己的MtdDev结构体。

1.3 块设备驱动程序为MTD开了一个后门

为什么说开了个后门呢,正常操作驱动设备都是read,write,但是我们跳过去会发现都是空的

(不知道是什么原因source突然卡的不行,我用vs截图吧)

我们使用mtd专门的read,write去操作。

在JFFS2文件系统中,是直接使用MTD的,没有使用block_operations。 比如:third_party\Linux_Kernel\fs\jffs2\read.c

/*
 * JFFS2 -- Journalling Flash File System, Version 2.
 *
 * Copyright © 2001-2007 Red Hat, Inc.
 *
 * Created by David Woodhouse <dwmw2@infradead.org>
 *
 * For licensing information, see the file 'LICENCE' in this directory.
 *
 */

#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/pagemap.h>
#include <linux/compiler.h>
#include <mtd_dev.h>
#include "nodelist.h"
#include "compr.h"
#include "los_crc32.h"
#include "user_copy.h"

int jffs2_read_dnode(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
		     struct jffs2_full_dnode *fd, unsigned char *buf,
		     int ofs, int len)
{
	struct jffs2_raw_inode *ri;
	size_t readlen;
	uint32_t crc;
	unsigned char *decomprbuf = NULL;
	unsigned char *readbuf = NULL;
	int ret = 0;

	ri = jffs2_alloc_raw_inode();
	if (!ri)
		return -ENOMEM;

	ret = jffs2_flash_read(c, ref_offset(fd->raw), sizeof(*ri), &readlen, (char *)ri);
	if (ret) {
		jffs2_free_raw_inode(ri);
		pr_warn("Error reading node from 0x%08x: %d\n",
			ref_offset(fd->raw), ret);
		return ret;
	}
	if (readlen != sizeof(*ri)) {
		jffs2_free_raw_inode(ri);
		pr_warn("Short read from 0x%08x: wanted 0x%zx bytes, got 0x%zx\n",
			ref_offset(fd->raw), sizeof(*ri), readlen);
		return -EIO;
	}
	crc = crc32(0, ri, sizeof(*ri)-8);

	jffs2_dbg(1, "Node read from %08x: node_crc %08x, calculated CRC %08x. dsize %x, csize %x, offset %x, buf %p\n",
		  ref_offset(fd->raw), je32_to_cpu(ri->node_crc),
		  crc, je32_to_cpu(ri->dsize), je32_to_cpu(ri->csize),
		  je32_to_cpu(ri->offset), buf);
	if (crc != je32_to_cpu(ri->node_crc)) {
		pr_warn("Node CRC %08x != calculated CRC %08x for node at %08x\n",
			je32_to_cpu(ri->node_crc), crc, ref_offset(fd->raw));
			jffs2_dbg_dump_node(c, ref_offset(fd->raw));
		ret = -EIO;
		goto out_ri;
	}
	/* There was a bug where we wrote hole nodes out with csize/dsize
	   swapped. Deal with it */
	if (ri->compr == JFFS2_COMPR_ZERO && !je32_to_cpu(ri->dsize) &&
	    je32_to_cpu(ri->csize)) {
		ri->dsize = ri->csize;
		ri->csize = cpu_to_je32(0);
	}

	D1(if(ofs + len > je32_to_cpu(ri->dsize)) {
			pr_warn("jffs2_read_dnode() asked for %d bytes at %d from %d-byte node\n",
				len, ofs, je32_to_cpu(ri->dsize));
		ret = -EINVAL;
		goto out_ri;
	});

	if (ri->compr == JFFS2_COMPR_ZERO) {
		ret = LOS_UserMemClear(buf, len);
		goto out_ri;
	}

	/* Cases:
	   Reading whole node and it's uncompressed - read directly to buffer provided, check CRC.
	   Reading whole node and it's compressed - read into comprbuf, check CRC and decompress to buffer provided
	   Reading partial node and it's uncompressed - read into readbuf, check CRC, and copy
	   Reading partial node and it's compressed - read into readbuf, check checksum, decompress to decomprbuf and copy
	*/
	if (ri->compr == JFFS2_COMPR_NONE && len == je32_to_cpu(ri->dsize)) {
		readbuf = kmalloc(je32_to_cpu(ri->dsize), GFP_KERNEL);
		if (!readbuf) {
			ret = -ENOMEM;
			goto out_ri;
		}
	} else {
		readbuf = kmalloc(je32_to_cpu(ri->csize), GFP_KERNEL);
		if (!readbuf) {
			ret = -ENOMEM;
			goto out_ri;
		}
	}
	if (ri->compr != JFFS2_COMPR_NONE) {
		decomprbuf = kmalloc(je32_to_cpu(ri->dsize), GFP_KERNEL);
		if (!decomprbuf) {
			ret = -ENOMEM;
			goto out_readbuf;
		}
	} else {
		decomprbuf = readbuf;
	}

	jffs2_dbg(2, "Read %d bytes to %p\n", je32_to_cpu(ri->csize),
		  readbuf);
	ret = jffs2_flash_read(c, (ref_offset(fd->raw)) + sizeof(*ri),
			       je32_to_cpu(ri->csize), &readlen, (char *)readbuf);

	if (!ret && readlen != je32_to_cpu(ri->csize))
		ret = -EIO;
	if (ret)
		goto out_decomprbuf;

	crc = crc32(0, readbuf, je32_to_cpu(ri->csize));
	if (crc != je32_to_cpu(ri->data_crc)) {
		pr_warn("Data CRC %08x != calculated CRC %08x for node at %08x\n",
			je32_to_cpu(ri->data_crc), crc, ref_offset(fd->raw));
		jffs2_dbg_dump_node(c, ref_offset(fd->raw));
		ret = -EIO;
		goto out_decomprbuf;
	}
	jffs2_dbg(2, "Data CRC matches calculated CRC %08x\n", crc);
	if (ri->compr != JFFS2_COMPR_NONE) {
		jffs2_dbg(2, "Decompress %d bytes from %p to %d bytes at %p\n",
			  je32_to_cpu(ri->csize), readbuf,
			  je32_to_cpu(ri->dsize), decomprbuf);
		ret = jffs2_decompress(c, f, ri->compr | (ri->usercompr << 8), readbuf, decomprbuf, je32_to_cpu(ri->csize), je32_to_cpu(ri->dsize));
		if (ret) {
			pr_warn("Error: jffs2_decompress returned %d\n", ret);
			goto out_decomprbuf;
		}
	}

	if (LOS_CopyFromKernel(buf, len, decomprbuf + ofs, len) != 0) {
		ret = -EFAULT;
	}
 out_decomprbuf:
	if(decomprbuf != buf && decomprbuf != readbuf)
		kfree(decomprbuf);
 out_readbuf:
	if(readbuf != buf)
		kfree(readbuf);
 out_ri:
	jffs2_free_raw_inode(ri);

	return ret;
}

int jffs2_read_inode_range(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
			   unsigned char *buf, uint32_t offset, uint32_t len)
{
	uint32_t end = offset + len;
	struct jffs2_node_frag *frag;
	int ret;

	jffs2_dbg(1, "%s(): ino #%u, range 0x%08x-0x%08x\n",
		  __func__, f->inocache->ino, offset, offset + len);

	frag = jffs2_lookup_node_frag(&f->fragtree, offset);

	/* XXX FIXME: Where a single physical node actually shows up in two
	   frags, we read it twice. Don't do that. */
	/* Now we're pointing at the first frag which overlaps our page
	 * (or perhaps is before it, if we've been asked to read off the
	 * end of the file). */
	while(offset < end) {
		jffs2_dbg(2, "%s(): offset %d, end %d\n",
			  __func__, offset, end);
		if (unlikely(!frag || frag->ofs > offset ||
			     frag->ofs + frag->size <= offset)) {
			uint32_t holesize = end - offset;
			if (frag && frag->ofs > offset) {
				jffs2_dbg(1, "Eep. Hole in ino #%u fraglist. frag->ofs = 0x%08x, offset = 0x%08x\n",
					  f->inocache->ino, frag->ofs, offset);
				holesize = min(holesize, frag->ofs - offset);
			}
			jffs2_dbg(1, "Filling non-frag hole from %d-%d\n",
				  offset, offset + holesize);
			ret = LOS_UserMemClear(buf, holesize);
			if (ret != 0) {
				return ret;
			}
			buf += holesize;
			offset += holesize;
			continue;
		} else if (unlikely(!frag->node)) {
			uint32_t holeend = min(end, frag->ofs + frag->size);
			jffs2_dbg(1, "Filling frag hole from %d-%d (frag 0x%x 0x%x)\n",
				  offset, holeend, frag->ofs,
				  frag->ofs + frag->size);
			ret = LOS_UserMemClear(buf, holeend - offset);
			if (ret != 0) {
				return ret;
			}
			buf += holeend - offset;
			offset = holeend;
			frag = frag_next(frag);
			continue;
		} else {
			uint32_t readlen;
			uint32_t fragofs; /* offset within the frag to start reading */

			fragofs = offset - frag->ofs;
			readlen = min(frag->size - fragofs, end - offset);
			jffs2_dbg(1, "Reading %d-%d from node at 0x%08x (%d)\n",
				  frag->ofs+fragofs,
				  frag->ofs + fragofs+readlen,
				  ref_offset(frag->node->raw),
				  ref_flags(frag->node->raw));
			ret = jffs2_read_dnode(c, f, frag->node, buf, fragofs + frag->ofs - frag->node->ofs, readlen);
			jffs2_dbg(2, "node read done\n");
			if (ret) {
				jffs2_dbg(1, "%s(): error %d\n",
					  __func__, ret);
				(void)LOS_UserMemClear(buf, readlen);
				return ret;
			}
			buf += readlen;
			offset += readlen;
			frag = frag_next(frag);
			jffs2_dbg(2, "node read was OK. Looping\n");
		}
	}
	return 0;
}

int jffs2_flash_direct_read(struct jffs2_sb_info *c, loff_t ofs, size_t len,
			size_t *retlen, const char *buf)
{
	int ret;
	ret = c->mtd->read(c->mtd, ofs, len, (char *)buf);
	if (ret >= 0) {
		*retlen = ret;
		return 0;
	}
	*retlen = 0;
	return ret;
}

怎么用内存模拟Flash

正常是用emmc启动的但是他太难了我们从内存中划出一块模拟flash

2.1 指定要使用的内存地址、大小

        这个函数的来源是韦东山老师在华为的时候根据这个文件金星的修改所以名字还没改但是内容是和spi没啥关系了

 

2.2 实现MtdDev结构体

源码:vendor\democom\demochip\driver\mtd\spi_nor\src\common\spinor.c

2.3 怎么使用块设备

  • 添加分区

    • /dev/spinor表示整个块设备

    • /dev/spinorblk0表示里面的第0个分区

    • 不添加分区也可以,可以直接挂载/dev/spinor

  • mount

2.4 移植过程

这里定义文件系统的内存大小

怎么理解呢

 

定义0分区的大小和虚拟内存大小

这个是路径

之前注释掉的挂载代码也可以放出来了

然后修改相关的函数名

#include "los_base.h"
#include "los_config.h"
#include "los_process_pri.h"
#include "lwip/init.h"
#include "lwip/tcpip.h"
#include "sys/mount.h"
#include "mtd_partition.h"
#include "console.h"

UINT32 OsRandomStackGuard(VOID)
{
    return 0;
}

static void exynos4412_mount_rootfs()
{
#if 1
#if 1	
    dprintf("register parition ...\n");
    if (add_mtd_partition("spinor", 0, 0x4000000, 0))
    {
        PRINT_ERR("add_mtd_partition fail\n");
    }
    
    dprintf("mount /dev/spinorblk0 / ...\n");
    //if (mount("/dev/spinorblk0", "/", "jffs2", MS_RDONLY, NULL))
    if (mount("/dev/spinorblk0", "/", "jffs2", 0, NULL))
    {
        PRINT_ERR("mount failed\n");
    }
#else
    dprintf("mount /dev/ramdisk / ...\n");
    //if (mount("/dev/spinorblk0", "/", "jffs2", MS_RDONLY, NULL))
    if (mount("/dev/ramdisk", "/", "vfat", 0, NULL))
    {
        PRINT_ERR("mount failed\n");
    }
#endif
#endif
}

static void exynos4412_driver_init()
{
#if 0	
	extern int my_ramdisk_init(void);
    if (my_ramdisk_init())
    {
        PRINT_ERR("my_ramdisk_init failed\n");
    }

#else	
    extern int spinor_init(void);
    dprintf("spinor_init init ...\n");
    if (!spinor_init())
    {
        PRINT_ERR("spinor_init failed\n");
    }
#endif

#ifdef LOSCFG_DRIVERS_VIDEO
    dprintf("imx6ull_fb_init init ...\n");
	extern int imx6ull_fb_init(void);
    if (imx6ull_fb_init())
    {
        PRINT_ERR("imx6ull_fb_init failed\n");
    }
#endif	

}


void SystemInit()
{
#ifdef LOSCFG_FS_PROC
    dprintf("proc fs init ...\n");
    extern void ProcFsInit(void);
    ProcFsInit();
#endif

#ifdef LOSCFG_DRIVERS_MEM
    dprintf("mem dev init ...\n");
    extern int mem_dev_register(void);
    mem_dev_register();
#endif
    exynos4412_driver_init();
    exynos4412_mount_rootfs();

#ifdef LOSCFG_DRIVERS_HDF
	    extern int DeviceManagerStart(void);
		PRINT_RELEASE("DeviceManagerStart start ...\n");	
		if (DeviceManagerStart()) {
			PRINT_ERR("No drivers need load by hdf manager!");
		}
		dprintf("DeviceManagerStart end ...\n");
#endif

    extern int uart_dev_init(void);
    uart_dev_init();

    if (virtual_serial_init("/dev/uartdev-0") != 0)
    {
        PRINT_ERR("virtual_serial_init failed");
    }

    if (system_console_init(SERIAL) != 0)
    {
        PRINT_ERR("system_console_init failed\n");
    }

    if (OsUserInitProcess())
    {
        PRINT_ERR("Create user init process faialed!\n");
    }
}

我们之前注释的现在也可以放出来了

这里的大小改成刚刚定义的分区0的宏

报错

 先放出来

发现一个事,有定义加U的

但是为什么没用呢神奇,不然之前也不会报那个错误了

有可能这个值找到不到填上现在就没错了


 


http://www.niftyadmin.cn/n/5448565.html

相关文章

10 开源鸿蒙中芯片与开发板对应的源码(硬件相关的部分)

开源鸿蒙中芯片与开发板对应的源码&#xff08;硬件相关的部分&#xff09; 作者将狼才鲸日期2024-03-20 开源鸿蒙通过芯片仓存放指定芯片和指定开发板的代码&#xff0c;硬件相关的代码和纯逻辑代码是分开存放的 源码模块的组织结构在manifest这个Git仓库&#xff0c;这也是拉…

C++中重载定位new表达式

new表达式 当我们使用一条 new表达式时&#xff1a; string *sp new string("hello, world"); string *arr new string[10];实际执行了三步操作&#xff1a; 第一步&#xff0c;new表达式调用一个名为 operator new&#xff08;或者 operator new[]&#xff09;的…

【Redis】在Java中操作Redis

前言 今天简单学习一下怎样在java springboot中使用redis进行数据的操作&#xff0c;我们将使用SpringDataRedis&#xff0c;它 是Spring Boot 集成的Redis服务。 Spring Data Redis 是 Spring 的一部分&#xff0c;提供了在 Spring 应用中通过简单的配置就可以访问 Redis 服务…

【Web】NKCTF 2024 个人wp(部分)

目录 my first cms 全世界最简单的CTF attack_tacooooo 属实太菜了&#xff0c;3/4 my first cms 一眼搜版本2.2.19 CVE -CVE-2024-27622 GitHub - capture0x/CMSMadeSimple 访问/admin/login.php 爆出弱口令&#xff0c;后台登录 admin Admin123 Extensions > User D…

【前端寻宝之路】学习和总结HTML表格的实现和合并

&#x1f308;个人主页: Aileen_0v0 &#x1f525;热门专栏: 华为鸿蒙系统学习|计算机网络|数据结构与算法|MySQL| ​&#x1f4ab;个人格言:“没有罗马,那就自己创造罗马~” #mermaid-svg-IWDj0gWiFt6IMq3x {font-family:"trebuchet ms",verdana,arial,sans-serif;f…

字符串筛选排序 - 华为OD统一考试(C卷)

OD统一考试(C卷) 分值: 100分 题解: Java / Python / C++ 题目描述 输入一个由n个大小写字母组成的字符串, 按照 ASCII 码值从小到大的排序规则,查找字符串中第 k 个最小ASCII 码值的字母(k>=1) , 输出该字母所在字符串的位置索引(字符串的第一个字符位置索引为0) 。…

TypeScript+Jest测试

1、初始化TypeScript工程 npm i -D typescript生成TypeScript工程配置 tsc --init代码目录 test └── src├── sum.test.ts└── sum.tssum.ts export function add(a: number, b: number): number {return a b; }sum.test.ts import { add } from ./sum;test(add …

【vue核心技术实战精讲】1.1 Vue开篇介绍 + 1.2 Vue的起步 和 插值表达式

文章目录 准备开始适应人群vue 框架学习路线一、vue 基础1、历史介绍2、前端框架与库的区别? 二、vue的起步 和 插值表达式Stage 1&#xff1a;下载包&#xff0c;并放入项目中Stage 2&#xff1a;编码Stage 3&#xff1a;源码 与 效果 准备开始 适应人群 有一定的HTML/CSS/…