【Linux内核中的并发控制】- 自旋锁

2023-11-04

     在内核中会经常看到spin_lock(自旋锁),它到底是个神马东西?在驱动相关的书籍和论坛中查阅了不少资料,看的也是云里雾里,现在将知识罗列总结一下,便于日后回顾!

    

1.自旋锁定义

    在Linux内核并发控制中最常见的锁就是自旋锁。自旋锁最多只能被一个可执行的线程持有。如果一个执行线程试图获得一个已经被持有的自旋锁,那么该线程就会一直进行“ 忙循环 — 旋转 — 等待锁重新可用 ”,这就是自旋的过程。要是锁未被持有(争用),请求锁的执行线程就可以立即得到它,继续执行,而代码继续进入临界区。

    在任意时间内,自旋锁都可以防止多于一个执行线程进入临界区。即使多个线程在给定时间自旋,也只有一个线程可获得该锁。同一个锁可以用在多个位置,比如对于给定数据的所有访问都可以得到保护和同步。

2. 死锁的情况

    自旋锁最初是为了多处理器系统(SMP,multiprocessing system)使用而设计的,但是只要考虑到并发问题,单处理器在运行可抢占内核时,其行为就类似于SMP。因此,自旋锁对于SMP和单处理器可抢占内核都适用。
    可以想象,当一个处理器处于自旋状态时,它做不了任何的工作,因此自旋锁对于单处理器不可抢占内核是没有意义的,实际上非抢占式的单处理器系统上自旋锁常被实现为空操作,不做任何的事情。
    
    自旋锁的重要特性如下:
    1)被自旋锁保护的临界区代码在执行时,不能进入休眠。
    2)被自旋锁保护的临界区代码在执行时,不能被其他中断打断。
    3)被自旋锁保护的临界区代码在执行时,内核不能被抢占。
    从以上几个特性可以归纳出共性:被自旋锁保护的临界区代码在执行时,它不能因为任何原因放弃处理器。
    
    考虑上面第一种和第三种情况,若你的内核代码请求到一个自旋锁并且在它的临界区里做它的事情,在中间某处,你的代码失去了处理器。或许它已调用了一个函数(copy_from_user,假设)使进程进入睡眠。也或许,内核抢占发威,一个更高优先级的进程将你的代码推到了一边。此时,正好某个别的线程想获取同一个锁,如果这个线程运行在和你的内核代码不同的处理器上(幸运的情况),那么它可能要自旋等待一段时间(可能很长),当你的代码从休眠中唤醒或者重新得到处理器并释放锁,它就能得到锁。而最坏的情况是,那个想获取锁得线程刚好和你的代码运行在同一个处理器上,这时它将一直持有CPU进行自旋操作,而你的代码是永远不可能有任何机会来获得CPU释放这个锁了,这就是悲催的死锁
    
    考虑上面第二种情况,和上面第一种情况类似。假设我们的驱动程序正在运行,并且已经获取了一个自旋锁,这个锁控制着对设备的访问。在拥有这个锁得时候,设备产生了一个中断,它导致中断处理例程被调用,而中断处理例程在访问设备之前,也要获得这个锁。当中断处理例程和我们的驱动程序代码在同一个处理器上运行时,由于中断处理例程持有CPU不断自旋,我们的代码将得不到机会释放锁,这也将导致死锁

    因此,如果我们有一个自旋锁,它可以被运行在(硬件或软件)中断上下文中的代码获得,则必须使用某个禁用中断的 spin_lock 形式的锁来禁用本地中断(注意,只是禁用本地CPU的中断,不能禁用别的处理器的中断),使用其他的锁定函数迟早会导致系统死锁(导致死锁的时间可能不定,但是发生上述死锁情况的概率肯定是有的,看处理器怎么调度了)。如果我们不会在硬中断处理例程中访问自旋锁,但可能在软中断(例如,以tasklet的形式运行的代码)中访问,则应该使用 spin_lock_bh,以便在安全避免死锁的同时还能服务硬件中断。
    

