linux-2.6.22.6内核i2c驱动框架源码分析

i2c是常见的通信协议,协议比较简单,只有数据和时钟两条线(SDA和SCL),i2c的通信分为主机和从机,主机一般占主导地位,从机可以有多个。

i2c通信的数据格式为(SDA上的数据):开始的7位里面指定了设备地址(因为有多个从机),第8位是读或写信号,表示此次传输是读还是写,第9位是ack信号,也就是当某个从机收到信号好需要发ack信号(低电平)确认。

接下来就是发数据了,这个过程是由8个数据位和一个ack位组成,如果是读,这8位数据由从机驱动,第9位ack由主机发出(因为此时是主机向从机读数据,主机需要确认是否收到数据),如果是写,则这8位由主机驱动,第9位ack由从机发出,发数据这个过程是可以反复进行的,一旦发送或者读取完成,主机就会发出一个停止信号(SDA由低变高)来结束通信,这所有的过程都是在SDA上进行的。
在这里插入图片描述
i2c通信可以分为读和写,读比写的过程更复杂,要读则先写,读取某设备内某地址的数据过程为:先发送设备地址,然后还要写入设备内要读取的地址,最后再发送设备地址后才可进行数据读取。写入数据则少了再发设备地址这一步,写是直接发送设备地址和设备内要写入的地址后就可以发送要写入的数据了。

写:
在这里插入图片描述
读:
在这里插入图片描述
内核驱动框架分析:

在开发i2c驱动程序时,我们可以自己根据芯片手册进行相关寄存器和时序等设置,这种开发比较原始,完全从底层开始,工作量大并且出错概率大,这种其实就是裸机开发了。如果是在Linux内核上进行开发,则完全可以利用内核提供的i2c框架进行开发。i2c框架主要分为两大部分,一部分是用平台设备总线驱动模型实现的,主要封装了主机(s3c2440芯片)i2c控制器的寄存器和时序设置等操作,另一部分就是设备驱动(相当于从机),主要提供主机部分具体操作所需要的数据信息,如果用总线设备驱动模型来看,这一部分就相当于提供设备信息,所以这两大部分又可以看成总线设备驱动模型。

i2c框架代码在/linux-2.6.22.6/drivers/i2c这个目录下,这里面又有algos、busses、chips三个部分,algos主要是协议算法相关的东西,busses里是主机(s3c2440芯片)i2c控制器的寄存器和时序设置等操作部分,chips里就是具体的设备驱动(相当于从机),框架入口就在busses下i2c-s3c2410.c这个文件里。

先分析i2c-s3c2410.c

