测试平台

宿主机平台:Ubuntu 12.04.4 LTS

目标机:Easy-ARM IMX283

目标机内核:Linux 2.6.35.3

LCD驱动分析

LCD屏的驱动总体上分成两块,一块是GUI显示输出驱动;一块是触摸驱动(该部分单独一节另外描述)。

LCD驱动概念

LCD是Liquid Crystal Display的简称,也就是经常所说的液晶显示器。LCD能够支持彩色图像的显示和视频的播放,是一种非常重要的输出设备。如果我们的系统要用GUI(图形界面接口),比如minigui,MicroWindows。这时LCD设备驱动程序就应该编写成frambuffer接口,而不是编写成仅仅操作底层的LCD控制器接口。

framebuffer是Linux系统为显示设备提供的一个接口,它将显示缓冲区抽象,屏蔽图像硬件的底层差异,允许上层应用程序在图形模式下直接对显示缓冲区进行操作。framebuffer又叫帧缓冲,是Linux为操作显示设备提供的一个用户接口。用户应用程序可以通过framebuffer透明地访问不同类型的显示设备。从这个方面来说,framebuffer是硬件设备显示缓冲区的抽象。Linux抽象出framebuffer这个帧缓冲区可以供用户应用程序直接读写,通过更改framebuffer中的内容,就可以立刻显示在LCD显示屏上。

framebuffer是一个标准的字符设备,主设备号是29,次设备号根据缓冲区的数目而定。framebuffer对应/dev/fb%d设备文件。根据显卡的多少,设备文件可能是/dev/fb0、/dev/fb1等。缓冲区设备也是一种普通的内存设备,可以直接对其进行读写。对用户程序而言,它和/dev下面的其他设备没有什么区别,用户可以把frameBuffer看成一块内存,既可以写,又可以读。显示器将根据内存数据显示对应的图像界面。这一切都由LCD控制器和响应的驱动程序来完成。

LCD驱动框架分析

总体上是一个平台设备驱动与字符驱动的组合;

先从LCD的设备 dev/fb* 是怎么实现的来进行追溯;

1.开发板启动,进行设备注册;

在  arch/arm/mach-mx28/device.c  设备注册文件中,注册LCD的fd平台设备 “mxs-fb”

// mxs-fb平台设备资源定义
static struct resource framebuffer_resource[] = {
{
.flags = IORESOURCE_MEM,
.start = LCDIF_PHYS_ADDR,
.end = LCDIF_PHYS_ADDR + 0x2000 - 1,
},
{
.flags = IORESOURCE_IRQ,
.start = IRQ_LCDIF,
.end = IRQ_LCDIF,
},
};

// mxs-fb平台设备私有数据,包含显示屏名称、分辨率、位宽、时钟、面板操作等,在drivers/video/mxs/lcd_43wvf1g.c 中通过subsys_initcall 接口将设备私有数据添加到链表中
static struct mxs_platform_fb_data mxs_framebuffer_pdata = {
.list = LIST_HEAD_INIT(mxs_framebuffer_pdata.list),
};

// lcd设备启动初始化,在 m28evk.c 中 mx28_device_init()会调用
static void __init mx28_init_lcdif(void)
{
struct platform_device *pdev;
pdev = mxs_get_device("mxs-fb", 0);  
//获取匹配的设备结构体,定义在 arch/arm/plat-mxs/device.c 中
if (pdev == NULL || IS_ERR(pdev))
return;
pdev->resource = framebuffer_resource;
pdev->num_resources = ARRAY_SIZE(framebuffer_resource);
pdev->dev.platform_data = &mxs_framebuffer_pdata;
mxs_add_device(pdev, 3);  // 添加到设备注册列表,设备注册在
arch/arm/plat-mxs/device.c 中实现 通过 device_initcall(mxs_device_init);遍历设备列表并进行平台设备注册
}

2.接下来是 platform_driver   mxsfb_driver 的注册,匹配之后触发 mxsfb_probe 函数执行以下操作:

进行相关硬件初始化和 framebuffer 设置;

register_framebuffer()  注册 LCD 屏的 fd 设备;
 
LCD的驱动包含:
drivers/video/mxs/lcd_43wvf1g.c  // LCD设备私有数据,包含名称、分辨率、位宽、时钟、面板操作
drivers/video/mxs/lcdif.c       // lcd的一些接口操作
drivers/video/mxs/mxsfb.c      // 

平台设备驱动 platform_driver 注册  drivers/video/mxs/mxsfb.c 

