背景

在学习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. JS代码优化小技巧

    下面介绍一种JS代码优化的一个小技巧,通过动态加载引入js外部文件来提高网页加载速度 [基本优化] 将所有需要的<script>标签都放在</body>之前,确保脚本执行之前完 ...

  2. vue从事件修饰符的角度讨论如何合理的设计一个弹窗

    1.设计思路:弹窗一般可以通过封装,单独设计一个组件,在需要的地方引入并通过变量布尔值进行展示和隐藏,方便使用者进行交互或提示信息 具体操作就是在给这个组件背景层添加全屏固定定位并设置透明度(cove ...

  3. WebStorm2023安装prettier并生效

    1.首先去File > Settings > Plugins 里下载并install插件 Prettier 2.在settings里搜索prettier,按图片所示设置一下Apply 3. ...

  4. echarts(数据可视化图表)

    echarts饼图详细 echarts下载 https://echarts.apache.org/zh/index.html  echarts官网 http://www.isqqw.com/#/hom ...

  5. sql 常用手册

    根据code 分组然后取出每组中的任意一行 SELECT *from( SELECT *, row_number () OVER ( partition BY code ORDER BY code D ...

  6. synchronized锁升级过程

    更过博文请关注:https://blog.bigcoder.cn JDK 1.6后锁的状态总共有四种,级别由低到高依次为:无锁.偏向锁.轻量级锁.重量级锁,这四种锁状态分别代表什么,为什么会有锁升级? ...

  7. 轻松绕过 Graphql 接口爬取有米有数的商品数据

    轻松绕过 Graphql 接口爬取有米有数的商品数据 有米有数数据的 API 接口,使用的是一种 API 查询语言 graphql.所有的 API 只有一个入口,具体的操作隐藏在请求数据体里面传输. ...

  8. Uni-app极速入门(二) - 登录demo

    需求 背景 1.进入小程序,默认页面判断用户是否已经登录,已经登录则进入首页,没有登录则进入登录页面 2.首页为tabbar,包括首页和设置页,设置页可以退出登录,回到登录页面 页面流转 graph ...

  9. Flutter(一):MAC的Flutter安装指南

    官网地址 官网: https://flutter.dev Github: https://github.com/flutter/flutter Git的核心分支包括master.dev.stable. ...

  10. kubernetes ingress部署

    ingress概念 ingress与service,deployment同样都是k8s中的一种资源 ingress用于实现域名方式访问k8s内部应用 安装ingress 1. 安装helm: wget ...