3. 自旋锁函数

 3.1 基本函数

    void spin_lock(spinlock_t *lock);
    最基本得自旋锁函数,它不失效本地中断。

    void spin_lock_irqsave(spinlock_t *lock, unsigned long flags);
    在获得自旋锁之前禁用硬中断(只在本地处理器上),而先前的中断状态保存在flags中。

    void spin_lock_irq(spinlock_t *lock);
    在获得自旋锁之前禁用硬中断(只在本地处理器上),不保存中断状态。

    void spin_lock_bh(spinlock_t *lock);
    在获得锁前禁用软中断,保持硬中断打开状态。

 3.2 函数定义

    自旋锁的实现接口定义在\include\linux\spinlock.h

    自旋锁的结构体 spinlock_t

  spinlock_t()
typedef struct {
	/**
	 * 该字段表示自旋锁的状态,值为1表示未加锁,任何负数和0都表示加锁
	 */
	volatile unsigned int slock;
#ifdef CONFIG_DEBUG_SPINLOCK
	unsigned magic;
#endif
#ifdef CONFIG_PREEMPT
	/**
	 * 表示进程正在忙等待自旋锁。
	 * 只有内核支持SMP和内核抢占时才使用本标志。
	 */
	unsigned int break_lock;
#endif
} spinlock_t;

    

  spin_lock()
/**
 * 当内核不可抢占时,spin_lock的实现过程。
 */
#define _spin_lock(lock)	\
do { \
	/**
	 * 调用preempt_disable禁用抢占。
	 */
	preempt_disable(); \
	/**
	 * _raw_spin_lock对自旋锁的slock字段执行原子性的测试和设置操作。
	 */
	_raw_spin_lock(lock); \
	__acquire(lock); \
} while(0)

  函数_raw_spin_lock()对自旋锁的SLOCK字段执行原子性的测试和设置操作。

#define _raw_spin_lock(x)		\
	do { \
	 	CHECK_LOCK(x); \
		if ((x)->lock&&(x)->babble) { \
			(x)->babble--; \
			printk("%s:%d: spin_lock(%s:%p) already locked by %s/%d\n", \
					__FILE__,__LINE__, (x)->module, \
					(x), (x)->owner, (x)->oline); \
		} \
		(x)->lock = 1; \
		(x)->owner = __FILE__; \
		(x)->oline = __LINE__; \
	} while (0)

  spin_unlock()
#define _spin_unlock(lock) \
do { \
	_raw_spin_unlock(lock); \
	preempt_enable(); \
	__release(lock); \
} while (0)

函数 _raw_spin_unlock()

static inline void _raw_spin_unlock(spinlock_t *lock)
{
#ifdef CONFIG_DEBUG_SPINLOCK
	BUG_ON(lock->magic != SPINLOCK_MAGIC);
	BUG_ON(!spin_is_locked(lock));
#endif
	__asm__ __volatile__(
		spin_unlock_string
	);
}

  宏函数spin_unlock_string

    在spin_unlock_string中,%0即为锁 - > s 锁,movb指令将锁 - > s 锁定为1,movb指令本身就是原子操作,所以不需要锁总线。

#define spin_unlock_string \
	"movb $1,%0" \
		:"=m" (lock->slock) : : "memory"

    自旋锁在同一时刻至多被一个执行线程持有,所以一个时刻只有一个线程位于临界区内,这就为多处理器机器提供了防止并发访问所需的保护机制。

    在单处理机器上,编译的时候不会加入自旋锁,仅会被当作一个设置内核抢占机制是否被启用的开关。如果禁止内核抢占,那么在编译时自旋锁就会被剔除出内核。
    
    内核提供的禁止中断同时请求锁的接口

  spin_lock_irqsave()

      保存中断的当前状态,并禁止本地中断,然后再去获取指定的锁。