会编译成 mxsfb.ko

static struct platform_driver mxsfb_driver = {
.probe = mxsfb_probe,
.remove = mxsfb_remove,
.suspend = mxsfb_suspend,
.resume = mxsfb_resume,
.driver = {
.name = "mxs-fb",    // 与启动时的平台设备注册的 platform_device 名称相同
.owner = THIS_MODULE,
},
}; static int __init mxsfb_init(void)
{
return platform_driver_register(&mxsfb_driver);  // 显示屏平台设备驱动注册
}

驱动安装时与平台设备匹配之后触发 mxsfb_probe 函数,这个时核心。

static int __devinit mxsfb_probe(struct platform_device *pdev)
{
int ret = 0;
struct mxs_fb_data *data;
struct resource *res;
struct fb_info *info;
struct mxs_platform_fb_data *pdata = pdev->dev.platform_data;
struct mxs_platform_fb_entry *pentry = NULL; mydbg("\n");
if (pdata == NULL) {
ret = -ENODEV;
goto out;
} if (default_panel_name) {
mydbg("default_panel_name=%s\n",default_panel_name);
     // 通过LCD面板名称匹配获取面板参数及设置句柄(平台设备私有数据传递过来)
pentry = (void *)mxs_lcd_iterate_pdata(pdata,
get_matching_pentry_by_name,
default_panel_name);
if (pentry) {
mxs_lcd_move_pentry_up(pentry, pdata);
pdata->cur = pentry;
}
}
if (!default_panel_name || !pentry) {
mydbg("\n");
pentry = pdata->cur;
}
if (!pentry || !pentry->init_panel || !pentry->run_panel ||
!pentry->release_panel) {
mydbg("\n");
ret = -EINVAL;
goto out;
} data =
(struct mxs_fb_data *)framebuffer_alloc(sizeof(struct mxs_fb_data) +
sizeof(u32) * 256 -
sizeof(struct fb_info),
&pdev->dev);
if (data == NULL) {
ret = -ENOMEM;
goto out;
} cdata = data;
data->dev = &pdev->dev;
data->pdata = pdata;
platform_set_drvdata(pdev, data);
info = &data->info; dev_dbg(&pdev->dev, "resolution %dx%d, bpp %d\n", pentry->x_res,
pentry->y_res, pentry->bpp); mxs_lcd_iterate_pdata(pdata, get_max_memsize, data); data->map_size = PAGE_ALIGN(data->mem_size) * NUM_SCREENS;
dev_dbg(&pdev->dev, "memory to allocate: %d\n", data->map_size); data->virt_start = dma_alloc_writecombine(&pdev->dev,
data->map_size,
&data->phys_start,
GFP_KERNEL); if (data->virt_start == NULL) {
ret = -ENOMEM;
goto out_dma;
}
dev_dbg(&pdev->dev, "allocated at %p:0x%x\n", data->virt_start,
data->phys_start);
mutex_init(&data->blank_mutex);
INIT_WORK(&data->work, mxsfb_task);
data->state = F_ENABLE; mxsfb_default.bits_per_pixel = pentry->bpp;
/* NB: rotated */
mxsfb_default.xres = pentry->y_res;
mxsfb_default.yres = pentry->x_res;
mxsfb_default.xres_virtual = pentry->y_res;
mxsfb_default.yres_virtual = data->map_size /
(pentry->y_res * pentry->bpp / 8);
if (mxsfb_default.yres_virtual >= mxsfb_default.yres * 2)
mxsfb_default.yres_virtual = mxsfb_default.yres * 2;
else
mxsfb_default.yres_virtual = mxsfb_default.yres; mxsfb_fix.smem_start = data->phys_start;
mxsfb_fix.smem_len = pentry->y_res * pentry->x_res * pentry->bpp / 8;
mxsfb_fix.ypanstep = 1; switch (pentry->bpp) {
case 32:
case 24:
mxsfb_default.red.offset = 16;
mxsfb_default.red.length = 8;
mxsfb_default.green.offset = 8;
mxsfb_default.green.length = 8;
mxsfb_default.blue.offset = 0;
mxsfb_default.blue.length = 8;
break; case 16:
#if 0
mxsfb_default.red.offset = 11;
mxsfb_default.red.length = 5;
mxsfb_default.green.offset = 5;
mxsfb_default.green.length = 6;
mxsfb_default.blue.offset = 0;
mxsfb_default.blue.length = 5;
break;
#else
mxsfb_default.red.offset = 0 ;
mxsfb_default.red.length = 5;
mxsfb_default.green.offset = 5;
mxsfb_default.green.length = 6;
mxsfb_default.blue.offset = 11;
mxsfb_default.blue.length = 5;
break;
#endif
default:
dev_err(&pdev->dev, "unsupported bitwidth %d\n", pentry->bpp);
ret = -EINVAL;
goto out_dma;
} info->screen_base = data->virt_start;
info->fbops = &mxsfb_ops;
info->var = mxsfb_default;
info->fix = mxsfb_fix;
info->pseudo_palette = &data->par;
data->par = NULL;
info->flags = FBINFO_FLAG_DEFAULT; init_waitqueue_head(&data->vsync_wait_q);
data->vsync_count = 0; res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (res == NULL) {
dev_err(&pdev->dev, "cannot get IRQ resource\n");
ret = -ENODEV;
goto out_dma;
}
data->regbase = (unsigned long)IO_ADDRESS(res->start); res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (res == NULL) {
dev_err(&pdev->dev, "cannot get IRQ resource\n");
ret = -ENODEV;
goto out_dma;
}
data->irq = res->start; mxsfb_check_var(&info->var, info); ret = fb_alloc_cmap(&info->cmap, 256, 0);
if (ret)
goto out_cmap; mxsfb_set_par(info); mxs_init_lcdif();
ret = pentry->init_panel(data->dev, data->phys_start,
mxsfb_fix.smem_len, pentry);
if (ret) {
dev_err(&pdev->dev, "cannot initialize LCD panel\n");
goto out_panel;
}
dev_dbg(&pdev->dev, "LCD panel initialized\n");
init_timings(data); // not effect dotclk mode ret = request_irq(data->irq, lcd_irq_handler, 0, "fb_irq", data);
if (ret) {
dev_err(&pdev->dev, "request_irq (%d) failed with error %d\n",
data->irq, ret);
goto out_panel;
}
ret = register_framebuffer(info); // 注册fb_info
if (ret)
goto out_irq; pentry->run_panel();
/* REVISIT: temporary workaround for MX23EVK */
mxsfb_disable_controller(data);
mxsfb_enable_controller(data);
data->cur_phys = data->phys_start;
dev_dbg(&pdev->dev, "LCD running now\n"); #ifdef CONFIG_CPU_FREQ
mxsfb_nb.fb_data = data;
cpufreq_register_notifier(&mxsfb_nb.nb, CPUFREQ_TRANSITION_NOTIFIER);
#endif /* CONFIG_CPU_FREQ */ goto out; out_irq:
free_irq(data->irq, data);
out_panel:
fb_dealloc_cmap(&info->cmap);
out_cmap:
dma_free_writecombine(&pdev->dev, data->map_size, data->virt_start,
data->phys_start);
out_dma:
kfree(data);
out:
return ret;
}