static int s3c24xx_i2c_probe(struct platform_device *pdev)
{struct s3c24xx_i2c *i2c = &s3c24xx_i2c;struct resource *res;int ret;/* find the clock and enable it */i2c->dev = &pdev->dev;i2c->clk = clk_get(&pdev->dev, "i2c");if (IS_ERR(i2c->clk)) {dev_err(&pdev->dev, "cannot get clock\n");ret = -ENOENT;goto err_noclk;}dev_dbg(&pdev->dev, "clock source %p\n", i2c->clk);clk_enable(i2c->clk);/* map the registers */res = platform_get_resource(pdev, IORESOURCE_MEM, 0);if (res == NULL) {dev_err(&pdev->dev, "cannot find IO resource\n");ret = -ENOENT;goto err_clk;}i2c->ioarea = request_mem_region(res->start, (res->end-res->start)+1,pdev->name);if (i2c->ioarea == NULL) {dev_err(&pdev->dev, "cannot request IO\n");ret = -ENXIO;goto err_clk;}i2c->regs = ioremap(res->start, (res->end-res->start)+1);if (i2c->regs == NULL) {dev_err(&pdev->dev, "cannot map IO\n");ret = -ENXIO;goto err_ioarea;}dev_dbg(&pdev->dev, "registers %p (%p, %p)\n", i2c->regs, i2c->ioarea, res);/* setup info block for the i2c core */i2c->adap.algo_data = i2c;i2c->adap.dev.parent = &pdev->dev;/* initialise the i2c controller */ret = s3c24xx_i2c_init(i2c);if (ret != 0)goto err_iomap;/* find the IRQ for this unit (note, this relies on the init call to* ensure no current IRQs pending */res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);if (res == NULL) {dev_err(&pdev->dev, "cannot find IRQ\n");ret = -ENOENT;goto err_iomap;}ret = request_irq(res->start, s3c24xx_i2c_irq, IRQF_DISABLED,pdev->name, i2c);if (ret != 0) {dev_err(&pdev->dev, "cannot claim IRQ\n");goto err_iomap;}i2c->irq = res;dev_dbg(&pdev->dev, "irq resource %p (%lu)\n", res,(unsigned long)res->start);ret = i2c_add_adapter(&i2c->adap);if (ret < 0) {dev_err(&pdev->dev, "failed to add bus to i2c core\n");goto err_irq;}platform_set_drvdata(pdev, i2c);dev_info(&pdev->dev, "%s: S3C I2C adapter\n", i2c->adap.dev.bus_id);return 0;err_irq:free_irq(i2c->irq->start, i2c);err_iomap:iounmap(i2c->regs);err_ioarea:release_resource(i2c->ioarea);kfree(i2c->ioarea);err_clk:clk_disable(i2c->clk);clk_put(i2c->clk);err_noclk:return ret;
}static struct platform_driver s3c2440_i2c_driver = {.probe		= s3c24xx_i2c_probe,.remove		= s3c24xx_i2c_remove,.resume		= s3c24xx_i2c_resume,.driver		= {.owner	= THIS_MODULE,.name	= "s3c2440-i2c",},
};static int __init i2c_adap_s3c_init(void)
{int ret;ret = platform_driver_register(&s3c2410_i2c_driver);if (ret == 0) {ret = platform_driver_register(&s3c2440_i2c_driver);if (ret)platform_driver_unregister(&s3c2410_i2c_driver);}return ret;
}static struct resource s3c_i2c_resource[] = {[0] = {.start = S3C24XX_PA_IIC,.end   = S3C24XX_PA_IIC + S3C24XX_SZ_IIC - 1,.flags = IORESOURCE_MEM,},[1] = {.start = IRQ_IIC,.end   = IRQ_IIC,.flags = IORESOURCE_IRQ,}};

devs.c

static struct resource s3c_i2c_resource[] = {[0] = {.start = S3C24XX_PA_IIC,.end   = S3C24XX_PA_IIC + S3C24XX_SZ_IIC - 1,.flags = IORESOURCE_MEM,},[1] = {.start = IRQ_IIC,.end   = IRQ_IIC,.flags = IORESOURCE_IRQ,}};struct platform_device s3c_device_i2c = {.name		  = "s3c2410-i2c",.id		  = -1,.num_resources	  = ARRAY_SIZE(s3c_i2c_resource),.resource	  = s3c_i2c_resource,
};

devs.c和i2c-s3c2410.c是标准的总线设备驱动模型,主要提供主机(s3c2440芯片)i2c控制器的寄存器和时序设置等操作,当设备和驱动匹配后,会调用s3c24xx_i2c_probe,s3c24xx_i2c_probe里又会通过ret = i2c_add_adapter(&i2c->adap);去调剂一个adapter,这个adapter就是给设备驱动(从机部分)开了一个口子,使得这两大部分又构成一个设备驱动模型。

分析i2c_add_adapter(i2c-core.c)

