Linux_kernel内核定时器14

news2024/12/24 17:03:26

一、内核定时器

        1、内核定时器

使用方法:

        2、系统时钟中断处理函数

        1)更新时间

        2)检查当前时间片是否耗尽

                Linux操作系统是基于时间片轮询的,属于抢占式的内核

        3)jiffies++

        3、基本概念

        1)HZ

                HZ决定了1秒钟产生系统时钟中断的次数

        2)tick

                tick时钟滴答数

                tick = 1 / HZ

                记录了两次系统时钟中断之间的时间间隔

        3)jiffies

                记录了自开机以来,系统时钟中断产生的次数

        4、内核定时器

        1)timer_list

        2)操作系统定时器

                【1】定义变量

struct timer_list led_timer;

                【2】初始化

init_timer(timer)

                【3】启动定时器

void add_timer(struct timer_list *timer)

int mod_timer(struct timer_list *timer, unsigned long expires)

                【4】停止定时器

int del_timer(struct timer_list *timer)

        3)实验
           【1】进入工程目录

        cd /home/zjd/s5p6818/KERNEL/drivers

        【2】创建新的工程

        mkdir timer_led

        【3】编写程序

        vim timer_led.c

#include <linux/init.h>
#include <linux/module.h>
#include <mach/platform.h>
#include <linux/gpio.h>
#include <linux/timer.h>

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Zjd");

// step_2 : define the gpio_b26
#define LED0 (PAD_GPIO_B + 26)
// step_5 : define the global var
struct timer_list led_timer;

// step_7 : implement the fun when the time is over
void timer_func(unsigned long data)
{
	// step_8 : set the LED0 value
	gpio_set_value(LED0, data);
	// step_9 : reverse the LED0
	led_timer.data = !data;
	// step_10 : begin count timer, second is 1s
	mod_timer(&led_timer, jiffies + 1 * HZ);
}

int __init timer_led_init(void)
{
	// step_1 : request the gpio
	gpio_request(LED0, "led0");

	// step_3 : set output mode, default value is 1, the turn_off led
	gpio_direction_output(LED0, 1);

	// step_4 : initialize the timer
	init_timer(&led_timer);
	// set the intervalue timer
	led_timer.expires = jiffies + 1 * HZ;
	// set the fun when the timer over
	led_timer.function = timer_func;
	// set the param of timer_func
	led_timer.data = 0;
	// step_6 : turn on the timer
	add_timer(&led_timer);

	return 0;
}

void __exit timer_led_exit(void)
{
	// step_11 : stop the timer
	del_timer(&led_timer);
	// step_12 : destory the gpio
	gpio_free(LED0);

	return ;
}

module_init(timer_led_init);
module_exit(timer_led_exit);

        【4】编写Makefile

        vim Makefile

obj-m += timer_led.o
KERNEL_PATH=/home/zjd/s5p6818/KERNEL/kernel
ROOTFS_PATH=/nfs_share/rootfs

all:
	make -C $(KERNEL_PATH) M=$(PWD) modules
	cp *.ko $(ROOTFS_PATH)

clean:
	make -C $(KERNEL_PATH) M=$(PWD) clean
        【5】编译工程

        make

        【6】下位机测试

        telnet 192.168.1.6

        insmod timer_led.ko

        【7】流水灯

        vim water_led.c

#include <linux/init.h>
#include <linux/module.h>
#include <linux/gpio.h>
#include <linux/timer.h>
#include <mach/platform.h>

MODULE_LICENSE("GPL");
MODULE_AUTHOR("zjd");

#define LED0 (PAD_GPIO_B + 26)
#define LED1 (PAD_GPIO_C + 11)
#define LED2 (PAD_GPIO_C + 7)
#define LED3 (PAD_GPIO_C + 12)

struct timer_list led_timer;
unsigned int led[] = {LED0, LED1, LED2, LED3};

void led_function(unsigned long data)
{
	static int pre = 0;
	static int next = 0;
	led_timer.data = !data;
	gpio_set_value(led[pre], led_timer.data);
	next = (pre + 1) % (sizeof(led) / sizeof(led[0]));
	led_timer.data = !led_timer.data;
	gpio_set_value(led[next], led_timer.data);
	pre = next;
	mod_timer(&led_timer, jiffies + HZ / 2);
}