unsigned long __lockfunc _spin_lock_irqsave(spinlock_t *lock)
{
	unsigned long flags;
 
	local_irq_save(flags);
	preempt_disable();
	_raw_spin_lock_flags(lock, flags);
	return flags;
}

  pin_unlock_irqrestore()

      对指定的锁解锁,然后让中断恢复到加锁前的状态。

void __lockfunc _write_unlock_irqrestore(rwlock_t *lock, unsigned long flags)
{
	_raw_write_unlock(lock);
	local_irq_restore(flags);
	preempt_enable();
}

    如果能确定中断在加锁前是激活的,那就不需要在解锁后恢复中断以前的状态。也就可以无条件地在解锁时激活中断。这时可以使用spin_lock_irq()和spin_unlock_irq()。

  spin_lock_irq()

      禁止本地中断并获取指定的锁。

void __lockfunc _read_lock_irq(rwlock_t *lock)
{
	local_irq_disable();
	preempt_disable();
	_raw_read_lock(lock);
}

  spin_unlock_irq()

      释放指定的锁,并激活本地中断。

void __lockfunc _spin_unlock_irq(spinlock_t *lock)
{
	_raw_spin_unlock(lock);
	local_irq_enable();
	preempt_enable();
}

    在使用spin_lock_irq()方法时,需要确定中断原来是否处于激活状态。一般不建议使用。
  

  spin_lock_init()

      动态初始化指定的spinlock_t,(此时只有一个指向spinlock_t类型地指针,没有它的实体)

#define spin_lock_init(lock)	do { (void)(lock); } while(0)

  spin_try_lock()

      试图获的某个特定的自旋锁。如果该锁已经被争用,那么该函数立即返回一个非0值,而不会自旋等待锁被释放; 如果成功地获得了这个自旋锁,该函数返回0。

int __lockfunc _spin_trylock(spinlock_t *lock)
{
	preempt_disable();	//使抢占计数加1
	if (_raw_spin_trylock(lock))
		return 1;
	
	preempt_enable();	// 使抢占计数减1,并在thread_info描述符的TIF_NEED_RESCHED标志被置为1的情况下,调用preempt_schedule()
	return 0;
}

  spin_is_locked()

      用于检查特定的锁当前是否已被占用,如果已被占用,返回非0值;否则返回0。

#define spin_is_locked(x) \
	({ \
	 	CHECK_LOCK(x); \
		if ((x)->lock&&(x)->babble) { \
			(x)->babble--; \
			printk("%s:%d: spin_is_locked(%s:%p) already locked by %s/%d\n", \
					__FILE__,__LINE__, (x)->module, \
					(x), (x)->owner, (x)->oline); \
		} \
		0; \
	})

4. 总结

    自旋锁使用总结:
    (1) 一个被争用的自旋锁使得请求它的线程在等待锁重新可用时自旋,会特别浪费处理器时间。所以自旋锁不应该被长时间持有。因此,自旋锁应该使用在:短时间内进行轻量级加锁

    (2)还可以采取另外的方式来处理对锁的争用:让请求线程睡眠,直到锁重新可用时再唤醒它这样处理器不必循环等待,可以执行其他任务。
    但是让请求线程睡眠的处理也会带来一定开销:会有两次上下文切换,被阻塞的线程要换出和换入。所以,自旋持有锁的时间最好小于完成两次上下文的耗时,也就是让持有自旋锁的时间尽可能短。(在抢占式内核中,锁持有等价于系统的调度等待时间),信号量可以在发生争用时,等待的线程能投入睡眠,而不是旋转。

    (3)在单处理机器上,自旋锁是无意义的。因为在编译时不会加入自旋锁,仅仅被当作一个设置内核抢占机制是否被启用的开关。如果禁止内核抢占,那么在编译时自旋锁会被完全剔除出内核。

    (4)Linux内核中,自旋锁是不可递归的。如果试图得到一个你正在持有的锁,你必须去自旋,等待你自己释放这个锁。但这时你处于自旋忙等待中,所以永远不会释放锁,就会造成死锁现象。

    (5)在中断处理程序中,获取锁之前一定要先禁止本地中断(当前处理器的中断),否则,中断程序就会打断正持有锁的内核代码,有可能试图去争用这个已经被持有的自旋锁。这样就会造成双重请求死锁(中断处理程序会自旋,等待该锁重新可用,但锁的持有者在这个处理程序执行完之前是不可能运行的)。

    (6)锁真正保护的是数据(共享数据),而不是代码。
    
