Linux内核用户身份管理全链路深度剖析:setuid系统调用完整架构

前言

在现代操作系统的安全架构中,用户身份管理是构建权限隔离和安全边界的基石。Linux内核通过一套精密的用户身份切换机制,实现了进程权限的动态调整和安全控制。本文将以Linux 2.6.10内核为例,深入剖析用户身份管理的完整技术栈,从最上层的系统调用接口到底层的原子操作实现,揭示Linux如何在高性能和高安全性之间取得完美平衡。

文章将系统性地解析五个关键层次组成的完整调用链:sys_setuid ------作为用户身份切换的系统调用入口,承担权限验证和流程控制的核心职责;set_user ------执行实际的用户切换操作,处理资源限制检查和内存管理;switch_uid ------完成用户上下文的原子切换,更新进程计数和安全上下文;alloc_uid ------通过高效的哈希表缓存机制管理用户数据结构;atomic_dec_and_lock------利用无锁编程和条件锁获取优化并发性能。通过对这一完整技术栈的逐层分析,我们将展现Linux内核如何在多核并发环境下,通过能力机制、引用计数、内存屏障和乐观并发控制等先进技术,构建出既符合POSIX标准又具备卓越性能的用户身份管理系统。

用户身份切换sys_setuid

c 复制代码
asmlinkage long sys_setuid(uid_t uid)
{
	int old_euid = current->euid;
	int old_ruid, old_suid, new_ruid, new_suid;
	int retval;

	retval = security_task_setuid(uid, (uid_t)-1, (uid_t)-1, LSM_SETID_ID);
	if (retval)
		return retval;

	old_ruid = new_ruid = current->uid;
	old_suid = current->suid;
	new_suid = old_suid;
	
	if (capable(CAP_SETUID)) {
		if (uid != old_ruid && set_user(uid, old_euid != uid) < 0)
			return -EAGAIN;
		new_suid = uid;
	} else if ((uid != current->uid) && (uid != new_suid))
		return -EPERM;

	if (old_euid != uid)
	{
		current->mm->dumpable = 0;
		wmb();
	}
	current->fsuid = current->euid = uid;
	current->suid = new_suid;

	key_fsuid_changed(current);

	return security_task_post_setuid(old_ruid, old_euid, old_suid, LSM_SETID_ID);
}

函数功能分析

sys_setuid函数实现了Linux系统中的用户身份切换机制,通过精细的权限检查和状态管理,确保进程用户标识的安全变更,同时遵循POSIX标准和Linux安全模块的要求。

变量声明与初始化

c 复制代码
int old_euid = current->euid;
int old_ruid, old_suid, new_ruid, new_suid;
int retval;
  • 当前状态保存old_euid保存当前有效用户ID,用于后续比较和恢复
  • 多标识符管理 :声明real uidsaved uid的旧值和新值变量
  • 返回值准备retval用于存储安全检查的返回结果

Linux安全模块检查

c 复制代码
retval = security_task_setuid(uid, (uid_t)-1, (uid_t)-1, LSM_SETID_ID);
if (retval)
	return retval;
  • LSM钩子调用:调用Linux安全模块进行权限变更前的安全检查
  • 参数说明 :只设置uid,其他参数为-1表示不修改,LSM_SETID_ID表示setuid操作
  • 提前返回:如果安全模块拒绝操作,立即返回错误码

用户标识符状态初始化

c 复制代码
old_ruid = new_ruid = current->uid;
old_suid = current->suid;
new_suid = old_suid;
  • real uid初始化old_ruidnew_ruid初始化为当前real uid
  • saved uid保存old_suid保存当前saved uid状态
  • 新值预设new_suid默认保持不变,后续根据权限调整

特权进程处理路径

c 复制代码
if (capable(CAP_SETUID)) {
	if (uid != old_ruid && set_user(uid, old_euid != uid) < 0)
		return -EAGAIN;
	new_suid = uid;
}
  • 能力检查capable(CAP_SETUID)检查进程是否有设置用户ID的能力
  • 用户切换 :如果目标uid与当前real uid不同,调用set_user进行实际用户切换
  • 资源限制set_user失败返回-EAGAIN,通常是因为超出资源限制
  • saved uid更新 :特权进程设置saved uid为目标uid