int i2c_add_adapter(struct i2c_adapter *adapter)
{int	id, res = 0;retry:if (idr_pre_get(&i2c_adapter_idr, GFP_KERNEL) == 0)return -ENOMEM;mutex_lock(&core_lists);/* "above" here means "above or equal to", sigh */res = idr_get_new_above(&i2c_adapter_idr, adapter,__i2c_first_dynamic_bus_num, &id);mutex_unlock(&core_lists);if (res < 0) {if (res == -EAGAIN)goto retry;return res;}adapter->nr = id;return i2c_register_adapter(adapter);
}static int i2c_register_adapter(struct i2c_adapter *adap)
{list_add_tail(&adap->list, &adapters);........list_for_each(item,&drivers) {driver = list_entry(item, struct i2c_driver, list);if (driver->attach_adapter)/* We ignore the return code; if it fails, too bad */driver->attach_adapter(adap);}}

i2c_add_adapter里调用了i2c_register_adapter,i2c_register_adapter里先把自己放入adapters数组,然后会循环遍历drivers这个链表,drivers存放的是设备驱动(从机部分),这个操作会调用
drivers里的每个driver的attach_adapter函数,这个函数会检测这个driver对应的设备是否匹配,这个后面会讲到。

接下来看下设备驱动(从机部分)代码,以chips下eeprom.c为例:

static int __init eeprom_init(void)
{return i2c_add_driver(&eeprom_driver);
}
static inline int i2c_add_driver(struct i2c_driver *driver)
{return i2c_register_driver(THIS_MODULE, driver);
}int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
{......list_add_tail(&driver->list,&drivers);list_for_each_entry(adapter, &adapters, list) {driver->attach_adapter(adapter);}...
...
}

eeprom.c里也会去注册一个driver,在i2c_register_driver里先把自己加入到drivers这个链表里,然后遍历adapters,取出每一个adapter然后调用driver的attach_adapter,这个步骤和前面注册adapter时处理一样,下面就看下attach_adapter这个方法。

static struct i2c_driver eeprom_driver = {.driver = {.name	= "eeprom",},.id		= I2C_DRIVERID_EEPROM,.attach_adapter	= eeprom_attach_adapter,.detach_client	= eeprom_detach_client,
};static int eeprom_attach_adapter(struct i2c_adapter *adapter)
{return i2c_probe(adapter, &addr_data, eeprom_detect);
}

attach_adapter就是eeprom_driver 的attach_adapter 方法,这个方法会调用i2c_probe(adapter, &addr_data, eeprom_detect);addr_data是从设备地址,如果检测到从设备匹配成功则会触发eeprom_detect函数的执行。

下面分析i2c_probe:

int i2c_probe(struct i2c_adapter *adapter,struct i2c_client_address_data *address_data,int (*found_proc) (struct i2c_adapter *, int, int))
{...err = i2c_probe_address(adapter, address_data->normal_i2c[i],-1, found_proc);...}static int i2c_probe_address(struct i2c_adapter *adapter, int addr, int kind,int (*found_proc) (struct i2c_adapter *, int, int))
{...if (kind < 0) {if (i2c_smbus_xfer(adapter, addr, 0, 0, 0,I2C_SMBUS_QUICK, NULL) < 0)return 0;/* prevent 24RF08 corruption */if ((addr & ~0x0f) == 0x50)i2c_smbus_xfer(adapter, addr, 0, 0, 0,I2C_SMBUS_QUICK, NULL);}...
}s32 i2c_smbus_xfer(struct i2c_adapter * adapter, u16 addr, unsigned short flags,char read_write, u8 command, int size,union i2c_smbus_data * data)
{...res = i2c_smbus_xfer_emulated(adapter,addr,flags,read_write,command,size,data);...
}static s32 i2c_smbus_xfer_emulated(struct i2c_adapter * adapter, u16 addr,unsigned short flags,char read_write, u8 command, int size,union i2c_smbus_data * data)
{
...if (i2c_transfer(adapter, msg, num) < 0)return -1;...}int i2c_transfer(struct i2c_adapter * adap, struct i2c_msg *msgs, int num)
{
...ret = adap->algo->master_xfer(adap,msgs,num);...
}