1
    
    自旋锁一般这样被使用:

/* 定义一个自旋锁  */
spinlock_t  lock;

 /* 初始化自旋锁 */
spin_lock_init(&lock);

/* 获取自旋锁,保护临界区 */
spin_lock(&lock);
.  .  .  //临界区 

/* 解锁 */
spin_unlock(&lock);

5
6

5. 实例代码

驱动层:
#include <linux/device.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/irq.h>
#include <asm/uaccess.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <mach/gpio.h>
#include <linux/delay.h>
#include <mach/regs-gpio.h>  /*S5PV210_GPH3_BASE*/
 
#define EINT_DEVICE_ID			1
 
#define DRIVER_NAME				"key_eint_race"
#define err(msg) 				printk(KERN_ERR "%s: " msg "\n", DRIVER_NAME)
#define __debug(fmt, arg...)	printk(KERN_DEBUG fmt, ##arg)
 
#define GPH3CON					(unsigned long)(S5PV210_GPH3_BASE+ 0x00)
#define GPH3DAT					(unsigned long)(S5PV210_GPH3_BASE + 0x04)
#define GPH2UP					(unsigned long)(S5PV210_GPH2_BASE + 0x08)
 
static int major = 0;		/* Driver Major Number */
static int minor = 0;		/* Driver Minor Number */
struct class *key_class;
static struct device *key_device;
 
static unsigned int key;
 
static spinlock_t my_spin_lock; //
 
static unsigned int deal_key_value(unsigned int data)
{
	key = data;
	udelay(1000);
	return key;
}
 
static unsigned int deal_key_value_excl(unsigned int data)
{
	unsigned int value;
	unsigned long flag;
	/*
	*	自旋锁解决了SMP多处理的并发问题,对于内核的抢占也可以起到关闭的作用,
	*   可仍没有解决中断产生的并发问题,但是不用担心,我们可以采用spin_lock
	*   的变体spin_lock_irqsave函数完成同时关中断的功能
	*
	*   注意:此处用自旋锁并不合理,因为我们的临界区udelay了1ms(时间很长),
	*   但我们本例子的目的是验证自旋锁的作用,在实际编程中要特别注意
	*/
	spin_lock_irqsave(&my_spin_lock,flag);		//获取自旋锁
	value =deal_key_value(data);
	spin_unlock_irqrestore(&my_spin_lock,flag);		//释放自旋锁
	
	return value;
}
 
irqreturn_t buttons_interrupt(int irq, void *dev_id)
{	
	deal_key_value_excl((unsigned int)dev_id);
	//__debug("in eint function...\n");
	return IRQ_HANDLED;
}
 
static void key_io_port_init(void)
{
	unsigned long reg_val;
	
	reg_val = readl(GPH3CON);
	reg_val &= ~((0x0f<<0) | (0x0f<<4));
	reg_val |= ((0x01<<0) | (0x01<<4));
	writel(reg_val, GPH3CON);
 
	reg_val = readl(GPH3DAT);
	reg_val &= ~((0x01<<0) | (0x01<<1));
	writel(reg_val, GPH3DAT);
 
	reg_val = readl(GPH2UP);
	reg_val &= ~(0x03<<8);
	reg_val |= 0x02<<8;
	writel(reg_val, GPH2UP);
}
 
static ssize_t key_read(struct file *filp, char *buf, size_t count, loff_t *f_pos)
{
	int key_num;
	int cpy_len;
	int retval;
	
	key_num = deal_key_value_excl(current->pid);
	
	cpy_len = min(sizeof(key_num), count);
	retval = copy_to_user(buf, &key_num, cpy_len);
	
	return (cpy_len - retval);
}
 