非特权进程限制检查

c 复制代码
else if ((uid != current->uid) && (uid != new_suid))
	return -EPERM;
  • 权限限制 :非特权进程只能将effective uid设置为当前real uidsaved uid
  • 错误返回 :如果尝试设置其他uid,返回权限错误-EPERM
  • POSIX合规 :遵循POSIX标准对非特权进程setuid的限制

核心转储权限管理

c 复制代码
if (old_euid != uid)
{
	current->mm->dumpable = 0;
	wmb();
}
  • 权限变更检测 :检查effective uid是否实际发生变化
  • 核心转储禁用 :当effective uid变化时,设置进程为不可核心转储状态
  • 内存屏障wmb()写内存屏障确保修改对其他CPU可见,防止指令重排

用户标识符实际更新

c 复制代码
current->fsuid = current->euid = uid;
current->suid = new_suid;
  • effective uid设置 :更新进程的effective uid为目标值
  • 文件系统uid同步fsuideffective uid保持同步,用于文件系统权限检查
  • saved uid更新 :根据之前逻辑设置新的saved uid

安全子系统通知

c 复制代码
key_fsuid_changed(current);

return security_task_post_setuid(old_ruid, old_euid, old_suid, LSM_SETID_ID);
  • 密钥环通知key_fsuid_changed通知内核密钥环子系统用户ID变更
  • 事后安全钩子 :调用LSM的post_setuid钩子,传递旧值用于审计
  • 操作完成:返回安全模块的最终结果

函数功能总结

sys_setuid系统调用是Linux用户身份管理的核心组件,通过多层级的权限验证和状态管理,实现了安全可靠的用户标识符切换机制:

  1. 安全优先:集成LSM框架,在操作前后进行完整的安全检查
  2. 权限分离:区分特权进程和非特权进程的不同行为模式
  3. 状态一致性 :确保effective uidreal uidsaved uidfsuid的同步更新
  4. 安全防护:在权限降低时自动禁用核心转储,防止信息泄露
  5. 子系统协同:通知密钥环等子系统关于用户标识符的变更
  6. 标准兼容:遵循POSIX标准和System V的SAVED_IDS语义

该实现体现了Linux在保持向后兼容性的同时,通过能力机制和LSM框架实现了更现代、更安全的权限管理模型,为setuid-root程序等特权操作提供了坚实的基础设施支持。

执行用户切换set_user

c 复制代码
static int set_user(uid_t new_ruid, int dumpclear)
{
	struct user_struct *new_user;

	new_user = alloc_uid(new_ruid);
	if (!new_user)
		return -EAGAIN;

	if (atomic_read(&new_user->processes) >=
				current->signal->rlim[RLIMIT_NPROC].rlim_cur &&
			new_user != &root_user) {
		free_uid(new_user);
		return -EAGAIN;
	}

	switch_uid(new_user);

	if(dumpclear)
	{
		current->mm->dumpable = 0;
		wmb();
	}
	current->uid = new_ruid;
	return 0;
}

函数功能分析

set_user函数是setuid操作的底层实现核心,负责分配新的用户数据结构、检查进程数限制、执行用户切换并管理核心转储权限,确保用户身份变更的资源安全和权限控制。

新用户结构分配

c 复制代码
struct user_struct *new_user;

new_user = alloc_uid(new_ruid);
if (!new_user)
	return -EAGAIN;
  • 用户结构指针new_user用于指向新用户的内部数据结构
  • 资源分配alloc_uid(new_ruid)根据新的真实用户ID分配或查找对应的user_struct
  • 分配失败处理 :如果内存不足无法分配用户结构,返回-EAGAIN表示资源暂时不可用

进程数限制检查