i2c_probe里面的层级调用关系如下:
i2c_probe
i2c_probe_address
i2c_smbus_xfer
i2c_smbus_xfer_emulated
i2c_transfer
adap->algo->master_xfer(adap,msgs,num);
可以看到,i2c_probe最终会调用到adapter里面 algo结构体的 master_xfer函数,adapter是在i2c-s3c2410.c 里构造的,也就是在设置是主机(s3c2440芯片)i2c控制器的寄存器和时序等操作时构造的。也就是说,master_xfer是一个真正i2c通信时数据传输函数,最终这个函数里会去发出中断和开始信号,并设置相关寄存器发起数据传输。

static const struct i2c_algorithm s3c24xx_i2c_algorithm = {.master_xfer		= s3c24xx_i2c_xfer,.functionality		= s3c24xx_i2c_func,
};static struct s3c24xx_i2c s3c24xx_i2c = {.lock		= __SPIN_LOCK_UNLOCKED(s3c24xx_i2c.lock),.wait		= __WAIT_QUEUE_HEAD_INITIALIZER(s3c24xx_i2c.wait),.tx_setup	= 50,.adap		= {.name			= "s3c2410-i2c",.owner			= THIS_MODULE,.algo			= &s3c24xx_i2c_algorithm,.retries		= 2,.class			= I2C_CLASS_HWMON,},
};
static int s3c24xx_i2c_xfer(struct i2c_adapter *adap,struct i2c_msg *msgs, int num)
{struct s3c24xx_i2c *i2c = (struct s3c24xx_i2c *)adap->algo_data;int retry;int ret;for (retry = 0; retry < adap->retries; retry++) {ret = s3c24xx_i2c_doxfer(i2c, msgs, num);if (ret != -EAGAIN)return ret;dev_dbg(i2c->dev, "Retrying transmission (%d)\n", retry);udelay(100);}return -EREMOTEIO;
}
static int s3c24xx_i2c_doxfer(struct s3c24xx_i2c *i2c, struct i2c_msg *msgs, int num)
{unsigned long timeout;int ret;ret = s3c24xx_i2c_set_master(i2c);if (ret != 0) {dev_err(i2c->dev, "cannot get bus (error %d)\n", ret);ret = -EAGAIN;goto out;}spin_lock_irq(&i2c->lock);i2c->msg     = msgs;i2c->msg_num = num;i2c->msg_ptr = 0;i2c->msg_idx = 0;i2c->state   = STATE_START;s3c24xx_i2c_enable_irq(i2c);s3c24xx_i2c_message_start(i2c, msgs);spin_unlock_irq(&i2c->lock);timeout = wait_event_timeout(i2c->wait, i2c->msg_num == 0, HZ * 5);ret = i2c->msg_idx;/* having these next two as dev_err() makes life very * noisy when doing an i2cdetect */if (timeout == 0)dev_dbg(i2c->dev, "timeout\n");else if (ret != num)dev_dbg(i2c->dev, "incomplete xfer (%d)\n", ret);/* ensure the stop has been through the bus */msleep(1);out:return ret;
}static void s3c24xx_i2c_message_start(struct s3c24xx_i2c *i2c, struct i2c_msg *msg)
{unsigned int addr = (msg->addr & 0x7f) << 1;unsigned long stat;unsigned long iiccon;stat = 0;stat |=  S3C2410_IICSTAT_TXRXEN;if (msg->flags & I2C_M_RD) {stat |= S3C2410_IICSTAT_MASTER_RX;addr |= 1;} elsestat |= S3C2410_IICSTAT_MASTER_TX;if (msg->flags & I2C_M_REV_DIR_ADDR)addr ^= 1;// todo - check for wether ack wanted or nots3c24xx_i2c_enable_ack(i2c);iiccon = readl(i2c->regs + S3C2410_IICCON);writel(stat, i2c->regs + S3C2410_IICSTAT);dev_dbg(i2c->dev, "START: %08lx to IICSTAT, %02x to DS\n", stat, addr);writeb(addr, i2c->regs + S3C2410_IICDS);/* delay here to ensure the data byte has gotten onto the bus* before the transaction is started */ndelay(i2c->tx_setup);dev_dbg(i2c->dev, "iiccon, %08lx\n", iiccon);writel(iiccon, i2c->regs + S3C2410_IICCON);stat |=  S3C2410_IICSTAT_START;writel(stat, i2c->regs + S3C2410_IICSTAT);
}