待续.....

LCD驱动移植总结

1.LCD引脚配置及初始化

上述原理图包含了数据、时钟、背光控制、触摸、复位等引脚的分配,具体有机会在深入理解LCD硬件驱动原理有在进行说明。

在 arch/arm/mach-mx28/mx28evk_pins.c 的 mx28evk_fixed_pins[ ] 引脚列表中添加 LCD 的驱动引脚,相关引脚转义在  mx28_pins.h 结合 arch/arm/mach/pinctrl.h 实现

#if defined(CONFIG_FB_MXS) || defined(CONFIG_FB_MXS_MODULE)
{
.name = "LCD_D00",
.id = PINID_LCD_D00,
.fun = PIN_FUN1,
.strength = PAD_8MA,
.voltage = PAD_3_3V,
.drive = 1,
},
{
.name = "LCD_D01",
.id = PINID_LCD_D01,
.fun = PIN_FUN1,
.strength = PAD_8MA,
.voltage = PAD_3_3V,
.drive = 1,
},
{
.name = "LCD_D02",
.id = PINID_LCD_D02,
.fun = PIN_FUN1,
.strength = PAD_8MA,
.voltage = PAD_3_3V,
.drive = 1,
},
{
.name = "LCD_D03",
.id = PINID_LCD_D03,
.fun = PIN_FUN1,
.strength = PAD_8MA,
.voltage = PAD_3_3V,
.drive = 1,
},
{
.name = "LCD_D04",
.id = PINID_LCD_D04,
.fun = PIN_FUN1,
.strength = PAD_8MA,
.voltage = PAD_3_3V,
.drive = 1,
},
{
.name = "LCD_D05",
.id = PINID_LCD_D05,
.fun = PIN_FUN1,
.strength = PAD_8MA,
.voltage = PAD_3_3V,
.drive = 1,
},
{
.name = "LCD_D06",
.id = PINID_LCD_D06,
.fun = PIN_FUN1,
.strength = PAD_8MA,
.voltage = PAD_3_3V,
.drive = 1,
},
{
.name = "LCD_D07",
.id = PINID_LCD_D07,
.fun = PIN_FUN1,
.strength = PAD_8MA,
.voltage = PAD_3_3V,
.drive = 1,
},
{
.name = "LCD_D08",
.id = PINID_LCD_D08,
.fun = PIN_FUN1,
.strength = PAD_8MA,
.voltage = PAD_3_3V,
.drive = 1,
},
{
.name = "LCD_D09",
.id = PINID_LCD_D09,
.fun = PIN_FUN1,
.strength = PAD_8MA,
.voltage = PAD_3_3V,
.drive = 1,
},
{
.name = "LCD_D10",
.id = PINID_LCD_D10,
.fun = PIN_FUN1,
.strength = PAD_8MA,
.voltage = PAD_3_3V,
.drive = 1,
},
{
.name = "LCD_D11",
.id = PINID_LCD_D11,
.fun = PIN_FUN1,
.strength = PAD_8MA,
.voltage = PAD_3_3V,
.drive = 1,
},
{
.name = "LCD_D12",
.id = PINID_LCD_D12,
.fun = PIN_FUN1,
.strength = PAD_8MA,
.voltage = PAD_3_3V,
.drive = 1,
},
{
.name = "LCD_D13",
.id = PINID_LCD_D13,
.fun = PIN_FUN1,
.strength = PAD_8MA,
.voltage = PAD_3_3V,
.drive = 1,
},
{
.name = "LCD_D14",
.id = PINID_LCD_D14,
.fun = PIN_FUN1,
.strength = PAD_8MA,
.voltage = PAD_3_3V,
.drive = 1,
},
{
.name = "LCD_D15",
.id = PINID_LCD_D15,
.fun = PIN_FUN1,
.strength = PAD_8MA,
.voltage = PAD_3_3V,
.drive = 1,
},
/*
{
.name = "LCD_D16",
.id = PINID_LCD_D16,
.fun = PIN_FUN1,
.strength = PAD_8MA,
.voltage = PAD_3_3V,
.drive = 1,
},
{
.name = "LCD_D17",
.id = PINID_LCD_D17,
.fun = PIN_FUN1,
.strength = PAD_8MA,
.voltage = PAD_3_3V,
.drive = 1,
},
{
.name = "LCD_D18",
.id = PINID_LCD_D18,
.fun = PIN_FUN1,
.strength = PAD_8MA,
.voltage = PAD_3_3V,
.drive = 1,
},
{
.name = "LCD_D19",
.id = PINID_LCD_D19,
.fun = PIN_FUN1,
.strength = PAD_8MA,
.voltage = PAD_3_3V,
.drive = 1,
},
{
.name = "LCD_D20",
.id = PINID_LCD_D20,
.fun = PIN_FUN1,
.strength = PAD_8MA,
.voltage = PAD_3_3V,
.drive = 1,
},
{
.name = "LCD_D21",
.id = PINID_LCD_D21,
.fun = PIN_FUN1,
.strength = PAD_8MA,
.voltage = PAD_3_3V,
.drive = 1,
},
{
.name = "LCD_D22",
.id = PINID_LCD_D22,
.fun = PIN_FUN1,
.strength = PAD_8MA,
.voltage = PAD_3_3V,
.drive = 1,
},
{
.name = "LCD_D23",
.id = PINID_LCD_D23,
.fun = PIN_FUN1,
.strength = PAD_8MA,
.voltage = PAD_3_3V,
.drive = 1,
},
*/
{
.name = "LCD_RESET",
.id = PINID_LCD_RESET,
.fun = PIN_FUN1,
.strength = PAD_8MA,
.voltage = PAD_3_3V,
.drive = 1,
},
{
.name = "LCD_VSYNC",
.id = PINID_LCD_RD_E,
.fun = PIN_FUN2,
.strength = PAD_8MA,
.voltage = PAD_3_3V,
.drive = 1,
},
{
.name = "LCD_HSYNC",
.id = PINID_LCD_WR_RWN,
.fun = PIN_FUN2,
.strength = PAD_8MA,
.voltage = PAD_3_3V,
.drive = 1,
},
{
.name = "LCD_ENABLE",
.id = PINID_LCD_CS,
.fun = PIN_FUN2,
.strength = PAD_8MA,
.voltage = PAD_3_3V,
.drive = 1,
},
{
.name = "LCD_DOTCLK",
.id = PINID_LCD_RS,
.fun = PIN_FUN2,
.strength = PAD_8MA,
.voltage = PAD_3_3V,
.drive = 1,
},
{
.name = "LCD_BACKLIGHT",
.id = PINID_PWM3,
.fun = PIN_FUN1,
.strength = PAD_8MA,
.voltage = PAD_3_3V,
.drive = 1,
},
#endif