c 复制代码
if (atomic_read(&new_user->processes) >=
			current->signal->rlim[RLIMIT_NPROC].rlim_cur &&
		new_user != &root_user) {
	free_uid(new_user);
	return -EAGAIN;
}
  • 原子读取atomic_read(&new_user->processes)安全地读取当前用户的进程计数
  • 资源限制比较 :检查是否超过RLIMIT_NPROC限制(每个用户的进程数限制)
  • root用户豁免root_user不受进程数限制,这是特权用户的特殊待遇
  • 资源释放 :如果超过限制,释放之前分配的用户结构并返回-EAGAIN

用户身份切换执行

c 复制代码
switch_uid(new_user);
  • 关键切换操作 :调用switch_uid函数执行实际的用户身份切换
  • 内部状态更新:更新内核中所有与用户身份相关的内部状态
  • 引用计数管理:调整新旧用户结构的引用计数

核心转储权限管理

c 复制代码
if(dumpclear)
{
	current->mm->dumpable = 0;
	wmb();
}
  • 条件性清理 :仅在dumpclear参数为真时禁用核心转储
    • dumpclear机制在权限变化时保护敏感信息
  • 安全防护current->mm->dumpable = 0设置进程为不可核心转储状态
  • 内存屏障wmb()写内存屏障确保修改对所有CPU核心可见,防止指令重排导致的竞态条件

真实用户ID更新

c 复制代码
current->uid = new_ruid;
return 0;
  • 最终状态设置:将进程的真实用户ID设置为目标值
  • 成功返回:返回0表示用户切换操作成功完成

函数功能总结

set_user函数是Linux用户身份管理的关键底层组件,通过精细的资源管理和安全控制,实现了安全可靠的用户切换机制:

  1. 资源安全:严格检查RLIMIT_NPROC限制,防止用户进程数超限
  2. 内存管理:正确分配和释放user_struct,维护引用计数完整性
  3. 权限控制:在权限变更时智能管理核心转储能力,保护敏感信息
  4. 并发安全:使用原子操作和内存屏障确保多核环境下的数据一致性
  5. 错误恢复:统一的错误码和资源清理,确保操作的事务性

该函数体现了Linux内核在用户身份管理方面的核心设计原则:安全性优先、资源可控、状态一致。作为setuid系统调用的核心实现部分,它为进程权限管理提供了可靠的基础设施支持。

执行进程用户身份的原子切换switch_uid

c 复制代码
void switch_uid(struct user_struct *new_user)
{
	struct user_struct *old_user;

	old_user = current->user;
	atomic_inc(&new_user->processes);
	atomic_dec(&old_user->processes);
	switch_uid_keyring(new_user);
	current->user = new_user;
	free_uid(old_user);
	suid_keys(current);
}

函数功能分析

switch_uid函数负责执行进程用户身份的原子切换,更新进程计数、密钥环和其他用户相关的安全上下文,确保用户切换过程中资源统计的正确性和安全性。

旧用户结构保存

c 复制代码
struct user_struct *old_user;

old_user = current->user;
  • 旧用户指针保存old_user保存当前进程的用户结构指针
  • 现状分析 :此时假设资源限制检查已在set_user中完成

进程计数更新

c 复制代码
atomic_inc(&new_user->processes);
atomic_dec(&old_user->processes);
  • 新用户进程计数增加atomic_inc原子性地增加新用户的进程计数
  • 旧用户进程计数减少atomic_dec原子性地减少旧用户的进程计数
  • 统计完整性:确保系统范围内每个用户的进程计数准确
  • 原子性保证:使用原子操作防止并发更新导致计数错误

密钥环上下文切换

c 复制代码
switch_uid_keyring(new_user);
  • 安全上下文切换:切换到新用户对应的密钥环

当前用户指针更新

c 复制代码
current->user = new_user;
  • 身份标识更新:将进程的当前用户指针指向新的用户结构
  • 原子性切换:这是实际的用户身份切换点
  • 后续操作基础:所有后续的用户相关操作都基于新的用户身份

旧用户资源释放

c 复制代码
free_uid(old_user);
  • 引用计数管理:减少旧用户结构的引用计数
  • 条件性释放:如果引用计数降为零,释放用户结构内存
  • 资源回收:清理不再使用的用户相关资源
  • 生命周期管理:基于引用计数的自动内存管理