总结:i2c驱动框架也是一个总线设备驱动模型,它可以分为两大部分,第一部分就是标准的总线设备驱动模型,这一部分主要封装对主机(s3c2440芯片)i2c控制器的相关操作,这些操作就是对应芯片手册里的相关寄存器设置等操作,并且还会去注册一个adapter链表,这个adapter里封装一些真正数据传输函数,这样的好处时我们在开发一个具体从设备驱动时,不用关系主机控制器设置这些复杂操作了,我们只需要调用它提供的传输函数就可完成数据的收发。

开发一个i2c驱动时,我们只需关心从设备部分,主要提供主机控制器操作所需要的从设备信息,这就是另一部分。在注册adapter和从设备driver时,两变都会把自己添加进自己的链表里,然后遍历对方链表去调用driver的attach_adapter方法进行匹配。从这个层面看,这两大部分又构成了一个总线设备驱动模型,而从设备驱动类型设备,主要提供主机部分所需操作信息。
在这里插入图片描述
i2c驱动实例:

#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/jiffies.h>
#include <linux/i2c.h>
#include <linux/mutex.h>
#include <linux/fs.h>
#include <asm/uaccess.h>static unsigned short ignore[]      = { I2C_CLIENT_END };
static unsigned short normal_addr[] = { 0x50, I2C_CLIENT_END }; /* 地址值是7位 *//* 改为0x60的话, 由于不存在设备地址为0x60的设备, 所以at24cxx_detect不被调用 */static unsigned short force_addr[] = {ANY_I2C_BUS, 0x60, I2C_CLIENT_END};
static unsigned short * forces[] = {force_addr, NULL};static struct i2c_client_address_data addr_data = {.normal_i2c	= normal_addr,  /* 要发出S信号和设备地址并得到ACK信号,才能确定存在这个设备 */.probe		= ignore,.ignore		= ignore,//.forces     = forces, /* 强制认为存在这个设备 */
};static struct i2c_driver at24cxx_driver;static int major;
static struct class *cls;
struct i2c_client *at24cxx_client;static ssize_t at24cxx_read(struct file *file, char __user *buf, size_t size, loff_t * offset)
{unsigned char address;unsigned char data;struct i2c_msg msg[2];int ret;/* address = buf[0] * data    = buf[1]*/if (size != 1)return -EINVAL;copy_from_user(&address, buf, 1);/* 数据传输三要素: 源,目的,长度 *//* 读AT24CXX时,要先把要读的存储空间的地址发给它 */msg[0].addr  = at24cxx_client->addr;  /* 目的 */msg[0].buf   = &address;              /* 源 */msg[0].len   = 1;                     /* 地址=1 byte */msg[0].flags = 0;                     /* 表示写 *//* 然后启动读操作 */msg[1].addr  = at24cxx_client->addr;  /* 源 */msg[1].buf   = &data;                 /* 目的 */msg[1].len   = 1;                     /* 数据=1 byte */msg[1].flags = I2C_M_RD;                     /* 表示读 */ret = i2c_transfer(at24cxx_client->adapter, msg, 2);if (ret == 2){copy_to_user(buf, &data, 1);return 1;}elsereturn -EIO;
}static ssize_t at24cxx_write(struct file *file, const char __user *buf, size_t size, loff_t *offset)
{unsigned char val[2];struct i2c_msg msg[1];int ret;/* address = buf[0] * data    = buf[1]*/if (size != 2)return -EINVAL;copy_from_user(val, buf, 2);/* 数据传输三要素: 源,目的,长度 */msg[0].addr  = at24cxx_client->addr;  /* 目的 */msg[0].buf   = val;                   /* 源 */msg[0].len   = 2;                     /* 地址+数据=2 byte */msg[0].flags = 0;                     /* 表示写 */ret = i2c_transfer(at24cxx_client->adapter, msg, 1);if (ret == 1)return 2;elsereturn -EIO;
}static struct file_operations at24cxx_fops = {.owner = THIS_MODULE,.read  = at24cxx_read,.write = at24cxx_write,
};static int at24cxx_detect(struct i2c_adapter *adapter, int address, int kind)
{	printk("at24cxx_detect\n");/* 构构一个i2c_client结构体: 以后收改数据时会用到它 */at24cxx_client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL);at24cxx_client->addr    = address;at24cxx_client->adapter = adapter;at24cxx_client->driver  = &at24cxx_driver;strcpy(at24cxx_client->name, "at24cxx");i2c_attach_client(at24cxx_client);major = register_chrdev(0, "at24cxx", &at24cxx_fops);cls = class_create(THIS_MODULE, "at24cxx");class_device_create(cls, NULL, MKDEV(major, 0), NULL, "at24cxx"); /* /dev/at24cxx */return 0;
}static int at24cxx_attach(struct i2c_adapter *adapter)
{return i2c_probe(adapter, &addr_data, at24cxx_detect);
}static int at24cxx_detach(struct i2c_client *client)
{printk("at24cxx_detach\n");class_device_destroy(cls, MKDEV(major, 0));class_destroy(cls);unregister_chrdev(major, "at24cxx");i2c_detach_client(client);kfree(i2c_get_clientdata(client));return 0;
}/* 1. 分配一个i2c_driver结构体 */
/* 2. 设置i2c_driver结构体 */
static struct i2c_driver at24cxx_driver = {.driver = {.name	= "at24cxx",},.attach_adapter = at24cxx_attach,.detach_client  = at24cxx_detach,
};static int at24cxx_init(void)
{i2c_add_driver(&at24cxx_driver);return 0;
}static void at24cxx_exit(void)
{i2c_del_driver(&at24cxx_driver);
}module_init(at24cxx_init);
module_exit(at24cxx_exit);MODULE_LICENSE("GPL");