/* Driver Operation structure */
static struct file_operations key_fops = {
	.owner = THIS_MODULE,
	.read = key_read,
};
 
 
static int __init key_eint_init(void)
{
	int retval;
	
	key_io_port_init();
	
	spin_lock_init(&my_spin_lock);
 
	
	//__debug("in key_eint_init\n");
	
	retval = set_irq_type(IRQ_EINT(20),IRQ_TYPE_EDGE_FALLING);
	if(retval){
		err("IRQ_EINT20 set irq type failed");
		goto error;
	}
	
	retval = request_irq(IRQ_EINT(20), buttons_interrupt, IRQF_DISABLED, 
			"KEY1", (void *)EINT_DEVICE_ID);
	if(retval){
		err("request eint20 failed");
		goto error;
	}
	
	/* Driver register */
	major = register_chrdev(major, DRIVER_NAME, &key_fops);
	if(major < 0){
		err("register char device fail");
		retval = major;
		goto error_register;
	}
	key_class=class_create(THIS_MODULE,DRIVER_NAME);
	if(IS_ERR(key_class)){
		err("class create failed!");
		retval =  PTR_ERR(key_class);
		goto error_class;
	}
	key_device=device_create(key_class,NULL, MKDEV(major, minor), NULL,DRIVER_NAME);
	if(IS_ERR(key_device)){
		err("device create failed!");
		retval = PTR_ERR(key_device);
		goto error_device;
	}
	__debug("register myDriver OK! Major = %d\n", major);
	return 0;
 
error_device:
	class_destroy(key_class);
error_class:
	unregister_chrdev(major, DRIVER_NAME);
error_register:
	free_irq(IRQ_EINT(20), (void *)EINT_DEVICE_ID);
error:
	return retval;
}
 
static void __exit key_eint_exit(void)
{
	//__debug("in key_eint_exit\n");
	
	free_irq(IRQ_EINT(20), (void *)EINT_DEVICE_ID);
 
	unregister_chrdev(major, DRIVER_NAME);
	device_destroy(key_class,MKDEV(major, minor));
	class_destroy(key_class);
 
	return;
}
 
module_init(key_eint_init);
module_exit(key_eint_exit);
 
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Eric");

应用层:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
 