然后通过类似 LCDIF_PHYS_ADDR + BM_LCDIF_CTRL1_RESET 组合就可以操作相关引脚寄存器了

2. 驱动移植到drivers 目录

1).将LCD驱动放到 drivers/video/ 目录下,本例为 mxs

2).修改 drivers/video/Kconfig,添加如下配置,表示会提取 mxs 驱动的 Kconfig 配置

if ARCH_MXS
source "drivers/video/mxs/Kconfig"
endif

3. 修改板级文件

板级文件有两个

mach-mx28   // mx28系列特有的

plat-mxs     // fsl通用共有的功能

1)在设备注册 arch/arm/mach-mx28/device.c 中添加 mxs LCD 平台设备注册

#if defined(CONFIG_FB_MXS) || defined(CONFIG_FB_MXS_MODULE)
// LCD平台设备资源 resource
static struct resource framebuffer_resource[] = {
{
.flags = IORESOURCE_MEM,    //寻址地址空间资源
     .start = LCDIF_PHYS_ADDR,
.end = LCDIF_PHYS_ADDR + 0x2000 - 1,
},
{
.flags = IORESOURCE_IRQ,   //中断资源
.start = IRQ_LCDIF,
.end = IRQ_LCDIF,
},
}; static struct mxs_platform_fb_data mxs_framebuffer_pdata = {
.list = LIST_HEAD_INIT(mxs_framebuffer_pdata.list),
}; static void __init mx28_init_lcdif(void)
{
struct platform_device *pdev;
pdev = mxs_get_device("mxs-fb", 0);  //获取匹配的设备结构体,定义在 arch/arm/plat-mxs/device.c 中
if (pdev == NULL || IS_ERR(pdev))
return;
pdev->resource = framebuffer_resource;
pdev->num_resources = ARRAY_SIZE(framebuffer_resource);
pdev->dev.platform_data = &mxs_framebuffer_pdata; //设备私有数据链表,包含名称、分辨率、位宽、时钟、面板操作,在drivers/video/mxs/lcd_43wvf1g.c 中通过subsys_initcall 接口将设备私有数据添加到链表中
    mxs_add_device(pdev, 3);  //添加到设备注册列表
}
#else
static void __init mx28_init_lcdif(void)
{
;
}
#endif