int __init led_drv_init(void)
{
	gpio_request(LED0, "led0");
	gpio_request(LED1, "led1");
	gpio_request(LED2, "led2");
	gpio_request(LED3, "led3");

	gpio_direction_output(LED0, 1);
	gpio_direction_output(LED1, 1);
	gpio_direction_output(LED2, 1);
	gpio_direction_output(LED3, 1);

	init_timer(&led_timer);

	led_timer.expires = jiffies + HZ / 2;

	led_timer.function = led_function;

	led_timer.data = 0;

	add_timer(&led_timer);

	return 0;
}

void __exit led_drv_exit(void)
{
	del_timer(&led_timer);
	
	gpio_free(LED0);
	gpio_free(LED1);
	gpio_free(LED2);
	gpio_free(LED3);

	return ;
}

module_init(led_drv_init);
module_exit(led_drv_exit);

二、内核的竞态与并发

        1、介绍

        PC机上的串口设备是一种独占式访问的设备(只能有一个进程进行访问),独占式操作的实现是在串口的驱动程序中实现的,我们希望按键设备只允许一个进程进行访问。

        2、基础实验

          【1】进入工程目录

        cd /home/zjd/s5p6818/KERNEL/drivers

        【2】创建新的工程

        mkdir single_btn

        【3】编写程序

        vim single_btn.c

#include <linux/init.h>
#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/device.h>


MODULE_LICENSE("GPL");

//[2]定义全局变量----设备号
dev_t dev;
//[4]定义全局变量----cdev对象
struct cdev btn_cdev;
//[7]定义全局指针----class类
struct class *btn_cls = NULL;

//[d]按照原型实现btn_open
int btn_open(struct inode *inode, struct file *fp)
{
    printk("<0>" "enter : %s\n", __func__);

    return 0;
}

//[e]按照原型实现btn_close
int btn_close(struct inode *inode, struct file *fp)
{
    printk("<0>" "enter : %s\n", __func__);

    return 0;
}

//[c]定义全局变量---操作函数集合
struct file_operations btn_fops = {
    .owner = THIS_MODULE,
    .open = btn_open,
    .release = btn_close
};


int __init btn_drv_init(void)
{
    //[1]申请设备号
    alloc_chrdev_region(&dev, 0, 1, "btn");

    //[3]初始化 cdev 对象
    cdev_init(&btn_cdev, &btn_fops);

    //[5]注册 cdev 对象到内核中
    cdev_add(&btn_cdev, dev, 1);

    //[6]自动创建设备文件(创建树枝)
    btn_cls = class_create(THIS_MODULE, "mybuttons");
    //(创建果实)
    device_create(btn_cls, NULL, dev, NULL, "buttons");

    return 0;
}

void __exit btn_drv_exit(void)
{
    //[8]销毁设备文件(销毁果实)
    device_destroy(btn_cls, dev);

    //[9]销毁设备文件依赖的设备树(销毁树枝)
    class_destroy(btn_cls);

    //[a]注销 cdev 对象
    cdev_del(&btn_cdev);

    //[b]释放设备号
    unregister_chrdev_region(dev, 1);

    return ;
}

module_init(btn_drv_init);
module_exit(btn_drv_exit);
        【4】编写Makefile

        vim Makefile

obj-m += signal_btn.o
KERNEL_PATH=/home/zjd/s5p6818/KERNEL/kernel
ROOTFS_PATH=/nfs_share/_install

all:
	make -C $(KERNEL_PATH) M=$(PWD) modules
	cp *.ko $(ROOTFS_PATH)

clean:
	make -C $(KERNEL_PATH) M=$(PWD) clean
        【5】编译工程

        make

        【6】编写应用层程序

        mkdir test

        cd test

        vim btn_test.c

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

int main(void)
{
    int fd = open("/dev/buttons", O_RDONLY);
    if(fd < 0)
    {
        perror("open()");
        return -1;
    }
    
    printf("open success!\n");
    
    while (1) {
        ;
    }
    
    close(fd);
    
    return 0;
}

     vim Makefile

SRC=btn_test.c
OBJ=btn_test

ARM_COMPILE=arm-cortex_a9-linux-gnueabi-
GCC=gcc

ROOTFS_PATH=/nfs_share/_install