/*Linux内核是抢占式内核,在一个系统调用未结束前,
另一个系统调用也可以进入进程上下文,访问同一缓冲区*/
int main(void)
{	
	int status;
	pid_t pid;
	
	//打开文件raceStation
	int fd_driver;
	if((fd_driver = open("/dev/key_eint_race", O_RDWR)) < 0){
		printf("file open error\n");
		exit(1);
	}
	//创建子进程
 	if((pid = fork()) < 0){
		perror("fork:");
		exit(1);
	}
	else if(pid == 0){		//判断如果是子进程
		int num;
		while(1){
			read(fd_driver,&num,sizeof(num));	
			printf("the num value is <son-%d>: %d\n",getpid(), num);
 
		//	usleep(50*1000);
		}
		close(fd_driver);
	}else{				//判断如果是父进程
		int num;
		while(1){	
			read(fd_driver,&num,sizeof(num));	
			printf("the num value is <father-%d>: %d\n",getpid(), num);
 
		//	usleep(50*1000);
		}
		pid = wait(&status);
		close(fd_driver);
	}
}

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【Linux内核中的并发控制】- 自旋锁 的相关文章

  • 无法加载 JavaHL 库。- linux/eclipse

    在尝试安装 Subversion 插件时 当 Eclipse 启动时出现此错误 Failed to load JavaHL Library These are the errors that were encountered no libs
  • 强制卸载 NFS 安装目录 [关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 Locked 这个问题及其答案是locked help locked posts因为这个问题是题外话 但却具有历史意义 目前不接受新的答案
  • 如何检测并找出程序是否陷入死锁?

    这是一道面试题 如何检测并确定程序是否陷入死锁 是否有一些工具可用于在 Linux Unix 系统上执行此操作 我的想法 如果程序没有任何进展并且其状态为运行 则为死锁 但是 其他原因也可能导致此问题 开源工具有valgrind halgr
  • 应用程序无缘无故地被杀死。怀疑 BSS 高。如何调试呢?

    我已经在CentOs6 6中成功运行我的应用程序 最近 硬件 主板和内存 更新了 我的应用程序现在毫无理由地被杀死 root localhost PktBlaster PktBlaster Killed 文件和 ldd 输出 root lo
  • 如何在 Linux 中编写文本模式 GUI? [关闭]

    Closed 这个问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 当我编写脚本 程序时 我经常想弹出一个简单的文本 gui 来提示输入 我该怎么做 例如 来自 Shel
  • 如何在bash中使用jq从变量中包含的json中提取值

    我正在编写一个 bash 脚本 其中存储了一个 json 值 现在我想使用 Jq 提取该 json 中的值 使用的代码是 json val code lyz1To6ZTWClDHSiaeXyxg redirect to http examp
  • Linux中的CONFIG_OF是什么?

    我看到它在很多地方被广泛使用 但不明白在什么场景下我需要使用它 What is 配置 OF OF 的全名是什么 打开固件 这是很久以前发明的 当时苹果公司正在生产基于 PowerPC CPU 的笔记本电脑 而 Sun Microsystem
  • 从 Python 调用 PARI/GP

    我想打电话PARI GP http pari math u bordeaux fr dochtml gpman html仅从Python计算函数nextprime n 对于不同的n是我定义的 不幸的是我无法得到帕里蟒蛇 http code
  • Linux中的定时器类

    我需要一个计时器来以相对较低的分辨率执行回调 在 Linux 中实现此类 C 计时器类的最佳方法是什么 有我可以使用的库吗 如果您在框架 Glib Qt Wx 内编写 那么您已经拥有一个具有定时回调功能的事件循环 我认为情况并非如此 如果您
  • 在哪里可以找到并安装 pygame 的依赖项?

    我对 Linux 比较陌生 正在尝试安装 python 的 pygame 开发环境 当我运行 setup py 时 它说我需要安装以下依赖项 我找到并安装了其中之一 SDL 然而 其他人则更加难以捉摸 Hunting dependencie
  • arm64和armhf有什么区别?

    Raspberry Pi Type 3 具有 64 位 CPU 但其架构不是arm64 but armhf 有什么区别arm64 and armhf armhf代表 arm hard float 是给定的名称Debian 端口 https
  • 尝试安装 LESS 时出现“请尝试以 root/管理员身份再次运行此命令”错误

    我正在尝试在我的计算机上安装 LESS 并且已经安装了节点 但是 当我输入 node install g less 时 出现以下错误 并且不知道该怎么办 FPaulMAC bin paul npm install g less npm ER
  • Pyaudio 安装错误 - “命令‘gcc’失败,退出状态 1”

    我正在运行 Ubuntu 11 04 Python 2 7 1 并想安装 Pyaudio 于是我跑了 sudo easy install pyaudio 在终端中 进程退出并显示以下错误消息 Searching for pyaudio Re
  • 如何在Linux内核源代码中打印IP地址或MAC地址

    我必须通过修改 Linux 内核源代码来稍微改变 TCP 拥塞控制算法 但为了检查结果是否正确 我需要记录 MAC 或 IP 地址信息 我使用 PRINTK 函数来打印内核消息 但我感觉很难打印出主机的MAC IP地址 printk pM
  • PHP 从命令行启动 gui 程序,但 apache 不启动

    首先 我阅读了有类似问题的人的一些帖子 但所有答案都没有超出导出 DISPLAY 0 0 和 xauth cookies 这是我的问题 提前感谢您的宝贵时间 我开发了一个小库 它使用 OpenGL 和 GLSL 渲染货架 过去几天我将它包装
  • NPTL 和 POSIX 线程有什么区别?

    NPTL 和 POSIX 线程之间的基本区别是什么 这两者是如何演变的 POSIX 线程 pthread 不是一个实现 它是几个函数的 API 规范 纸上的标准 英文 其名称以pthread 以及定义在
  • Linux 中什么处理 ping?

    我想覆盖 更改 linux 处理 ping icmp echo 请求数据包的方式 这意味着我想运行自己的服务器来回复传入的 icmp 回显请求或其他 数据包 但为了使其正常工作 我想我需要禁用 Linux 的默认 ping icmp 数据包
  • ubuntu:升级软件(cmake)-版本消歧(本地编译)[关闭]

    Closed 这个问题是无关 help closed questions 目前不接受答案 我的机器上安装了 cmake 2 8 0 来自 ubuntu 软件包 二进制文件放置在 usr bin cmake 中 我需要将 cmake 版本至少
  • 查找哪些页面不再与写入时复制共享

    假设我在 Linux 中有一个进程 我从中fork 另一个相同的过程 后forking 因为原始进程将开始写入内存 Linux写时复制机制将为进程提供与分叉进程使用的不同的唯一物理内存页 在执行的某个时刻 我如何知道原始进程的哪些页面已被写
  • ftrace:仅打印trace_printk()的输出

    是否可以只转储trace printk 输出于trace文件 我的意思是过滤掉函数跟踪器 或任何其他跟踪器 中的所有函数 一般来说 您可以在选项目录中关闭选项 sys kernel debug tracing options Use ls显

随机推荐

  • 记录一下mac mini 2018 的折腾过程

    更新 昨天手贱在外置雷电SSD中安装了苹果内置SSD的驱动 随即就造成了外置显卡的挂载不上 又折腾了大概三个小时 随后想到了应该是操作系统内部资源竞争 造成不挂载外置显卡 随后删除了内置SSD的驱动 然后才挂载外置显卡成功 还有一点就是 外
  • SendMessage()窗体之间发消息

    SendMessage调用一个窗口的窗口函数 将一条消息发给那个窗口 一 父窗口向子窗口发消息 1 接收方 1 1头文件里面声明消息 define MSG UPDATE LEFT CHILD WM USER 600 1 2消息映射 在头文件
  • halcon基本图像操作

    halcon基本图像操作 阈值分割 取某一个阈值下的某一个区域 获取中心点位置 形态学 膨胀 腐蚀 开运算 闭运算 综合使用 开运算和检测轮廓 字符识别 资源路径 F halcon halconStudy 阈值分割 灰度值 读取图像 转灰度
  • 在线接口测试工具(神器)

    前方高能 请注意 想必大家都用过POSTMAN 进行接口的测试吧 那么接下来我告诉你 你用了这个工具以后 你就不会再想去用POSTMAN了 话不多说了 直接上代码吧 还是那句话 我会尽可能详细的去演示操作过程 避免大家走弯路 123456
  • MySQL阅读网上MySQL文章有感的杂记

    前言 本篇文章将会记录各大MySQL文章的一些有意思的内容摘取 以及一些问题的提问 并且持续更新 并且MySQL专栏将会记录MySQL常考的场景题等实战 问题归类 1 MySQL从加锁范围上分为哪三类 2 全局锁加锁方法的执行命令是什么 主
  • Openwrt的uci接口

    UCI是Unified Configuration Interface的缩写 翻译成中文就是统一配置接口 用途就是为OpenWrt提供一个集中控制的接口 OpenWrt实现的这个工具 能够让你的不管是Lua还是PHP程序 或者SHELL程序
  • 无网络环境,如何部署Docker镜像

    一 简介 无网络环境 部署 Docker 镜像 这通常适用于一些部署环境是脱离网络的公司 或者公司内部有着严格的网络安全要求 且还是 Docker 部署的程序 这个时候怎么办 别急今天就来讲讲 无网络环境 如何部署 Docker 镜像 二
  • awk命令的使用

    1 获取根分区剩余大小 先用df h命令查看磁盘 确定我们需要获取字段的位置 再使用awk命令获取此字段 df h df h awk NR 6 print 4 2 获取当前机器ip地址 ifconfig awk NR 2 print 2 3
  • 终止for循环的方式

    continue break return 1 continue 当程序运行到 continue 语句时 会终止当前的这一次循环 进入下一次的循环中 它 适用于所有的循环结构 for int i 0 i lt 10 i 执行内容 conti
  • stm32F103C8T6 keil5编译完成使用XCOM进行串口打印时乱码

    一 检查波特率 串口调试工具和main c的串口初始化一定要相同 二 检查编码格式 第一步 点击keil5的小扳手图标 修改为Chinese GB2312 Simplified 这样一来 代码的中文就可以显示出来啦 第二步 XCOM这款串口
  • 安装Visio 2013与原本的office冲突的最终解决方案

    一 下载office visio 2013 二 开始安装 但是提示卸载原本的office 三 网上找寻答案 于是按照这篇文章https jingyan baidu com article 19192ad8c1d6dae53e570735 h
  • HashMap常用API及注意事项

    map clear map size map isEmpty map containsKey 判断 map containsValue map get key map put key value map putAll otherMap ma
  • ubuntu18.04安装cmake3.18.0

    ubuntu18 04安装cmake3 18 0 1 本方法可适用安装任何版本的cmake 可以在官网中找到需要的版本 本文以3 18 0为例 https cmake org files 2 wget https cmake org fil
  • Javaweb和微信小程序项目部署阿里云服务器总结(上)

    谈到微信小程序的java后台怎么部署在阿里云服务器上的问题 弯弯绕绕 好多坑 网上的博客资料也特别乱 博主也是在没有任何经验和指导下花了几天的工夫才完成的 这里为了方便大家不踩坑 总结了下整个流程和注意事项 由于篇幅原因 只讲重点的地方 所
  • 软件测试(3)——白盒测试

    文章目录 白盒测试 白盒测试方法 静态测试 人工代码检查 软件度量 其它方法 动态测试 覆盖测试分析 运行时错误检测 覆盖测试 逻辑覆盖方法 路径测试 数据流测试 白盒测试 白盒测试也称结构性测试 逻辑驱动测试 基于程序的测试 特点 将程序
  • Android App开机自启动

    最近项目中 有用到开机自启动的功能 这里做一下总结 供大家学习探讨 Android 开机启动延迟问题 Android 开机自启动被拦截问题 实战演练 测试手机 红米手机 Redmi 6A 安卓version 9 华为手机 DUA AL00
  • vs2010 vs2013等vs中如何统计整个项目的代码行数

    vs2010 vs2013等vs中如何统计整个项目的代码行数 在一个大工程中有很多的源文件和头文件 我如何快速统计总行数 解决方案 b b b b ctrl shift F 查找选项选 正则表达式 具体步骤 1 鼠标停靠在你的项目解决方案附
  • ERRORS: auth.User.groups: (fields.E304) Reverse accessor for ‘User.groups‘ clashes with reverse acce

    写博客网站后台 设计数据库结构时 博客 Article 表中定义了一个作者外键 author models ForeignKey settings AUTH USER MODEL verbose name 作者 这个外键是网站注册用户 这样
  • Oracle数据库获取uuid函数

    Oracle新建系统表时 要求主键为32位uuid 猜测Oracle肯定会提供相关的函数 翻阅相关文档 果然发现Oracle提供的函数 sys guid 用于获取32位uuid 简单使用为 select sys guid from dual
  • 【Linux内核中的并发控制】- 自旋锁

    在内核中会经常看到spin lock 自旋锁 它到底是个神马东西 在驱动相关的书籍和论坛中查阅了不少资料 看的也是云里雾里 现在将知识罗列总结一下 便于日后回顾 1 自旋锁定义 在Linux内核并发控制中最常见的锁就是自旋锁 自旋锁最多只能