在这个驱动程序里,首先会注册at24cxx_driver,这个注册会触发at24cxx_attach函数的执行,这个函数会通过 i2c_probe(adapter, &addr_data, at24cxx_detect)去触发内核框架函数检测设备地址addr_data是否能匹配成功,如果匹配成功则会调用at24cxx_detect函数。

在at24cxx_detect里,进行了字符设备驱动类和设备的创建,然后就是字符设备驱动那一套东西了,在at24cxx_read里构造好数据调用框架函数i2c_transfer进行数据读取,在at24cxx_write里构造好数据调用框架函数i2c_transfer进行数据写入。

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

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

相关文章

【单片机】STM32单片机的矩阵键盘驱动,标准库,无阻塞方式的矩阵键盘读取

原理图&#xff1a; 从左到右、从上到下&#xff0c;按键是1到16&#xff0c;没有按键返回0&#xff1a; key.c #include "key.h"/* 按键初始化函数 */ void KEY_Init(void) {GPIO_InitTypeDef GPIO_InitStructure;RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, …

Eclipse中的实用工具之JUnit

&#x1f973;&#x1f973;Welcome Huihuis Code World ! !&#x1f973;&#x1f973; 接下来看看由辉辉所写的关于JUnit的相关操作吧 目录 &#x1f973;&#x1f973;Welcome Huihuis Code World ! !&#x1f973;&#x1f973; 是什么 为什么要用 怎么用 是什么 JUnit…

UE4/5动画系列(4.足部ik制作)

目录 前期准备 添加虚拟骨骼 ​编辑 腿部函数&#xff1a; 前肢&#xff1a; ​编辑 盆骨函数&#xff1a; 后肢&#xff1a; 进入动画图表&#xff1a; 前期准备 首先准备一个后期处理动画蓝图 然后【因为笔者之前的大象因为不知明原因崩溃&#xff0c;这里就不展示如何…