会话密钥更新

c 复制代码
suid_keys(current);
  • 会话密钥重置:更新与用户ID相关的会话密钥

分配和管理用户数据结构alloc_uid

c 复制代码
struct user_struct * alloc_uid(uid_t uid)
{
	struct list_head *hashent = uidhashentry(uid);
	struct user_struct *up;

	spin_lock(&uidhash_lock);
	up = uid_hash_find(uid, hashent);
	spin_unlock(&uidhash_lock);

	if (!up) {
		struct user_struct *new;

		new = kmem_cache_alloc(uid_cachep, SLAB_KERNEL);
		if (!new)
			return NULL;
		new->uid = uid;
		atomic_set(&new->__count, 1);
		atomic_set(&new->processes, 0);
		atomic_set(&new->files, 0);
		atomic_set(&new->sigpending, 0);

		new->mq_bytes = 0;
		new->locked_shm = 0;

		if (alloc_uid_keyring(new) < 0) {
			kmem_cache_free(uid_cachep, new);
			return NULL;
		}

		/*
		 * Before adding this, check whether we raced
		 * on adding the same user already..
		 */
		spin_lock(&uidhash_lock);
		up = uid_hash_find(uid, hashent);
		if (up) {
			key_put(new->uid_keyring);
			key_put(new->session_keyring);
			kmem_cache_free(uid_cachep, new);
		} else {
			uid_hash_insert(new, hashent);
			up = new;
		}
		spin_unlock(&uidhash_lock);

	}
	return up;
}

函数功能分析

alloc_uid函数负责分配和管理用户数据结构,通过哈希表缓存实现高效的用户结构查找和分配,采用无锁查找加锁分配的模式优化性能,确保用户资源的正确引用计数管理。

哈希表查找准备

c 复制代码
struct list_head *hashent = uidhashentry(uid);
struct user_struct *up;

spin_lock(&uidhash_lock);
up = uid_hash_find(uid, hashent);
spin_unlock(&uidhash_lock);
  • 哈希桶定位uidhashentry(uid)根据用户ID计算哈希值,定位到对应的哈希桶
  • 短暂加锁 :使用自旋锁uidhash_lock保护哈希表的并发访问
  • 快速查找uid_hash_find在哈希链表中查找是否已存在该用户ID的结构
  • 立即释放锁:查找完成后立即释放锁,减少锁竞争时间

缓存未命中慢速路径

c 复制代码
if (!up) {
  • 缓存检查 :如果up为NULL,说明在哈希表中没有找到现有的用户结构

新用户结构分配

c 复制代码
	struct user_struct *new;

	new = kmem_cache_alloc(uid_cachep, SLAB_KERNEL);
	if (!new)
		return NULL;
	new->uid = uid;
	atomic_set(&new->__count, 1);
	atomic_set(&new->processes, 0);
	atomic_set(&new->files, 0);
	atomic_set(&new->sigpending, 0);

	new->mq_bytes = 0;
	new->locked_shm = 0;
  • Slab分配kmem_cache_alloc(uid_cachep, SLAB_KERNEL)从专用slab缓存分配用户结构
  • 内存不足处理:如果分配失败返回NULL,向上层传递错误
  • 基础字段初始化
    • uid:设置用户ID
    • __count:引用计数初始化为1
    • processes:进程计数初始化为0
    • files:文件计数初始化为0
    • sigpending:待处理信号计数初始化为0
  • 资源字段清零
    • mq_bytes:消息队列字节数清零
    • locked_shm:锁定共享内存清零

密钥环分配与错误处理

c 复制代码
	if (alloc_uid_keyring(new) < 0) {
		kmem_cache_free(uid_cachep, new);
		return NULL;
	}
  • 密钥环分配alloc_uid_keyring为用户分配专用的密钥环结构
  • 分配失败处理:如果密钥环分配失败,释放之前分配的用户结构内存
  • 资源清理:确保分配过程的事务性,要么全部成功,要么全部回滚

竞争条件检查与哈希表插入

c 复制代码
		spin_lock(&uidhash_lock);
		up = uid_hash_find(uid, hashent);
		if (up) {
			key_put(new->uid_keyring);
			key_put(new->session_keyring);
			kmem_cache_free(uid_cachep, new);
		} else {
			uid_hash_insert(new, hashent);
			up = new;
		}
		spin_unlock(&uidhash_lock);
  • 重新加锁检查:在插入前再次检查是否其他线程已创建相同用户结构
  • 竞争处理 :如果发现竞争(up不为NULL),清理新分配的结构
  • 资源释放
    • key_put:释放密钥环引用
    • kmem_cache_free:释放用户结构内存
  • 哈希表插入:如果没有竞争,将新结构插入哈希表并设置为返回结果

结果返回

c 复制代码
	}
