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

相关推荐
CS_浮鱼2 小时前
【Linux】进程控制
linux·运维·网络
Miraitowa_cheems2 小时前
LeetCode算法日记 - Day 104: 通配符匹配
linux·数据结构·算法·leetcode·深度优先·动态规划
fengyehongWorld2 小时前
Linux stat命令
linux
人工智能训练3 小时前
Docker中容器的备份方法和步骤
linux·运维·人工智能·ubuntu·docker·容器·nvidia
渡我白衣3 小时前
深入 Linux 内核启动:从按下电源到用户登录的全景解剖
java·linux·运维·服务器·开发语言·c++·人工智能
代码炼金术士3 小时前
linux的nginx版本升级
linux·运维·nginx
讨厌下雨的天空4 小时前
进程优先级
linux·服务器
大柏怎么被偷了4 小时前
【Linux】版本控制器git
linux·运维·服务器
JiMoKuangXiangQu4 小时前
busybox:启动阶段的静态 IP 配置过程
linux·busybox·静态ip配置