PostgreSQL中HOT对cluster的作用

PG中cluster的作用是根据表的索引重新构建一张表&#xff0c;并且表根据该索引进行排序&#xff0c;索引必须提前建好。 注意&#xff1a;cluster操作加ACCESS EXCLUSIVE锁&#xff0c;会阻塞其它任何操作。 我们为什么要运行cluster? PG中的表是堆表&#xff0c;表中行的顺…

青少年机器人技术一级核心知识点:机械结构及模型(四)

随着科技的不断进步&#xff0c;机器人技术已经成为了一个重要的领域。在这个领域中&#xff0c;机械结构是机器人设计中至关重要的一部分&#xff0c;它决定了机器人的形态、运动方式和工作效率。对于青少年机器人爱好者来说&#xff0c;了解机械结构的基础知识&#xff0c;掌…

简述环保用电监管云平台

1、概述 推进打赢蓝天保卫战&#xff0c;打好碧水保卫战&#xff0c;打胜净土保卫战&#xff0c;加快生态环境保护、建设美丽中国&#xff0c;各省市结合物联网和大数据政策&#xff0c;也相继颁布有关污染治理设施用电监管平台等相关政策。针对企业内的环保设施、设备运行状况…

GitHub下载破千万!这份Java大厂面试指南,竟是阿里面试官上传的

前言 本以为在大厂可以逃过35岁的坎儿&#xff0c;结果还没到35就遇上了大裁员。。。被裁的那一个月&#xff0c;我拿着公司给的2N在家躺了大半个月&#xff0c;刚开始是不甘&#xff0c;到后面每个月一万多的房贷催着我不得不重新审视自己&#xff0c;随后踏上了海投之路。 …

【Spring Cloud系列】-Eureka服务端高可用详解

【Spring Cloud系列】-Eureka服务端高可用详解 文章目录 【Spring Cloud系列】-Eureka服务端高可用详解一. 序言二. 什么是高可用性三. 什么是CAP一致性&#xff08;Consistency&#xff09;可用性&#xff08;Availability&#xff09;分区容错&#xff08;Partition-toleranc…

Kubernetes-Ingress、Ingress Controller、Ingress Class

概念 1.Ingress 是对K8S集群中服务的外部访问进行管理的 API 对象。Ingress 公开从集群外部到集群内服务的 HTTP 和 HTTPS 路由。 流量路由由 Ingress 资源上定义的规则控制。 2.Ingress Controller 通常负责通过负载均衡器来实现 Ingress。 3.必须拥有一个 Ingress Controller…

vue(typescript)项目在vs中打开出现的各种问题

目录 vue3 报错解决&#xff1a;找不到模块或其相应的类型声明。&#xff08;Vue 3 can not find module&#xff09; (TS) 未知的编译器选项“allowImportingTsExtensions”。 TS6046 (TS) “--moduleResolution”选项的参数必须为 node, classic, node16, nodenext。…

使用 Sigstore 签名的 Elastic Stack 容器镜像!

作者&#xff1a;Maxime Greau 软件供应链攻击不断增加。 这就是为什么这个主题是安全领导者的首要任务。 在这方面&#xff0c;这篇博文重点介绍了使用 Sigstore 对 Elastic Stack 容器镜像进行签名的新功能&#xff0c;以便&#xff1a; 保护 Elastic 软件供应链工作流程为…

6月份读书学习好文记录

看看CHATGPT在最近几个月的发展趋势 https://blog.csdn.net/csdnnews/article/details/130878125?spm1000.2115.3001.5927 这是属于 AI 开发者的好时代&#xff0c;有什么理由不多去做一些尝试呢。 北大教授陈钟谈 AI 未来&#xff1a;逼近 AGI、融进元宇宙&#xff0c;开源…