return up;
  • 统一返回:无论通过缓存找到还是新分配,都返回有效的用户结构指针
  • 引用计数:返回的结构已有正确的引用计数

关键设计模式

检查-分配-检查模式 (Check-Allocate-Check)

c 复制代码
// 第一阶段:无锁快速检查
// 第二阶段:分配资源
// 第三阶段:加锁验证并插入
// 这种模式平衡了性能和正确性

乐观并发控制

c 复制代码
// 假设没有竞争,先分配资源
// 在提交前验证假设,如果失败则回滚
// 避免在持有锁的情况下进行可能阻塞的分配操作

数据结构管理

引用计数机制

c 复制代码
// atomic_set(&new->__count, 1) 初始引用计数为1
// 后续通过get_uid/put_uid管理生命周期

资源统计字段

c 复制代码
// processes: 跟踪该用户的进程数量
// files: 跟踪打开文件数量  
// sigpending: 待处理信号计数
// mq_bytes/locked_shm: 资源使用限制

函数功能总结

alloc_uid函数是Linux用户管理子系统的核心基础设施,通过精巧的并发设计和资源管理,实现了高效可靠的用户结构分配:

  1. 性能优化:采用无锁查找和乐观分配策略,最小化锁竞争
  2. 并发安全:通过检查-分配-检查模式正确处理竞争条件
  3. 资源管理:完整的引用计数和资源统计,支持正确的生命周期管理
  4. 错误恢复:分层错误处理和资源清理,确保系统稳定性
  5. 内存效率:使用slab缓存减少内存分配开销
  6. 安全集成:与密钥环子系统深度集成,支持用户级安全特性

未来完整的用户资源跟踪系统user_struct

c 复制代码
/*
 * Some day this will be a full-fledged user tracking system..
 */
struct user_struct {
	atomic_t __count;	/* reference count */
	atomic_t processes;	/* How many processes does this user have? */
	atomic_t files;		/* How many open files does this user have? */
	atomic_t sigpending;	/* How many pending signals does this user have? */
	/* protected by mq_lock	*/
	unsigned long mq_bytes;	/* How many bytes can be allocated to mqueue? */
	unsigned long locked_shm; /* How many pages of mlocked shm ? */

#ifdef CONFIG_KEYS
	struct key *uid_keyring;	/* UID specific keyring */
	struct key *session_keyring;	/* UID's default session keyring */
#endif

	/* Hash table maintenance information */
	struct list_head uidhash_list;
	uid_t uid;
};
/*
 * UID task count cache, to get fast user lookup in "alloc_uid"
 * when changing user ID's (ie setuid() and friends).
 */
#define UIDHASH_BITS		8
#define UIDHASH_SZ		(1 << UIDHASH_BITS)
#define UIDHASH_MASK		(UIDHASH_SZ - 1)
#define __uidhashfn(uid)	(((uid >> UIDHASH_BITS) + uid) & UIDHASH_MASK)
#define uidhashentry(uid)	(uidhash_table + __uidhashfn((uid)))

static kmem_cache_t *uid_cachep;
static struct list_head uidhash_table[UIDHASH_SZ];
static spinlock_t uidhash_lock = SPIN_LOCK_UNLOCKED;

