背景

在学习SPI框架的时候,看到了有一个rtc驱动用到了regmap,本想通过传统方式访问spi接口的我,突然有点不适应,翻了整个驱动,愣是没有找到读写spi的范式;因此了解了regmap以后,才发现regmap做了这个事情。

介绍

在Linu 3.1开始,Linux引入了regmap来统一管理内核的I2C, SPI等总线,将I2C, SPI驱动做了一次重构,把I/O读写的重复逻辑在regmap中实现。只需初始化时指定总线类型、寄存器位宽等关键参数,即可通过regmap模型接口来操作器件寄存器。

当然,regmap同样适用于操作cpu自身的寄存器。将i2c、spi、mmio、irq都抽象出统一的接口regmap_read、regmap_write、regmap_update_bits等接口 ,从而提高代码的可重用性,并且使得在使用如上内核基础组件时变得更为简单易用。

regmap是在 linux 内核为减少慢速 I/O 驱动上的重复逻辑,提供一种通用的接口来操作底层硬件寄存器的模型框架。

此外,如果在regmap在驱动和硬件寄存器之间使用cache,会减少底层低速 I/O 的操作次数,提高访问效率;但降低了实时性会有所降低。

传统的写法

用一个I2C设备为例,在3.1之前的I2C设备驱动,需要各自调用i2c_transfer来实现读写,比如:

static int raydium_i2c_pda2_write(struct i2c_client *client,
unsigned char addr, unsigned char *w_data, unsigned short length)
{
int retval = -1;
unsigned char retry;
unsigned char buf[MAX_WRITE_PACKET_SIZE + 1];
struct raydium_ts_data *data = i2c_get_clientdata(client); struct i2c_msg msg[] = {
{
.addr = RAYDIUM_I2C_NID,
.flags = RAYDIUM_I2C_WRITE,
.len = length + 1,
.buf = buf,
},
}; if (length > MAX_WRITE_PACKET_SIZE)
{
return -EINVAL;
}
u8_i2c_mode = PDA2_MODE;
buf[0] = addr;
memcpy(&buf[1], w_data, length); for (retry = 0; retry < SYN_I2C_RETRY_TIMES; retry++)
{
if (i2c_transfer(client->adapter, msg, 1) == 1)
{
retval = length;
break;
}
msleep(20);
} if (retry == SYN_I2C_RETRY_TIMES)
{
dev_err(&data->client->dev,"[touch]%s: I2C write over retry limit.addr[0x%x]\n",__func__, addr);
retval = -EIO;
} return retval;
}

需要自行构建i2c_msg,然后使用i2c_transfer传输数据。

使用regmap机制进行读写

但是使用regmap机制,就会变的更为简单。

使用regmap比较简单:

1、使用前,只需根据外设属性配置配置regmap信息(总线类型、寄存器位宽、缓存类型、读写属性等参数);

2、接着注册一个regmap实例;

3、然后调用抽象访问接口访问寄存器;

4、不想使用的时候,释放regmap实例即可。

例如:

// 1.构建regmap_config结构
static const struct regmap_config mlx90632_regmap = {
// 寄存器地址位宽
.reg_bits = 16,
// 寄存器值的位宽,必须填写
.val_bits = 16,
// 可选,判断寄存器是否可写,可读,是否可缓冲等回调
.volatile_table = &mlx90632_volatile_regs_tbl,
.rd_table = &mlx90632_readable_regs_tbl,
.wr_table = &mlx90632_writeable_regs_tbl, .use_single_rw = true,
.reg_format_endian = REGMAP_ENDIAN_BIG,
.val_format_endian = REGMAP_ENDIAN_BIG,
.cache_type = REGCACHE_RBTREE,
}; // 2. 初始化regmap(或者regmap_init_spi)
struct regmap *map= regmap_init_i2c(client, &mlx90632_regmap); //3.读写操作
ret = regmap_read(mlx90632->regmap, MLX90632_EE_VERSION, &read); //4. 释放regmap
regmap_exit(&mlx90632_regmap);