2)在设备列表注册 arch/arm/plat-mxs/device.c 中添加 mxs LCD 平台设备结构及设备列表匹配信息

#if defined(CONFIG_FB_MXS) || defined(CONFIG_FB_MXS_MODULE)
// LCD面板平台设备结构体
static struct platform_device mxs_fb = {
.name = "mxs-fb",  //平台设备名称,后面的平台设备驱动名称要与这个一致
.id = 0,
.dev = {
.dma_mask = &common_dmamask,
.coherent_dma_mask = DMA_BIT_MASK(32),
.release = mxs_nop_release,
},
};
#endif static struct mxs_dev_lookup dev_lookup[] = {
......

#if defined(CONFIG_FB_MXS) || defined(CONFIG_FB_MXS_MODULE)
{
.name = "mxs-fb",
.size = 1,
.pdev = &mxs_fb,
},
#endif

......
}

Linux的LCD驱动分析及移植的更多相关文章

  1. Linux学习: LCD驱动

    一.LCD驱动框架: 1.分配一个fb_info结构体:s3c_lcd = framebuffer_alloc(0,NULL); 2.设置fb_info(s3c_lcd): ID.固定参数.可变参数. ...

  2. LCD驱动分析【转】

    转自:http://blog.csdn.net/hanmengaidudu/article/details/21559153 1.S3C2440上LCD驱动 (FrameBuffer)实例开发讲解 其 ...

  3. LCD驱动分析(一)字符设备驱动框架分析

    参考:S3C2440 LCD驱动(FrameBuffer)实例开发<一>   S3C2440 LCD驱动(FrameBuffer)实例开发<二> LCD驱动也是字符设备驱动,也 ...

  4. linux的串口驱动分析

    1.串口驱动中的数据结构 • UART驱动程序结构:struct uart_driver  驱动 • UART端口结构: struct uart_port  串口 • UART相关操作函数结构: st ...

  5. 高通 android平台LCD驱动分析

    目前手机芯片厂家提供的源码里包含整个LCD驱动框架,一般厂家会定义一个xxx_fb.c的源文件,注册一个平台设备和平台驱动,在驱动的probe函数中来调用register_framebuffer(), ...

  6. LCD驱动分析(二)帧缓冲设备作为平台设备

    参考:S3C2440 LCD驱动(FrameBuffer)实例开发<一>   S3C2440 LCD驱动(FrameBuffer)实例开发<二> 1.平台设备注册 1.1在li ...

  7. (linux)MMC 卡驱动分析

    最近花时间研究了一下 MMC 卡驱动程序,开始在网上找了很多关于 MMC 卡驱动的分析文章,但大都是在描述各个层,这对于初学者来讲帮助并不大,所以我就打算把自己的理解写下来,希望对大家有用.个人觉得理 ...

  8. S3C6410 LCD驱动分析(转)

    一. 理论分析1. 几个概念:FIMC :    Fully Interactive Mobile Camera (完全交互式移动摄像机)FIMD:     Fully Interactive Mob ...

  9. Hi3559AV100外接UVC/MJPEG相机实时采图设计(一):Linux USB摄像头驱动分析

    下面将给出Hi3559AV100外接UVC/MJPEG相机实时采图设计的整体流程,主要实现是通过V4L2接口将UVC/MJPEG相机采集的数据送入至MPP平台,经过VDEC.VPSS.VO最后通过HD ...