struct user_struct root_user = {
	.__count	= ATOMIC_INIT(1),
	.processes	= ATOMIC_INIT(1),
	.files		= ATOMIC_INIT(0),
	.sigpending	= ATOMIC_INIT(0),
	.mq_bytes	= 0,
	.locked_shm     = 0,
#ifdef CONFIG_KEYS
	.uid_keyring	= &root_user_keyring,
	.session_keyring = &root_session_keyring,
#endif
};

/*
 * These routines must be called with the uidhash spinlock held!
 */
static inline void uid_hash_insert(struct user_struct *up, struct list_head *hashent)
{
	list_add(&up->uidhash_list, hashent);
}

static inline void uid_hash_remove(struct user_struct *up)
{
	list_del(&up->uidhash_list);
}

static inline struct user_struct *uid_hash_find(uid_t uid, struct list_head *hashent)
{
	struct list_head *up;

	list_for_each(up, hashent) {
		struct user_struct *user;

		user = list_entry(up, struct user_struct, uidhash_list);

		if(user->uid == uid) {
			atomic_inc(&user->__count);
			return user;
		}
	}

	return NULL;
}

数据结构分析

user_struct - 用户资源跟踪结构

c 复制代码
struct user_struct {
	atomic_t __count;	/* reference count */
	atomic_t processes;	/* How many processes does this user have? */
	atomic_t files;		/* How many open files does this user have? */
	atomic_t sigpending;	/* How many pending signals does this user have? */
	/* protected by mq_lock	*/
	unsigned long mq_bytes;	/* How many bytes can be allocated to mqueue? */
	unsigned long locked_shm; /* How many pages of mlocked shm ? */

#ifdef CONFIG_KEYS
	struct key *uid_keyring;	/* UID specific keyring */
	struct key *session_keyring;	/* UID's default session keyring */
#endif

	/* Hash table maintenance information */
	struct list_head uidhash_list;
	uid_t uid;
};

核心字段说明:

  • __count:引用计数,用于生命周期管理
  • processes:该用户拥有的进程数量统计
  • files:该用户打开的文件数量统计
  • sigpending:待处理信号数量统计
  • mq_bytes:消息队列可分配字节数(受mq_lock保护)
  • locked_shm:mlock状态的共享内存页数
  • 密钥环字段:用户特定的密钥环和会话密钥环
  • 哈希表维护uidhash_list用于哈希链表,uid存储用户ID

哈希表系统设计

哈希表参数定义

c 复制代码
#define UIDHASH_BITS		8
#define UIDHASH_SZ		(1 << UIDHASH_BITS)  // 256个桶
#define UIDHASH_MASK		(UIDHASH_SZ - 1)     // 0xFF掩码
#define __uidhashfn(uid)	(((uid >> UIDHASH_BITS) + uid) & UIDHASH_MASK)
#define uidhashentry(uid)	(uidhash_table + __uidhashfn((uid)))

哈希算法分析:

c 复制代码
// 示例:uid = 1000 (0x3E8)
// __uidhashfn(1000):
//   1000 >> 8 = 3
//   3 + 1000 = 1003
//   1003 & 0xFF = 235
// 所以uid=1000映射到哈希桶235

全局变量声明

c 复制代码
static kmem_cache_t *uid_cachep;                    // 用户结构slab缓存
static struct list_head uidhash_table[UIDHASH_SZ];  // 哈希表数组
static spinlock_t uidhash_lock = SPIN_LOCK_UNLOCKED; // 哈希表保护锁

root用户特殊初始化

root用户静态定义

c 复制代码
struct user_struct root_user = {
	.__count	= ATOMIC_INIT(1),
	.processes	= ATOMIC_INIT(1),
	.files		= ATOMIC_INIT(0),
	.sigpending	= ATOMIC_INIT(0),
	.mq_bytes	= 0,
	.locked_shm     = 0,
#ifdef CONFIG_KEYS
	.uid_keyring	= &root_user_keyring,
	.session_keyring = &root_session_keyring,
#endif
};