读写特性

regmap的write操作:

  • 先尝试向缓冲cache中write.
  • 如果bypass了,或者volatile,或者cache none情况,调用对应的底层协议进行写操作。

_regmap_read:

_regmap_read

  • 先拿到context,在以后的map->reg_read会用到
  • 如果不是bypass的,调用regcache_read从cache中读
  • 如果不能从cache中read到,调用map->reg_read

regmap_config原型

在regmap_config,定义了寄存器的各种信息,比如寄存器地址长度,寄存器值的长度,读写寄存器的地址范围的信息,寄存器地址和值的大小端以及缓冲方式。

// include/linux/regmap.h
struct regmap_config {
const char *name; // 可选,寄存器名字 int reg_bits; // 寄存器地址位宽,必须填写
int reg_stride; // 寄存器操作宽度,比如为1时,所有寄存器可操作,为2时,只有2^n可操作
int pad_bits;
int val_bits; // 寄存器值的位宽,必须填写
// 可选,判断寄存器是否可写,可读,是否可缓冲等回调
bool (*writeable_reg)(struct device *dev, unsigned int reg);
bool (*readable_reg)(struct device *dev, unsigned int reg);
bool (*volatile_reg)(struct device *dev, unsigned int reg);
bool (*precious_reg)(struct device *dev, unsigned int reg);
regmap_lock lock;
regmap_unlock unlock;
void *lock_arg;
// 寄存器读写方法,可选
int (*reg_read)(void *context, unsigned int reg, unsigned int *val);
int (*reg_write)(void *context, unsigned int reg, unsigned int val); bool fast_io; unsigned int max_register;
const struct regmap_access_table *wr_table; //可选,可写寄存器
const struct regmap_access_table *rd_table;//可选,可读寄存器
const struct regmap_access_table *volatile_table;
const struct regmap_access_table *precious_table;
const struct reg_default *reg_defaults;
unsigned int num_reg_defaults;
enum regcache_type cache_type; // 缓冲方式
const void *reg_defaults_raw;
unsigned int num_reg_defaults_raw; u8 read_flag_mask;
u8 write_flag_mask; bool use_single_rw;
bool can_multi_write; enum regmap_endian reg_format_endian;
enum regmap_endian val_format_endian; const struct regmap_range_cfg *ranges;
unsigned int num_ranges;
};

regmap_init_i2c

当设备驱动配置好config以后,调用对应的regmap_init_xx,例如,这里是regmap_init_i2c

// drivers/base/regmap/regmap-i2c.c

struct regmap *regmap_init_i2c(struct i2c_client *i2c,
const struct regmap_config *config)
{
// regmap_bus 定位了对应总线的读写函数。
const struct regmap_bus *bus = regmap_get_i2c_bus(i2c, config); if (IS_ERR(bus))
return ERR_CAST(bus); return regmap_init(&i2c->dev, bus, &i2c->dev, config);
}