随机推荐

  1. Lateral Movement

    简介 这次去宁夏护网,发现我有的朋友连最基本的横向渗透有些都不晓得,他们问我 我也表达不清楚...,就想着写篇文章总结下吧 (慢慢更..) 可以发我邮箱讨论:muxue@protonmail.com ...

  2. Nacos 权限控制介绍及实战

    方案背景 Nacos自开源依赖,权限控制一直需求比较强烈,这也反应了用户需求将Nacos部署到生产环境的需求.最新发布的Nacos 1.2.0版本已经支持了服务发现和配置管理的权限控制,保障用户安全上 ...

  3. 作为有经验的程序员如果不懂Lambda表达式就说不过去了吧,建议收藏!!!

      最近刚好有空给大家整理下JDK8的特性,这个在实际开发中的作用也是越来越重了,本文重点讲解下Lambda表达式 Lambda表达式   Lambda 表达式,也可称为闭包,它是推动 Java 8 ...

  4. Create Virtual Network with Virtualbox

    Create a virtual machine "ubs1" with ubuntu server 12.04, set its network as Host-only; St ...

  5. MySQL全面瓦解27:主从复制(原理 + 实践)

    概念 主从复制,是指建立一个和主数据库完全一样的数据库环境(称为从数据库),并将主库的操作行为进行复制的过程:将主数据库的DDL和DML的操作日志同步到从数据库上, 然后在从数据库上对这些日志进行重新 ...

  6. MySQL-16-主从复制进阶

    延时从库 介绍 延时从库: 是我们人为配置的一种特殊从库,人为配置从库和主库延时N小时 为什么要有延时从库 数据库故障 物理损坏,普通的主从复制非常擅长解决物理损坏 逻辑损坏,普通主从复制没办法解决逻 ...

  7. mac 软件意外退出

    大概率的原因是软件签名问题. 先安装 xcode xcode-select --install 然后签名 sudo codesign --force --deep --sign - 文件位置(直接将应 ...

  8. sqli-labs 16-20

    less 16: 和less 15基本一致,只是对参数进行了 ") 的包裹,注意闭合语句使用延时注入即可. 下面给一个payload示例: uname=admin")and if( ...

  9. MATLAB—数组运算及数组化编程

    文章目录 前言 一.数组的结构和创建 1.数组及其结构 2.行数组的创建 3.对数组构造的操作 二.数组元素编址及寻访 1.数组元素的编址 2.二维数组元素的寻访 三.数组运算 非数的问题 前言 编程 ...

  10. asp.net core 搭建WebAPI微服务-----cosnul服务

    参考网址:https://blog.csdn.net/weixin_42084199/article/details/108643555 在此之前需要准备的是: vs2019,以往版本不支持dotne ...