root用户特权:

  • 引用计数:初始化为1,确保root用户结构不会被释放
  • 进程计数:初始化为1,表示至少有一个root进程
  • 密钥环:预定义root用户的专用密钥环

哈希表操作函数

插入操作

c 复制代码
static inline void uid_hash_insert(struct user_struct *up, struct list_head *hashent)
{
	list_add(&up->uidhash_list, hashent);
}
  • 链表插入:将用户结构添加到指定哈希桶的链表头部
  • 调用要求 :必须在持有uidhash_lock时调用

删除操作

c 复制代码
static inline void uid_hash_remove(struct user_struct *up)
{
	list_del(&up->uidhash_list);
}
  • 链表删除:从哈希链表中移除用户结构
  • 内存管理:只移除链表链接,不释放内存
  • 调用要求 :必须在持有uidhash_lock时调用

查找操作

c 复制代码
static inline struct user_struct *uid_hash_find(uid_t uid, struct list_head *hashent)
{
	struct list_head *up;

	list_for_each(up, hashent) {
		struct user_struct *user;

		user = list_entry(up, struct user_struct, uidhash_list);

		if(user->uid == uid) {
			atomic_inc(&user->__count);
			return user;
		}
	}

	return NULL;
}
  1. 遍历链表list_for_each宏遍历哈希桶中的所有元素
  2. 类型转换list_entry将链表节点转换为user_struct结构
  3. UID匹配:比较用户ID是否匹配目标UID
  4. 引用计数:找到匹配项时增加引用计数并返回
  5. 未找到:返回NULL表示用户结构不存在

原子递减并条件获取锁atomic_dec_and_lock

c 复制代码
#define atomic_dec_and_lock(atomic,lock) __cond_lock(_atomic_dec_and_lock(atomic,lock))
int _atomic_dec_and_lock(atomic_t *atomic, spinlock_t *lock)
{
	int counter;
	int newcount;

repeat:
	counter = atomic_read(atomic);
	newcount = counter-1;

	if (!newcount)
		goto slow_path;

	asm volatile("lock; cmpxchgl %1,%2"
		:"=a" (newcount)
		:"r" (newcount), "m" (atomic->counter), "0" (counter));

	/* If the above failed, "eax" will have changed */
	if (newcount != counter)
		goto repeat;
	return 0;

slow_path:
	spin_lock(lock);
	if (atomic_dec_and_test(atomic))
		return 1;
	spin_unlock(lock);
	return 0;
}

这是一个原子递减并条件获取锁的函数。它使用__cond_lock宏来配合(sparse)静态分析工具,确保锁的正确使用。

函数功能分析

_atomic_dec_and_lock函数实现了一个高效的原子递减操作,当计数值减到零时自动获取锁,否则无锁快速返回,这是一种常见的"最后释放者获取锁"模式,用于资源引用计数的优化管理。

宏定义包装器

c 复制代码
#define atomic_dec_and_lock(atomic,lock) __cond_lock(_atomic_dec_and_lock(atomic,lock))
  • 接口封装:提供用户友好的宏接口
  • 静态分析支持__cond_lock宏帮助稀疏工具理解锁的获取是条件性的

局部变量声明与初始递减

c 复制代码
int counter;
int newcount;

repeat:
counter = atomic_read(atomic);
newcount = counter-1;
  • 状态变量counter存储当前原子值,newcount存储期望的新值
  • 原子读取atomic_read(atomic)安全地读取原子变量的当前值
  • 预计算新值 :计算递减后的期望值counter-1

快速路径检查

c 复制代码
if (!newcount)
	goto slow_path;
  • 零值检测:如果新值将为0,跳转到慢速路径获取锁
  • 性能优化:这是关键的性能检查,避免在非零情况下不必要的锁操作
  • 快速返回:大多数情况下计数值不为零,可以快速返回

原子比较交换操作