all:
	$(ARM_COMPILE)$(GCC) $(SRC) -o $(OBJ)
	cp $(OBJ) $(ROOTFS_PATH)

clean:
	rm -rf $(OBJ)
        【7】编译工程

        make

        【8】下位机测试

        insmod single_btn.ko

        ./btn_test

        telnet 192.168.1.6

        ./btn_test

        【9】总结

        我们发现,这两个进程都可以打开这个设备文件,这样与我们的需求不符,我们希望在同一时刻,只能有一个进程访问这个设备文件。

        两个进程属于竞争关系,都来抢设备文件这个资源,如何做到同一时刻只能一个进程对设备文件进行访问?(竞态)

        3、全局变量

        vim single_btn.c

#include <linux/init.h>
#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/device.h>

#define ON	1
#define OFF	0

MODULE_LICENSE("GPL");

//[2]定义全局变量----设备号
dev_t dev;
//[4]定义全局变量----cdev对象
struct cdev btn_cdev;
//[7]定义全局指针----class类
struct class *btn_cls = NULL;

int  btn_switch = OFF;

//[d]按照原型实现btn_open
int btn_open(struct inode *inode, struct file *fp)
{
    printk("<0>" "enter : %s\n", __func__);
	if (OFF == btn_switch)
		btn_switch = ON;
	else
		return -EAGAIN;

    return 0;
}

//[e]按照原型实现btn_close
int btn_close(struct inode *inode, struct file *fp)
{
    printk("<0>" "enter : %s\n", __func__);
	if (ON == btn_switch)
		btn_switch = OFF;
	else
		return -EAGAIN;

    return 0;
}

//[c]定义全局变量---操作函数集合
struct file_operations btn_fops = {
    .owner = THIS_MODULE,
    .open = btn_open,
    .release = btn_close
};


int __init btn_drv_init(void)
{
    //[1]申请设备号
    alloc_chrdev_region(&dev, 0, 1, "btn");

    //[3]初始化 cdev 对象
    cdev_init(&btn_cdev, &btn_fops);

    //[5]注册 cdev 对象到内核中
    cdev_add(&btn_cdev, dev, 1);

    //[6]自动创建设备文件(创建树枝)
    btn_cls = class_create(THIS_MODULE, "mybuttons");
    //(创建果实)
    device_create(btn_cls, NULL, dev, NULL, "buttons");

    return 0;
}

void __exit btn_drv_exit(void)
{
    //[8]销毁设备文件(销毁果实)
    device_destroy(btn_cls, dev);

    //[9]销毁设备文件依赖的设备树(销毁树枝)
    class_destroy(btn_cls);

    //[a]注销 cdev 对象
    cdev_del(&btn_cdev);

    //[b]释放设备号
    unregister_chrdev_region(dev, 1);

    return ;
}

module_init(btn_drv_init);
module_exit(btn_drv_exit);

注意:

        由于cpu调度策略的不确定型,这种使用全局变量的方法解决竞态是不靠谱的。

        4、竞态

                1)简介

        竞态就是竞争的状态,竞争的是共享资源,在硬件设备的角度说,所谓的共享资源指的是:UART、LCD、声卡、网卡

                2)临界区

        访问共享资源的代码就是临界区

                3)产生竞态的原因

        【1】多核处理器

        【2】多任务之间的抢占

        【3】中断和任务之间的抢占

        【4】中断和中断之间的抢占(中断优先级,优先级编号越小,优先级越高)

                4)解决竞态的策略

        【1】中断屏蔽

        【2】原子操作

                位原子操作

                整型原子操作

        【3】自旋锁

        【4】信号量

                5)中断屏蔽

可以解决【2】【3】【4】产生竞态的原因

        【1】中断操作

local_irq_enable()        // 中断使能

local_irq_disable()        // 中断失能

        【2】执行代码

        在中断失能和中断使能之间执行临界区的资源

注意:

        关中断的时间要特别的短,一但过长,内核将直接崩溃,因为Linux内核中有很多重要的机制是靠中断实现的。写驱动时,不建议用这种方式。

                6)原子操作

        原子操作是不可分割的,要么都执行,要么不执行。在执行完毕之前不会被任何其他的任务或事件中断。

        【1】位原子操作

        static inline void set_bit(int nr, volatile unsigned long *addr)

        static inline void clear_bit(int nr, volatile unsigned long *addr)

        static inline int test_and_set_bit(int nr, volatile unsigned long *addr)

        static inline int test_and_clear_bit(int nr, volatile unsigned long *addr)

        static inline void change_bit(int nr, volatile unsigned long *addr)

        【2】整型原子操作