注意到2个新的数据结构:

  • struct regmap_bus *bus
  • struct regmap *regmap_init_i2c(..

regmap_get_i2c_bus

对于普通I2C设备,regmap_bus为:

// drivers/base/regmap/regmap-i2c.c
static const struct regmap_bus *regmap_get_i2c_bus(struct i2c_client *i2c,
const struct regmap_config *config)
{
if (i2c_check_functionality(i2c->adapter, I2C_FUNC_I2C))
return &regmap_i2c;
// ... return ERR_PTR(-ENOTSUPP);
} static struct regmap_bus regmap_i2c = {
.write = regmap_i2c_write,
.gather_write = regmap_i2c_gather_write,
.read = regmap_i2c_read,
.reg_format_endian_default = REGMAP_ENDIAN_BIG,
.val_format_endian_default = REGMAP_ENDIAN_BIG,
};

regmap_bus结构定义了读写函数和默认的寄存器地址和寄存器值的大小端。

regmap_bus原型

/**
* struct regmap_bus - Description of a hardware bus for the register map
* infrastructure.
*
* @fast_io: Register IO is fast. Use a spinlock instead of a mutex
* to perform locking. This field is ignored if custom lock/unlock
* functions are used (see fields lock/unlock of
* struct regmap_config).
* @write: Write operation.
* @gather_write: Write operation with split register/value, return -ENOTSUPP
* if not implemented on a given device.
* @async_write: Write operation which completes asynchronously, optional and
* must serialise with respect to non-async I/O.
* @reg_write: Write a single register value to the given register address. This
* write operation has to complete when returning from the function.
* @reg_update_bits: Update bits operation to be used against volatile
* registers, intended for devices supporting some mechanism
* for setting clearing bits without having to
* read/modify/write.
* @read: Read operation. Data is returned in the buffer used to transmit
* data.
* @reg_read: Read a single register value from a given register address.
* @free_context: Free context.
* @async_alloc: Allocate a regmap_async() structure.
* @read_flag_mask: Mask to be set in the top byte of the register when doing
* a read.
* @reg_format_endian_default: Default endianness for formatted register
* addresses. Used when the regmap_config specifies DEFAULT. If this is
* DEFAULT, BIG is assumed.
* @val_format_endian_default: Default endianness for formatted register
* values. Used when the regmap_config specifies DEFAULT. If this is
* DEFAULT, BIG is assumed.
* @max_raw_read: Max raw read size that can be used on the bus.
* @max_raw_write: Max raw write size that can be used on the bus.
*/
struct regmap_bus {
bool fast_io;
regmap_hw_write write;
regmap_hw_gather_write gather_write;
regmap_hw_async_write async_write;
regmap_hw_reg_write reg_write;
regmap_hw_reg_update_bits reg_update_bits;
regmap_hw_read read;
regmap_hw_reg_read reg_read;
regmap_hw_free_context free_context;
regmap_hw_async_alloc async_alloc;
u8 read_flag_mask;
enum regmap_endian reg_format_endian_default;
enum regmap_endian val_format_endian_default;
size_t max_raw_read;
size_t max_raw_write;
};

regmap_i2c_read

我们看下i2c对应的regmap_bus.read实现:

@/kernel/driver/base/rgmap/regmap-i2c.c
static int regmap_i2c_read(void *context,
const void *reg, size_t reg_size,
void *val, size_t val_size)
{
struct device *dev = context;
struct i2c_client *i2c = to_i2c_client(dev);
struct i2c_msg xfer[2];
int ret; xfer[0].addr = i2c->addr;
xfer[0].flags = 0;
xfer[0].len = reg_size;
xfer[0].buf = (void *)reg; xfer[1].addr = i2c->addr;
xfer[1].flags = I2C_M_RD;
xfer[1].len = val_size;
xfer[1].buf = val; ret = i2c_transfer(i2c->adapter, xfer, 2);
if (ret == 2)
return 0;
else if (ret < 0)
return ret;
else
return -EIO;
}

可以看到其实就是构造i2c_msg,选择操作寄存器地址然后读寄存器值。这正是regmap要做的事情,抽离出总线读写操作到regmap中,避免驱动去重复实现。

等到设备驱动调用regmap_read以后,最终会调用到regmap_i2c_read,这样子就会执行i2c驱动典型的驱动流程。

regmap_init

当在regmap_get_i2c_bus获取到对应的bus以后,就可以进行regmap的初始化了。

regmap_init主要是把config和描述i2c设备的bus设置到regmap中

struct regmap *regmap_init(struct device *dev,
const struct regmap_bus *bus,
void *bus_context,
const struct regmap_config *config)
{
struct regmap *map;
int ret = -EINVAL;
enum regmap_endian reg_endian, val_endian; map = kzalloc(sizeof(*map), GFP_KERNEL);
// 将regmap_config定义的参数赋值到regmap中
map->format.reg_bytes = DIV_ROUND_UP(config->reg_bits, 8);
map->format.pad_bytes = config->pad_bits / 8;
map->format.val_bytes = DIV_ROUND_UP(config->val_bits, 8);
map->format.buf_size = DIV_ROUND_UP(config->reg_bits +
config->val_bits + config->pad_bits, 8);
map->reg_shift = config->pad_bits % 8;
if (config->reg_stride)
map->reg_stride = config->reg_stride;
else
map->reg_stride = 1;
map->use_single_rw = config->use_single_rw;
map->can_multi_write = config->can_multi_write;
map->dev = dev;
map->bus = bus;
map->bus_context = bus_context;
map->max_register = config->max_register;
map->wr_table = config->wr_table;
map->rd_table = config->rd_table;
map->volatile_table = config->volatile_table;
map->precious_table = config->precious_table;
map->writeable_reg = config->writeable_reg;
map->readable_reg = config->readable_reg;
map->volatile_reg = config->volatile_reg;
map->precious_reg = config->precious_reg;
map->cache_type = config->cache_type;
map->name = config->name; /* regmap中有reg_read操作方法,通过bus是否为空赋值 */
if (!bus) {
// 对于该驱动,此处只会走这里
map->reg_read = config->reg_read;
map->reg_write = config->reg_write; map->defer_caching = false;
goto skip_format_initialization;
} else if (!bus->read || !bus->write) {
map->reg_read = _regmap_bus_reg_read;
map->reg_write = _regmap_bus_reg_write; map->defer_caching = false;
goto skip_format_initialization;
} else {
map->reg_read = _regmap_bus_read;
}
// 设置地址和寄存器值的大小端
reg_endian = regmap_get_reg_endian(bus, config);
val_endian = regmap_get_val_endian(dev, bus, config);
// 根据寄存器地址位宽和大小端解析寄存器地址
switch (config->reg_bits + map->reg_shift) {
case 32:
switch (reg_endian) {
case REGMAP_ENDIAN_BIG:
map->format.format_reg = regmap_format_32_be;
break;
case REGMAP_ENDIAN_NATIVE:
map->format.format_reg = regmap_format_32_native;
break;
default:
goto err_map;
}
break;
}
// 根据寄存器值位宽和大小端解析寄存器值
switch (config->val_bits) {
case 16:
switch (val_endian) {
case REGMAP_ENDIAN_BIG:
map->format.format_val = regmap_format_16_be;
map->format.parse_val = regmap_parse_16_be;
map->format.parse_inplace = regmap_parse_16_be_inplace;
break;
case REGMAP_ENDIAN_LITTLE:
map->format.format_val = regmap_format_16_le;
map->format.parse_val = regmap_parse_16_le;
map->format.parse_inplace = regmap_parse_16_le_inplace;
break;
case REGMAP_ENDIAN_NATIVE:
map->format.format_val = regmap_format_16_native;
map->format.parse_val = regmap_parse_16_native;
break;
default:
goto err_map;
}
break;
} /* 对于val_bits = 16,reg_bits=16,regmap写函数选择_regmap_bus_raw_write */
if (map->format.format_write) {
map->defer_caching = false;
map->reg_write = _regmap_bus_formatted_write;
} else if (map->format.format_val) {
map->defer_caching = true;
map->reg_write = _regmap_bus_raw_write;
}
// 缓存初始化
ret = regcache_init(map, config);
}

regmap_init()函数初始化了regmap,regmap中函数bus指针和config寄存器参数,已经足够用来操作I2C的寄存器了。

同时,也设置了regmap.reg_read_regmap_bus_read

regmap原型

struct regmap {
union {
struct mutex mutex;
struct {
spinlock_t spinlock;
unsigned long spinlock_flags;
};
};
regmap_lock lock;
regmap_unlock unlock;
void *lock_arg; /* This is passed to lock/unlock functions */
gfp_t alloc_flags; struct device *dev; /* Device we do I/O on */
void *work_buf; /* Scratch buffer used to format I/O */
struct regmap_format format; /* Buffer format */
const struct regmap_bus *bus;
void *bus_context;
const char *name; bool async;
spinlock_t async_lock;
wait_queue_head_t async_waitq;
struct list_head async_list;
struct list_head async_free;
int async_ret; unsigned int max_register;
bool (*writeable_reg)(struct device *dev, unsigned int reg);
bool (*readable_reg)(struct device *dev, unsigned int reg);
bool (*volatile_reg)(struct device *dev, unsigned int reg);
bool (*precious_reg)(struct device *dev, unsigned int reg);
const struct regmap_access_table *wr_table;
const struct regmap_access_table *rd_table;
const struct regmap_access_table *volatile_table;
const struct regmap_access_table *precious_table; int (*reg_read)(void *context, unsigned int reg, unsigned int *val);
int (*reg_write)(void *context, unsigned int reg, unsigned int val);
int (*reg_update_bits)(void *context, unsigned int reg,
unsigned int mask, unsigned int val); bool defer_caching; unsigned long read_flag_mask;
unsigned long write_flag_mask; /* number of bits to (left) shift the reg value when formatting*/
int reg_shift;
int reg_stride;
int reg_stride_order; /* regcache specific members */
const struct regcache_ops *cache_ops;
enum regcache_type cache_type; /* number of bytes in reg_defaults_raw */
unsigned int cache_size_raw;
/* number of bytes per word in reg_defaults_raw */
unsigned int cache_word_size;
/* number of entries in reg_defaults */
unsigned int num_reg_defaults;
/* number of entries in reg_defaults_raw */
unsigned int num_reg_defaults_raw; /* if set, only the cache is modified not the HW */
bool cache_only;
/* if set, only the HW is modified not the cache */
bool cache_bypass;
/* if set, remember to free reg_defaults_raw */
bool cache_free; struct reg_default *reg_defaults;
const void *reg_defaults_raw;
void *cache;
/* if set, the cache contains newer data than the HW */
bool cache_dirty;
/* if set, the HW registers are known to match map->reg_defaults */
bool no_sync_defaults; struct reg_sequence *patch;
int patch_regs; /* if set, converts bulk read to single read */
bool use_single_read;
/* if set, converts bulk read to single read */
bool use_single_write;
/* if set, the device supports multi write mode */
bool can_multi_write; /* if set, raw reads/writes are limited to this size */
size_t max_raw_read;
size_t max_raw_write; struct rb_root range_tree;
void *selector_work_buf; /* Scratch buffer used for selector */
};

_regmap_bus_read

reg_read回调函数是我们在读数据要使用的方法,

刚刚已经说了,map->reg_read = _regmap_bus_read;

我们需要看其实现:

// drivers/base/regmap/regmap.c

static int _regmap_bus_read(void *context, unsigned int reg,
unsigned int *val)
{
int ret;
struct regmap *map = context; if (!map->format.parse_val)
return -EINVAL;
// 读出寄存器数据
ret = _regmap_raw_read(map, reg, map->work_buf, map->format.val_bytes);
if (ret == 0) // 根据寄存器位值宽和大小端得到寄存器值
*val = map->format.parse_val(map->work_buf); return ret;
}

_regmap_raw_read

继续看:

static int _regmap_raw_read(struct regmap *map, unsigned int reg, void *val,
unsigned int val_len)
{
struct regmap_range_node *range;
int ret; WARN_ON(!map->bus); if (!map->bus || !map->bus->read)
return -EINVAL;
// 从范围中找到某一页(但在这里没有用到)。
range = _regmap_range_lookup(map, reg);
if (range) {
ret = _regmap_select_page(map, &reg, range,
val_len / map->format.val_bytes);
if (ret != 0)
return ret;
} map->format.format_reg(map->work_buf, reg, map->reg_shift);
regmap_set_work_buf_flag_mask(map, map->format.reg_bytes,
map->read_flag_mask);
trace_regmap_hw_read_start(map, reg, val_len / map->format.val_bytes);
// 使用bus定义的read函数指针读数据,这样子就会调用到`regmap_i2c_read`
ret = map->bus->read(map->bus_context, map->work_buf,
map->format.reg_bytes + map->format.pad_bytes,
val, val_len); trace_regmap_hw_read_done(map, reg, val_len / map->format.val_bytes); return ret;
}
查找range(不理解)

这个部分的代码好像是不会执行的。

// drivers/base/regmap/internal.h
struct regmap_range_node {
struct rb_node node;
const char *name;
struct regmap *map; unsigned int range_min;
unsigned int range_max; unsigned int selector_reg;
unsigned int selector_mask;
int selector_shift;
// range_min、range_max是当前reg范围的开始与结束地址,window_len是”窗”的长度.
unsigned int window_start;
unsigned int window_len;
}; // drivers/base/regmap/regmap.c
static struct regmap_range_node *_regmap_range_lookup(struct regmap *map,
unsigned int reg)
{
struct rb_node *node = map->range_tree.rb_node;
// reg是当前寄存器的index
// range_min、range_max是当前reg范围的开始与结束地址
while (node) {
struct regmap_range_node *this =
rb_entry(node, struct regmap_range_node, node);
// 找到 落在 min 与 max 这个范围中的 reg
if (reg < this->range_min)
node = node->rb_left;
else if (reg > this->range_max)
node = node->rb_right;
else
return this;
} return NULL;
} static int _regmap_select_page(struct regmap *map, unsigned int *reg,
struct regmap_range_node *range,
unsigned int val_num)
{
void *orig_work_buf;
unsigned int win_offset;
unsigned int win_page;
bool page_chg;
int ret; // 计算偏移
win_offset = (*reg - range->range_min) % range->window_len;
win_page = (*reg - range->range_min) / range->window_len; if (val_num > 1) {
/* Bulk write shouldn't cross range boundary */
if (*reg + val_num - 1 > range->range_max)
return -EINVAL; /* ... or single page boundary */
if (val_num > range->window_len - win_offset)
return -EINVAL;
} /* It is possible to have selector register inside data window.
In that case, selector register is located on every page and
it needs no page switching, when accessed alone. */
/* 如果目标寄存器跨越1个寄存器(超过4个字节了)
调用_regmap_update_bits来拿到新的数据
_regmap_update_bits调用_regmap_read
先从i2c中目标寄存器中读出值orig,
然后把该值orig掩码掉mask,然后或上val,
将得到的结果调用_regmap_write写入到目标寄存器
*/
if (val_num > 1 ||
range->window_start + win_offset != range->selector_reg) {
/* Use separate work_buf during page switching */
orig_work_buf = map->work_buf;
map->work_buf = map->selector_work_buf; ret = _regmap_update_bits(map, range->selector_reg,
range->selector_mask,
win_page << range->selector_shift,
&page_chg, false); map->work_buf = orig_work_buf; if (ret != 0)
return ret;
} *reg = range->window_start + win_offset; return 0;
}

regmap_read

regmap_read函数用来读取寄存器值,实现如下:

int regmap_read(struct regmap *map, unsigned int reg, unsigned int *val)
{
int ret;
// 通过寄存器操作宽度判断寄存器操作是否合法
if (reg % map->reg_stride)
return -EINVAL; map->lock(map->lock_arg); ret = _regmap_read(map, reg, val); map->unlock(map->lock_arg); return ret;
} static int _regmap_read(struct regmap *map, unsigned int reg,
unsigned int *val)
{
// 1.直接从缓存取值
if (!map->cache_bypass) {
ret = regcache_read(map, reg, val);
if (ret == 0)
return 0;
}
// 2.最终调用bus定义的read读寄存器数据
ret = map->reg_read(context, reg, val);
// 3.将寄存器结果写入缓存
if (!map->cache_bypass)
regcache_write(map, reg, *val); return ret;
}

regmap_read函数最后调用bus的read函数,同理,regmap_write函数最终也会调用bus的write函数。

regmap机制中的缓存

关于缓冲,需要解释的是,在regmap中加入了一层缓存,减少IO操作次数,提供硬件操作效率;

/* An enum of all the supported cache types */
enum regcache_type {
REGCACHE_NONE,
REGCACHE_RBTREE,//红黑树类型
REGCACHE_COMPRESSED,//压缩类型
REGCACHE_FLAT,//普通数据类型
};

在Linux 4.0 版本中,已经有 3 种缓存类型,分别是数据(flat)、LZO 压缩和红黑树(rbtree)。

  • 数据好理解,是最简单的缓存类型,当设备寄存器很少时,可以用这种类型来缓存寄存器值。
  • LZO(Lempel–Ziv–Oberhumer) 是 Linux 中经常用到的一种压缩算法,Linux 编译后就会用这个算法来压缩。这个算法有 3 个特性:压缩快,解压不需要额外内存,压缩比可以自动调节。在这里,你可以理解为一个数组缓存,套了一层压缩,来节约内存。当设备寄存器数量中等时,可以考虑这种缓存类型。
  • 而最后一类红黑树,它的特性就是索引快,所以当设备寄存器数量比较大,或者对寄存器操作延时要求低时,就可以用这种缓存类型。

缓存的类型是在 regmap 初始化时,由配置regmap_configcache_type 来指定的。

对于 regmap_read 来说,会先判断当前缓存是否有值,然后再检查是否需要 bypass,若没有,则可以直接从缓存里面取值,调用regcache_read来获取值,若需要从硬件上读取,则调用具体协议的读写函数,若是 I2C,调用i2c_transfer。

写的过程也是大同小异。

到此,在驱动程序中regmap的基本使用方法和调用简析就结束了。

Linux内核:regmap机制的更多相关文章

  1. [内核同步]浅析Linux内核同步机制

    转自:http://blog.csdn.net/fzubbsc/article/details/37736683?utm_source=tuicool&utm_medium=referral ...

  2. Linux内核同步机制--转发自蜗窝科技

    Linux内核同步机制之(一):原子操作 http://www.wowotech.net/linux_kenrel/atomic.html 一.源由 我们的程序逻辑经常遇到这样的操作序列: 1.读一个 ...

  3. Linux内核同步机制

    http://blog.csdn.net/bullbat/article/details/7376424 Linux内核同步控制方法有很多,信号量.锁.原子量.RCU等等,不同的实现方法应用于不同的环 ...

  4. Linux内核OOM机制的详细分析(转)

    Linux 内核 有个机制叫OOM killer(Out-Of-Memory killer),该机制会监控那些占用内存过大,尤其是瞬间很快消耗大量内存的进程,为了 防止内存耗尽而内核会把该进程杀掉.典 ...

  5. Linux内核同步机制之(五):Read Write spin lock【转】

    一.为何会有rw spin lock? 在有了强大的spin lock之后,为何还会有rw spin lock呢?无他,仅仅是为了增加内核的并发,从而增加性能而已.spin lock严格的限制只有一个 ...

  6. Linux内核同步机制之completion【转】

    Linux内核同步机制之completion 内核编程中常见的一种模式是,在当前线程之外初始化某个活动,然后等待该活动的结束.这个活动可能是,创建一个新的内核线程或者新的用户空间进程.对一个已有进程的 ...

  7. 浅析Linux内核同步机制

    非常早之前就接触过同步这个概念了,可是一直都非常模糊.没有深入地学习了解过,最近有时间了,就花时间研习了一下<linux内核标准教程>和<深入linux设备驱动程序内核机制>这 ...

  8. Linux内核同步机制之(四):spin lock【转】

    转自:http://www.wowotech.net/kernel_synchronization/spinlock.html 一.前言 在linux kernel的实现中,经常会遇到这样的场景:共享 ...

  9. linux 内核 RCU机制详解

    RCU(Read-Copy Update)是数据同步的一种方式,在当前的Linux内核中发挥着重要的作用.RCU主要针对的数据对象是链表,目的是提高遍历读取数据的效率,为了达到目的使用RCU机制读取数 ...

  10. Linux内核配置机制(make menuconfig 、Kconfig、Makefile)讲解【转】

    本文转载自:http://www.codexiu.cn/linux/blog/34801/ 前面我们介绍模块编程的时候介绍了驱动进入内核有两种方式:模块和直接编译进内核,并介绍了模块的一种编译方式—— ...

随机推荐

  1. 51k+ Star!动画图解、一键运行的数据结构与算法教程!

    大家好,我是 Java陈序员. 我们都知道,<数据结构与算法> -- 是程序员的必修课. 无论是使用什么编程语音,亦或者是前后端开发,都需要修好<数据结构与算法>这门课! 在各 ...

  2. Mybatis-plus把List数据分页

    一.编写工具类: /** * @project * @Description 多表联查-分页 * @Author songwp * @Date 2022/8/8 10:31 * @Version 1. ...

  3. fastposter v2.8.2 发布 电商海报生成器

    fastposter v2.8.2 发布 电商海报生成器 fastposter海报生成器,电商海报编辑器,电商海报设计器,fast快速生成海报 海报制作 海报开发.二维码海报,图片海报,分享海报,二维 ...

  4. 那什么是URL、URI、URN?

    URI Uniform Resource Identifier 统一资源标识符 URL Uniform Resource Locator 统一资源定位符 URN Uniform Resource Na ...

  5. WPF绑定数据源到ListBox等selector的注意事项

    如果使用CollectionViewSource绑定到控件上,会导致默认选择第一项,而使用List,SelectedItem就默认为空. 要避免默认选择第一项,就要设置 ListBox.IsSynch ...

  6. C# XML转Json Json转XML XML 转对象 对象转XML

    对象转XML对象时,只能是一个JObject对象,不能是一个集合对象.如果对象是一个列表集合,需要定义一个根对象比如这样:var obj =new { Root = ListLogs[ListLogs ...

  7. Qt-FFmpeg开发-音频解码为PCM文件(9)

    音视频/FFmpeg #Qt Qt-FFmpeg开发-使用libavcodec API的音频解码示例(MP3转pcm) 目录 音视频/FFmpeg #Qt Qt-FFmpeg开发-使用libavcod ...

  8. 继承,super,重写,多态,抽象,接口

    继承,super,重写,多态,抽象,接口 继承 extends 用于表示两个类之间的继承关系,继承是OOP的四大特性之一,他允许一个类(称之为子类或派送类) 继承另一个类(称之为父类或基类)的变量和方 ...

  9. C++笔记(6) 指针

    1.指针和数组 指针和数组基本等价的原因在于指针算数和C++内部处理数组的方式.在很多情况下,可以用相同的方式使用数组名和指针名. 在多数情况下,C++将数组名视为数组的第一个元素的地址.指针p的值为 ...

  10. 原来Stable Diffusion是这样工作的

    stable diffusion是一种潜在扩散模型,可以从文本生成人工智能图像.为什么叫做潜在扩散模型呢?这是因为与在高维图像空间中操作不同,它首先将图像压缩到潜在空间中,然后再进行操作. 在这篇文章 ...