c 复制代码
asm volatile("lock; cmpxchgl %1,%2"
	:"=a" (newcount)
	:"r" (newcount), "m" (atomic->counter), "0" (counter));
  • 内联汇编 :使用x86的lock cmpxchgl指令实现原子比较交换

  • 操作语义 :如果atomic->counter == counter,则atomic->counter = newcount

    asm 复制代码
    ; 指令: lock cmpxchgl 通用寄存器, [atomic->counter]
    ;
    ; 操作数对应:
    ;   EAX = counter (通过"0"约束)
    ;   通用寄存器 = newcount (通过"r"约束)  
    ;   目标内存 = atomic->counter (通过"m"约束)
    ;
    ; 实际语义:
    ; if (EAX == [atomic->counter]) {
    ;    [atomic->counter] = 通用寄存器;  // 即 atomic->counter = newcount
    ;    ZF = 1;
    ; } else {
    ;    EAX = [atomic->counter];        // EAX更新为当前内存值
    ;    ZF = 0;
    ; }
  • 约束说明

    • "=a" (newcount):结果输出到EAX寄存器和newcount变量
    • "r" (newcount):新值作为输入
    • "m" (atomic->counter):内存操作数
    • "0" (counter):期望值输入到EAX寄存器

竞争条件重试

c 复制代码
/* If the above failed, "eax" will have changed */
if (newcount != counter)
	goto repeat;
return 0;
  • 竞争检测 :如果newcount != counter,说明其他线程修改了原子变量
    • 如果未发生竞争,newcount 的值会保持和counter的值一致
    • 如果发生竞争,newcount 的值会和[atomic->counter]的值一致
  • 重试机制 :跳转回repeat标签重新尝试递减操作
  • 成功返回:如果原子操作成功且计数值不为零,返回0表示未获取锁

慢速路径锁获取

c 复制代码
slow_path:
spin_lock(lock);
if (atomic_dec_and_test(atomic))
	return 1;
spin_unlock(lock);
return 0;
  • 锁获取spin_lock(lock)获取自旋锁
  • 最终递减测试atomic_dec_and_test(atomic)原子递减并测试是否为零
  • 成功条件:如果递减后为零,返回1表示成功获取锁
  • 锁释放:如果其他线程已递减到零,释放锁并返回0

函数功能总结

_atomic_dec_and_lock函数是一个精心设计的并发原语,具有以下核心价值:

  1. 性能优化:通过无锁快速路径处理大多数情况,避免不必要的锁开销
  2. 正确性保证:使用原子指令和锁机制确保并发安全
  3. 竞争处理:通过CAS重试机制优雅处理多线程竞争
  4. 资源效率:只在真正需要时(引用计数归零)才获取锁
  5. 架构优化 :针对x86架构使用高效的lock cmpxchgl指令

该函数体现了Linux内核并发编程的精髓:在保证正确性的前提下,通过精细的算法设计最大化性能。它是实现高效资源生命周期管理的基石,广泛应用于各种需要引用计数和条件锁获取的场景。

相关推荐
Doro再努力1 小时前
Vim 快速上手实操手册:从入门到生产环境实战
linux·编辑器·vim
wypywyp1 小时前
8. ubuntu 虚拟机 linux 服务器 TCP/IP 概念辨析
linux·服务器·ubuntu
Doro再努力1 小时前
【Linux操作系统10】Makefile深度解析:从依赖推导到有效编译
android·linux·运维·服务器·编辑器·vim
senijusene1 小时前
Linux软件编程:IO编程,标准IO(1)
linux·运维·服务器
忧郁的橙子.2 小时前
02-本地部署Ollama、Python
linux·运维·服务器
醇氧2 小时前
【linux】查看发行版信息
linux·运维·服务器
No8g攻城狮2 小时前
【Linux】Windows11 安装 WSL2 并运行 Ubuntu 22.04 详细操作步骤
linux·运维·ubuntu
XiaoFan0123 小时前
免密批量抓取日志并集中输出
java·linux·服务器
souyuanzhanvip3 小时前
ServerBox v1.0.1316 跨平台 Linux 服务器管理工具
linux·运维·服务器
HalvmånEver4 小时前
Linux:线程互斥
java·linux·运维