atomic_t        // 整型原子结构体

static inline void atomic_add(int i, atomic_t *v)        // 给整型原子变量 + i

static inline void atomic_sub(int i, atomic_t *v)        // 给整型原子变量 - i

static inline void atomic_inc(atomic_t *v)        // 给整型原子变量 + 1

static inline void atomic_dec(atomic_t *v)        // 给整型原子变量 - 1

        【3】实验

        vim single_btn.c

#include <linux/init.h>
#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/device.h>

#define ON	1
#define OFF	0

MODULE_LICENSE("GPL");

//[2]定义全局变量----设备号
dev_t dev;
//[4]定义全局变量----cdev对象
struct cdev btn_cdev;
//[7]定义全局指针----class类
struct class *btn_cls = NULL;

// define global variable
//int  btn_switch = OFF;
// define int atomic_t variable
atomic_t btn_tv;

//[d]按照原型实现btn_open
int btn_open(struct inode *inode, struct file *fp)
{
    printk("<0>" "enter : %s\n", __func__);
#if 0
	if (OFF == btn_switch)
		btn_switch = ON;
	else
		return -EAGAIN;
#else
	if (!atomic_dec_and_test(&btn_tv)) {
		atomic_inc(&btn_tv);
		return -EAGAIN;
	}
#endif
    return 0;
}

//[e]按照原型实现btn_close
int btn_close(struct inode *inode, struct file *fp)
{
    printk("<0>" "enter : %s\n", __func__);
#if 0
	if (ON == btn_switch)
		btn_switch = OFF;
	else
		return -EAGAIN;
#else
	atomic_inc(&btn_tv);
#endif
    return 0;
}

//[c]定义全局变量---操作函数集合
struct file_operations btn_fops = {
    .owner = THIS_MODULE,
    .open = btn_open,
    .release = btn_close
};


int __init btn_drv_init(void)
{
    //[1]申请设备号
    alloc_chrdev_region(&dev, 0, 1, "btn");

    //[3]初始化 cdev 对象
    cdev_init(&btn_cdev, &btn_fops);

    //[5]注册 cdev 对象到内核中
    cdev_add(&btn_cdev, dev, 1);

    //[6]自动创建设备文件(创建树枝)
    btn_cls = class_create(THIS_MODULE, "mybuttons");
    //(创建果实)
    device_create(btn_cls, NULL, dev, NULL, "buttons");

	// initialize int atomic_t variable
	atomic_set(&btn_tv, 1);

    return 0;
}

void __exit btn_drv_exit(void)
{
    //[8]销毁设备文件(销毁果实)
    device_destroy(btn_cls, dev);

    //[9]销毁设备文件依赖的设备树(销毁树枝)
    class_destroy(btn_cls);

    //[a]注销 cdev 对象
    cdev_del(&btn_cdev);

    //[b]释放设备号
    unregister_chrdev_region(dev, 1);

    return ;
}

module_init(btn_drv_init);
module_exit(btn_drv_exit);

当上位机对btn设备文件访问完毕时

注意:

        由于对整型变量的操作都是原子化的,所以这种方案是可行的

        7)自旋锁

        【1】核心数据结构

spinlock_t

        【2】使用方法

                1】获取自旋锁

                2】执行临界区代码,访问共享资源

                3】释放自旋锁

        【3】特点

                1】自旋锁只有一个持有单元

                2】若试图获取一个已经被其它执行单元持有的自旋锁,将会阻塞等待,直到原持有方释放自旋锁。

        【4】使用步骤

                 【1】定义变量

spinlock_t btn_lock;

                【2】初始化

spin_lock_init(_lock)

                【3】获取锁

static inline void spin_lock(spinlock_t *lock)

                【4】释放自旋锁

static inline void spin_unlock(spinlock_t *lock)

                【5】实验

        vim single_btn.c

#include <linux/init.h>
#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/device.h>

#define ON	1
#define OFF	0

// controller the different ways though to define value
//#define ATOMIC
#define SPINLOCK

MODULE_LICENSE("GPL");

//[2]定义全局变量----设备号
dev_t dev;
//[4]定义全局变量----cdev对象
struct cdev btn_cdev;
//[7]定义全局指针----class类
struct class *btn_cls = NULL;

#if defined(ATOMC)
// define int atomic_t variable
atomic_t btn_tv;
#elif defined(SPINLOCK)
// define global variable the sources of compare
int  btn_switch = OFF;
// define the reverse lock
struct spinlock btn_lock;
#endif

//[d]按照原型实现btn_open
int btn_open(struct inode *inode, struct file *fp)
{
    printk("<0>" "enter : %s\n", __func__);
#if defined(SPINLOCK)
	if (OFF == btn_switch) {
		spin_lock(&btn_lock);
		btn_switch = ON;
		spin_unlock(&btn_lock);
	}
	else
		return -EAGAIN;

#elif defined(ATOMIC)
	if (!atomic_dec_and_test(&btn_tv)) {
		atomic_inc(&btn_tv);
		return -EAGAIN;
	}
#endif
    return 0;
}

//[e]按照原型实现btn_close
int btn_close(struct inode *inode, struct file *fp)
{
	printk("<0>" "enter : %s\n", __func__);
#if defined(SPINLOCK)
	spin_lock(&btn_lock);
	btn_switch = OFF;
	spin_unlock(&btn_lock);
#elif defined(ATOMIC)
	atomic_inc(&btn_tv);
#endif
    return 0;
}

//[c]定义全局变量---操作函数集合
struct file_operations btn_fops = {
    .owner = THIS_MODULE,
    .open = btn_open,
    .release = btn_close
};


int __init btn_drv_init(void)
{
    //[1]申请设备号
    alloc_chrdev_region(&dev, 0, 1, "btn");

    //[3]初始化 cdev 对象
    cdev_init(&btn_cdev, &btn_fops);

    //[5]注册 cdev 对象到内核中
    cdev_add(&btn_cdev, dev, 1);

    //[6]自动创建设备文件(创建树枝)
    btn_cls = class_create(THIS_MODULE, "mybuttons");
    //(创建果实)
    device_create(btn_cls, NULL, dev, NULL, "buttons");
#if defined(ATOMIC)
	// initialize int atomic_t variable
	atomic_set(&btn_tv, 1);
#elif defined(SPINLOCK)
	// initialize reverse lock variable
	spin_lock_init(&btn_lock);
#endif
    return 0;
}

void __exit btn_drv_exit(void)
{
    //[8]销毁设备文件(销毁果实)
    device_destroy(btn_cls, dev);

    //[9]销毁设备文件依赖的设备树(销毁树枝)
    class_destroy(btn_cls);

    //[a]注销 cdev 对象
    cdev_del(&btn_cdev);

    //[b]释放设备号
    unregister_chrdev_region(dev, 1);

    return ;
}

module_init(btn_drv_init);
module_exit(btn_drv_exit);

注意:

        1】持有自旋锁的事件要尽量短,临界区中不应该出现引起阻塞或休眠的函数

                会导致内核吐核

        2】避免死锁情况的发生

                static inline int spin_trylock(spinlock_t *lock)        // 获取不成功,返回错误

        8)信号量

当临界区的代码需要长时间执行时,我们可以使用信号量机制

         【1】核心数据结构

struct semaphore

        【2】使用方法

                1】获取信号量

                2】执行临界区代码,访问共享资源

                3】释放信号量

        【3】特点

                1】信号量可以持有多个单元

                2】获取信号量的本质就是引用计数 - 1(count--

                3】释放信号量的本质就是引用计数 + 1(count++

                4】最小减到0,如果到0了,再去获取信号量会失败,睡眠等待

        【4】使用步骤

                 【1】定义信号量

struct semaphore btn_sem;

                【2】初始化

static inline void sema_init(struct semaphore *sem, int val)

                【3】获取信号量

        1】void down(struct semaphore *sem)

        2int down_interruptible(struct semaphore *sem)

        3】int down_killable(struct semaphore *sem)

                【4】释放信号量

void up(struct semaphore *sem)

                【5】实验

        vim single_btn.c

#include <linux/init.h>
#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/delay.h>

// only one sources
#define SOURCE_NUM	1
#define ON	1
#define OFF	0

// controller the different ways though to define value
//#define ATOMIC
//#define SPINLOCK
#define SEMAPHORE

typedef int source_t;

MODULE_LICENSE("GPL");

//[2]定义全局变量----设备号
dev_t dev;
//[4]定义全局变量----cdev对象
struct cdev btn_cdev;
//[7]定义全局指针----class类
struct class *btn_cls = NULL;

#if defined(ATOMC)
// define int atomic_t variable
atomic_t btn_tv;
#elif defined(SPINLOCK)
// define global variable the sources of compare
int  btn_switch = OFF;
// define the reverse lock
struct spinlock btn_lock;
#elif defined(SEMAPHORE)
// define global variable the sources of compare
source_t src_num = SOURCE_NUM;
// define the semaphore
struct semaphore btn_sem;
#endif

//[d]按照原型实现btn_open
int btn_open(struct inode *inode, struct file *fp)
{
    printk("<0>" "enter : %s\n", __func__);
#if defined(SEMAPHORE)
	down(&btn_sem);
	mdelay(6000);	// delay 6s
	if (!src_num--) {
		src_num++;
		up(&btn_sem);
		return -EAGAIN;
	}
	up(&btn_sem);

#elif defined(SPINLOCK)
	if (OFF == btn_switch) {
		spin_lock(&btn_lock);
		msleep(100);
		btn_switch = ON;
		spin_unlock(&btn_lock);
	}
	else
		return -EAGAIN;

#elif defined(ATOMIC)
	if (!atomic_dec_and_test(&btn_tv)) {
		atomic_inc(&btn_tv);
		return -EAGAIN;
	}
#endif
    return 0;
}

//[e]按照原型实现btn_close
int btn_close(struct inode *inode, struct file *fp)
{
	printk("<0>" "enter : %s\n", __func__);
#if defined(SEMAPHORE)
	up(&btn_sem);
	src_num++;
#elif defined(SPINLOCK)
	spin_lock(&btn_lock);
	btn_switch = OFF;
	spin_unlock(&btn_lock);
#elif defined(ATOMIC)
	atomic_inc(&btn_tv);
#endif
    return 0;
}

//[c]定义全局变量---操作函数集合
struct file_operations btn_fops = {
    .owner = THIS_MODULE,
    .open = btn_open,
    .release = btn_close
};


int __init btn_drv_init(void)
{
    //[1]申请设备号
    alloc_chrdev_region(&dev, 0, 1, "btn");

    //[3]初始化 cdev 对象
    cdev_init(&btn_cdev, &btn_fops);

    //[5]注册 cdev 对象到内核中
    cdev_add(&btn_cdev, dev, 1);

    //[6]自动创建设备文件(创建树枝)
    btn_cls = class_create(THIS_MODULE, "mybuttons");
    //(创建果实)
    device_create(btn_cls, NULL, dev, NULL, "buttons");
#if defined(SEMAPHORE)
	// initialize semaphore variable
	sema_init(&btn_sem, 1);
#elif defined(ATOMIC)
	// initialize int atomic_t variable
	atomic_set(&btn_tv, 1);
#elif defined(SPINLOCK)
	// initialize reverse lock variable
	spin_lock_init(&btn_lock);
#endif
    return 0;
}

void __exit btn_drv_exit(void)
{
    //[8]销毁设备文件(销毁果实)
    device_destroy(btn_cls, dev);

    //[9]销毁设备文件依赖的设备树(销毁树枝)
    class_destroy(btn_cls);

    //[a]注销 cdev 对象
    cdev_del(&btn_cdev);

    //[b]释放设备号
    unregister_chrdev_region(dev, 1);

    return ;
}

module_init(btn_drv_init);
module_exit(btn_drv_exit);

        9)区别

在解决竞态问题时:

        【1】信号量保护的临界区对执行的时间的长短没有要求

                 自旋锁保护的临界区要求执行速度尽量快

                 执行时间一旦过长,造成另外一个进程获取锁不成功,内核就会崩溃

                 此外,多核系统时,自旋锁原地自旋,系统性能下降

        【2】获取信号量不成功,进程睡眠等待

                 获取自旋锁不成功,进程原地自旋

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2205810.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ubuntu24 修改ip地址 ubuntu虚拟机修改静态ip

1. ubuntu 修改地址在/etc/netplan # 进入路径 cd /etc/netplan # 修改文件夹下的配置文件&#xff0c;我的是50-cloud-init.yaml. ye可能你得是20-cloud-init.yaml 2. 修改为&#xff1a; dhcp4: 改为false 192.168.164.50 是我自己分配的ip地址, /24 为固定写法&#xff…

jmeter输出性能测试报告(常见问题处理与处理)

问题1&#xff1a;报错 WARNING: Could not open/create prefs root node Software\JavaSoft\Prefs at root 0x80000002. Windows R 意思是&#xff1a;报没有权限 处理&#xff1a; 操作非gui生成测试报告的方法 cmd界面进入到 jmeter的bin目录 jmeter –n –t -l -e –o …

高价跟低价的宠物空气净化器有什么区别?好用不贵净化器得这样选

有句俗话叫&#xff1a;“便宜电器等于白买”。这让咱们这些普通家庭想买却犹豫不决&#xff0c;毕竟顶级配置的电器价格昂贵&#xff0c;随便一件就得几千上万。而如果选择性价比高的&#xff0c;又担心效果不好&#xff0c;感觉像是在交智商税。但对于我们这些养宠物的家庭来…

NRF24L01无线通信模块学习 来自正点原子标准库

SPI通信 自动ACK&#xff0c;发送完数据后可等对方回你 NRF24L01介绍 时序介绍&#xff0c;数据位多字节传输时&#xff0c;低字节在前&#xff0c;高字节在后 工作模式介绍&#xff0c;当处于发送模式的时候&#xff0c;CE脚电平为1&#xff0c;延时10ms&#xff0c;CE脚电…

Nacos 2.2.x版本配置详解(鉴权版本)

Nacos 2.2.x 一、安装和鉴权二、项目中配置集成1.位置问题2.namespace命名空间3.username和password4.group5.file-extension6.prefix7.shared-configs 三、实战1.新建一个命名空间&#xff0c;取名wq-config&#xff08;这个你随意&#xff09;&#xff0c;会随机生成一个命名…

vulnhub-Kioptrix_Level_2_update靶机的测试报告

目录 一、测试环境 1、系统环境 2、使用工具/软件 二、测试目的 三、操作过程 1、解决检测不到IP的问题 2、信息搜集 3、Getshell 4、提权 四、结论 一、测试环境 1、系统环境 渗透机&#xff1a;kali2021.1(192.168.202.134) 靶 机&#xff1a;Linux kioptrix.l…

【springboot9730】基于springboot+vue的网吧管理系统

作者主页&#xff1a;Java码库 主营内容&#xff1a;SpringBoot、Vue、SSM、HLMT、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、小程序、安卓app等设计与开发。 收藏点赞不迷路 关注作者有好处 文末获取源码 项目描述 随着信息技术和网络技术的飞速发展&#xff0c;人类已进…

摩托车一键启动兼容机械钥匙点火

摩托车无钥匙一键启动 工作原理 摩托车无钥匙一键启动系统的工作原理主要依赖于RFID无线射频技术和车辆身份编码识别系统。这种技术能够通过小型化、小功率的射频天线来识别车辆的身份&#xff0c;并与遥控系统结合&#xff0c;实现双重射频系统和双重防盗保护。这意味着&…

CV实战01 YOLOv5实现图像分割

网上翻了一天&#xff0c;没找到称心的教程&#xff0c;最后发现还是Ultralytics官方的教程文档好用&#xff01;这里贴上官方教程一起学习&#xff01; 【1&#xff1a;找到官方教程文档】 yolov5官方下载地址&#xff1a;GitHub - ultralytics/yolov5: YOLOv5 &#x1f680…

【p2p、分布式,区块链笔记 UPNP】: Libupnp test_init.c 02 初始化SDK --- UpnpInitPreamble

启动前全局资源配置 代码解析函数分析代码中的重要部分1. Winsock 初始化 (WinsockInit)&#xff1a;2. 锁初始化资源 (UpnpInitMutexes)&#xff1a;3. 句柄表HandleTable(SDK 内部资源的表)初始化&#xff1a;4.线程池初始化 (UpnpInitThreadPools)&#xff1a;5. 回调函数设…

Android OpenGLES2.0开发(四):矩阵变换和相机投影

事物的本质是事物本身所固有的、深藏于‌现象背后并决定或支配现象的方面‌。 还记得我们上一篇绘制的三角形吗&#xff0c;我们确实能够顺利用OpenGL ES绘制出图形了&#xff0c;这是一个好的开始&#xff0c;但这还远远不够。我们定义的坐标是正三角形&#xff0c;但是绘制出…

解决无法安装“vue.volar“扩展,跟vscode版本不兼容问题

问题&#xff1a;安装volar插件的时候提示跟vscode版本不兼容 解决方案 1、进入VSCode插件市场&#xff0c;搜索Vue.volar&#xff08;直达链接&#xff1a;volar下载界面&#xff09; 2、点击download Extension&#xff08;下载插件&#xff09; 3、下载.vsix文件完成后&a…

杨中科 .netcore Linq 。一前期准备知识

为什么要学Linq 一、为什么要学LINQ? 让数据处理变得简单: 统计一个字符串中每个字母出现的频率(忽略大小写)&#xff0c;然后按照从高到低的顺序输出出现频率高于2次的单词和其出现的频率。 var itemss.Where(c >char.lsLetter(c))//过滤非字母 .Select(c>char.ToLo…

【网络安全】CVE-2024-46990: Directus环回IP过滤器绕过实现SSRF

未经许可,不得转载。 文章目录 背景漏洞详情受影响版本解决方案背景 Directus 是一款开源 CMS,提供强大的内容管理 API,使开发人员能够轻松创建自定义应用程序,凭借其灵活的数据模型和用户友好的界面备受欢迎。然而,Directus 存在一个漏洞,允许攻击者绕过默认的环回 IP …

55100-3H-02微型法兰安装霍尔效应传感器

55100-3H-02微型法兰安装霍尔效应传感器应用领域如&#xff1a;位置和极限感应、无刷换向、RPM测量、直流电机、流量计量、角度感应、磁编码器。 概述 55100微型法兰安装霍尔效应传感器&#xff0c;尺寸为25.5mmx 11.00m&#xff0c;高度仅为3.00mm&#xff0c;可选择数字或可…

生物制药洁净室:设计、施工及常见问题解决

生物制药洁净室的设计施工要点对于保障生产环境的洁净度至关重要。生物制药洁净室的建设不仅是为了符合行业标准&#xff0c;更是确保产品安全和质量的重要环节。生物制药洁净室设计与生物制药洁净室施工是相辅相成的&#xff0c;接下来&#xff0c;我们将深入探讨这两方面的关…

交易中心态管理和情绪控制是第一位

情绪是交易中常见的障碍&#xff0c;无论是负面情绪还是喜悦等正面情绪&#xff0c;都可能成为妨碍交易的关键因素。交易的核心在于保持客观理性&#xff0c;而情绪的起伏往往不经意间扰乱我们的思绪。因此&#xff0c;成功的交易员若想突破自我&#xff0c;首要任务便是克服情…

事件轮询机制

引出闭包案例 for(var i 0; i < 5; i) {setTimeout(function () {console.log(i); // &#xff1f;}); } console.log(a); // 结果是什么&#xff1a;&#xff1f; 2.事件轮询机制&#xff08;Event Loop&#xff09; 事件轮询&#xff08;Event Loop&#xff09;是一个…

调整ApplyItem的界面

调整ApplyItem的界面 前言 在上一集&#xff0c;我们就完成了一个clickItem的一个函数&#xff0c;继承SessionFriendItem并通过它添加不同的Item&#xff0c;并且我添加了它的枚举类。 在上一集的最后&#xff0c;我们提了一嘴&#xff0c;我们要修改ApplyItem的样式。 分…

Flink 04 | 窗口介绍 - 无界数据流的核心

窗口介绍 Flink中Windows&#xff08;窗口&#xff09;是处理无界数据流的核心。因为无界数据集是不断增长的&#xff0c;无法直接对整个数据集进行操作。窗口将数据流分成有限大小的Buckets&#xff0c;我们可以在这些Buckets上应用计算。本文档重点介绍如何在 Flink 中选择合…