Linux第77步_处理Linux并发的相关函数

了解linux中的“原子整形数据”操作、“原子位数据”操作、自旋锁、读写锁、顺序锁、信号量和互斥体,以及相关函数。

并发就是多个“用户”同时访问同一个共享资源。如:多个线程同时要求读写同一个EEPROM芯片,这个EEPROM就是共享资源,为了保证读写的正确性,其它线程必须等待“持有者”释放使用权限,才可以使用该EEPROM。并发带来的问题就是竞争。Linux采用“原子整形数据”操作、“原子位数据”操作、自旋锁、读写锁、顺序锁、信号量和互斥体的函数来解决并发与竞争。

1、Linux系统并发产生的原因:

1)、多线程并发访问。

2)、抢占式并发访问,从2.6版本内核开始,Linux内核支持抢占,也就是说调度程序可以在任意时刻抢占正在运行的线程,从而运行其他的线程。

3)、中断程序并发访问。

4)、SMP(多核)核间并发访问。

注意:SOC称为系统级芯片,也有称片上系统。

如果线程A和线程B修改同一个存储单元,如果没有竞争,就会按照下面的流程执行,都会得到正确的结果。

线程A:

ldr r0, =0X30000000    /* r0=0X30000000  */

ldr r1, =10            /* r1=10  */

str r1, [r0]           /*将地址0X30000000的存储单元设置为10*/

线程B:

ldr r0, =0X30000000    /* r0=0X30000000  */

ldr r1, =20            /* r1=20  */

str r1, [r0]           /*将地址0X30000000的存储单元设置为20*/

如果线程A和线程B发生竞争,地址0X30000000的存储单元设置的值可能是错误的。

2、原子操作:

原子操作是指不能再进行分割的操作。主要指整型变量操作或者位变量操作。

1)、“原子整形数据”操作:

①、在“include/linux/types.h”文件中,atomic_t的结构体如下

如果是32位的系统级芯片,Linux内核定义的32位原子结构体如下:

typedef struct {

  int counter;

}atomic_t;

如果是64位的系统级芯片,Linux内核也定义了64位原子结构体如下:

typedef struct {

s64 counter;

} atomic64_t;

②、“原子整形数据”操作函数:

ATOMIC INIT(int i)

定义原子变量的时候对其初始化;

int atomic read(atomic_t *v)

读取v->counter的值,并且返回;

void atomic_set(atomic_t *v, int i)

向v->counter写入i值;

void atomic_add(int i. atomic_t *v)

给v->counter加上i值;

void atomic_sub(int i, atomic_t *v)

从v->counter减去i值;

void atomic_inc(atomic_t *v)

给v->counter加1,也就是自增;

void atomic_dec(atomic_t *v)

从v->counter减1,也就是自减;

int atomic_dec_return(atomic_t *v)

从v->counter减1,并且返回v的值;

int atomic_inc_return(atomic_t *v)

给v->counter加1,并且返回v的值;

int atomic_sub_and_test(int i.atomic_t *v)

从v->counter减i,如果结果为0就返回真,否则返回假;

int atomic_dec_and_test(atomic_t *v)

从v->counter减1,如果结果为0就返回真,否则返回假;

int atomic_inc_and_test(atomic_t *v)

给v->counter加1,如果结果为0就返回真,否则返回假;

int atomic_add_negative(int i, atomic_t *v)

给v->counter加i,如果结果为负就返回真,否则返回假;

举例:

atomic_t v = ATOMIC_INIT(0); /* 定义原子变量v,并初始化原子变零v=0 */

atomic_set(&v, 10);         /* 设置v->counter=10 */

atomic_read(&v);            /* 读取v->counter的值,肯定是10 */

atomic_inc(&v);             /* v->counter的值加1,v->counter=11 */

atomic_t Mylock;                   /* 原子变量 */

Mylock = (atomic_t)ATOMIC_INIT(0); //初始化原子变量

atomic_set(&Mylock, 1);            //原子变量初始值为Mylock->counter=1

if (!atomic_dec_and_test(&Mylock)) {

//当Mylock->counter=1时,atomic_dec_and_test()返回1

//从Mylock->counter减1,如果结果为0就返回1,否则返回0;

 atomic_inc(&Mylock);

 return -EBUSY; /* Mylock被使用,返回忙 */

}

2)、“原子位数据”操作:

需要包含“#include <bitops.h>

void set_bit(int nr, void *p)

将p地址的第nr位置1;

void clear_bit(int nr,void *p)

将p地址的第nr位清零;

void change_bit(int nr, void *p)

将p地址的第nr位进行翻转;

int test_bit(int nr, void *p)

获取p地址的第nr位的值;

int test_and_set_bit(int nr, void *p)

将p地址的第nr位置 1,并且返回第nr位原来的值;

int test_and_clear_bit(int nr, void *p)

将p地址的第nr位清零,并且返回第nr位原来的值;

int test_and_change_bit(int nr, void *p)

将p地址的第nr位翻转,并且返回第nr位原来的值;

举例:

#include <bitops.h>

unsigned long     mig_status;

#define NFS_MIG_FAILED          (2)

int ret;

if ( test_bit(NFS_MIG_FAILED, &mig_status) )

{//mig_status的bit2=1,则执行

    ret = -EIO;

    return ret;

}

3)、原子操作只对整型变量和位变量数据进行保护。

3、自旋锁

1)、自旋锁结构体spinlock_t

需要包含头文件“#include < spinlock_types.h >”;

Linux内核使用结构体spinlock_t表示自旋锁,定义如下:

typedef struct spinlock {

    union {

       struct raw_spinlock rlock;

#ifdef CONFIG_DEBUG_LOCK_ALLOC

# define LOCK_PADSIZE (offsetof(struct raw_spinlock, dep_map))

       struct {

           u8 __padding[LOCK_PADSIZE];

           struct lockdep_map dep_map;

       };

#endif

    };

} spinlock_t;

2)、采用“自旋锁”,实现线程之间的并发访问函数:

需要包含头文件“#include <spinlock.h>”;

#define DEFINE_SPINLOCK(x)  spinlock_t x = __SPIN_LOCK_UNLOCKED(x)

声明x为自旋锁结构,并初始化;

int spin_lock_init(spinlock_t *lock)

初始化自旋锁;

void spin_lock(spinlock_t *lock)

获取指定的自旋锁,也叫做加锁;

void spin_unlock(spinlock_t *lock)

释放指定的自旋锁;

int spin_trylock(spinlock_t *lock)

尝试获取指定的自旋锁,如果没有获取到就返回0;

int spin_is_locked(spinlock_t *lock)

检查指定的自旋锁是否被获取,如果没有被获取就返回非0,否则返回0;

注意:

线程之间的并发访问函数,适用于线程之间的并发访问,但不适合中断程序并发访问

3)、采用“自旋锁”,实现线程与中断之间的并发访问函数:

void spin_lock_irq(spinlock_t *lock)

禁止本地中断,并获取自旋锁;

void spin_unlock_irq(spinlock_t *lock)

激活本地中断,并释放自旋锁;

void spin_lock_irqsave(spinlock_t *lockunsigned long flags)

保存中断状态,禁止本地中断,并获取自旋锁;

void spin_unlock_irqrestore(spinlock_t*lock, unsigned long flags)

将中断状态恢复到以前的状态,并且激活本地中断,释放自旋锁。

注意:在线程中使用spin_lock_irqsave()和spin_unlock_irqrestore();在中断中使用spin_lock()和spin_unlock()来获取自旋锁和释放自旋锁不推荐使用spin_lock_irq()和spin_unlock_irq函数

4)、举例:

DEFINE_SPINLOCK(MyLock) /*声明MyLock为自旋锁结构,并初始化*/

/* 线程A */

void functionA (){

unsigned long flags; /* 中断状态 */

spin_lock_irqsave(&MyLock, flags); /* 获取锁 */

    //保存中断状态,禁止本地中断,并获取自旋锁;

/* 临界区 */

spin_unlock_irqrestore(&MyLock, flags); /* 释放锁 */

   //将中断状态恢复到以前的状态,并且激活本地中断,释放自旋锁;

}

/* 中断服务函数 */

void irq() {

spin_lock(&MyLock); /* 获取锁 */

//获取MyLock自旋锁,也叫做加锁;

/* 临界区 */

spin_unlock(&MyLock); /* 释放锁 */

//释放MyLock自旋锁;

}

5)、“下半部”并发访问函数

需要包含文件“#include <spinlock.h>

下半部(BH)也会竟争共享资源,有些资料也会将下半部叫做底半部。如果要在“下半部”里面使用自旋锁,则需要用到下面的函数:

void spin_lock_bh(spinlock_t*lock)

关闭下半部,并获取自旋锁;

void spin_unlock_bh(spinlock_t*lock)

打开下半部,并释放自旋锁;

6)、自旋锁使用注意事项:

①、“其它线程”在等待自旋锁的时处于“自旋”状态,因此“持有锁的线程”不能长时间持用这个自旋锁,一定要短,否则的话会降低系统性能。如果临界区比较大,运行时间比较长的话要选择其他的并发处理方式,比如信号量和互斥体。

②、自旋锁保护的临界区内不能调用任何可能导致线程休眠的API函数,否则的话可能导致死锁。

③、不能递归申请白旋锁,因为一旦通过递归的方式申请一个你正在持有的锁,那么你就必须“自旋”,等待锁被释放,然而你正处于“自旋”状态,根本没法释放锁。结果就是自己把自己锁死了!

④、在编写驱动程序的时候我们必须考虑到驱动的可移植性,因此不管你用的是单核的还是多核的SOC,都将其当做多核SOC来编写驱动程序。

4、读写锁函数

需要包含文件“#include <rwlock_types.h>

typedef struct {

    arch_rwlock_t raw_lock;

#ifdef CONFIG_DEBUG_SPINLOCK

    unsigned int magic, owner_cpu;

    void *owner;

#endif

#ifdef CONFIG_DEBUG_LOCK_ALLOC

    struct lockdep_map dep_map;

#endif

} rwlock_t;

#define DEFINE_RWLOCK(x) rwlock_t x = __RW_LOCK_UNLOCKED(x)

声明“读写锁结构变量x”,并初始化读写锁;

读写锁函数需要包含文件“#include <rwlock.h>

void rwlock_init(rwlock_t *lock)

初始化读写锁;

void read_lock(rwlock_t *lock)

获取读锁;

void read_unlock(rwlock_t *lock)

释放读锁;

void read_lock_irq(rwlock_t *lock)

禁止本地中断,并且获取读锁;

void read_unlock_irg(rwlock_t *lock)

打开本地中断,并且释放读锁;

void read_lock_irgsave(rwlock_t *lock,unsigned long flags)

保存中断状态,禁止本地中断,并获取读锁;

void read_unlock_irqrestore(rwlock_t *lock,unsigned long flags)

将中断状态恢复到以前的状态,并且激活本地中断,释放读锁;

void read_lock_bh(rwlock_t *lock)

关闭下半部,并获取读锁;

void read_unlock_bh(rwlock_t *lock)

打开下半部,并释放读锁;

void write_lock(rwlock_t *lock)

获取写锁;

void write_unlock(rwlock_t *lock)

释放写锁;

void write_lock_irg(rwlock_t *lock)

禁止本地中断,并且获取写锁;

void write_unlock_irg(rwlock_t *lock)

打开本地中断,并且释放写锁;

void write_lock_irqsave(rwlock_t *lock,unsigned long flags)

保存中断状态,禁止本地中断,并获取写锁。

void write_unlock_irqrestore(rwlock_t *lock,unsigned long flags)

将中断状态恢复到以前的状态,并且激活本地中断,释放读锁。

void write_lock_bh(rwlock_t *lock)

关闭下半部,并获取读锁;

void write_unlock_bh(rwlock_t *lock)

打开下半部,并释放读锁;

5、顺序锁

顺序锁结构需要包含文件“#include <seqlock.h>

顺序锁结构体seqlock_t

typedef struct {

struct seqcount seqcount;

spinlock_t lock;

}seqlock_t;

使用顺序锁,实现同时读写,但是不允许同时进行并发的写操作。虽然顺序锁的读和写操作可以同时进行,但是如果在读的过程中发生了写操作,最好重新进行读取,保证数据完整性。

注意:

顺序锁“保护的资源”不能是指针,因为如果在写操作的时候可能会导致指针无效,而这个时候恰巧有读操作访问指针的话就可能导致意外发生,比如读取野指针导致系统崩溃

#define DEFINE_SEQLOCK(x) \

       seqlock_t x = __SEQLOCK_UNLOCKED(x)

声明“顺序锁结构变量x”,并初始化

void write_seqlock(seqlock_t *sl)

获取“写顺序锁”;

void write_sequnlock(seqlock_t *sl)

释放“写顺序锁”;

void write_seqlock_irq(seqlock_t *sl)

禁止本地中断,并且获取“写顺序锁”;

void write_sequnlock_irq(seqlock_t *sl)

打开本地中断,并且释放“写顺序锁”;

void write_seqlock_irqsave(seqlock_t *sl,unsigned long flags)

保存中断状态,禁止本地中断,并获取“写顺序锁”;

void write_sequnlock_irqrestore(seqlock_t *sl,unsigned long flags)

将中断状态恢复到以前的状态,并且激活本地中断,释放“写顺序锁”;

void write_seqlock_bh(seqlock_t *sl)

关闭下半部,并获取“写顺序锁”;

void write_sequnlock_bh(seqlock_t *sl)

打开下半部,并释放“写顺序锁”;

unsigned read_seqbegin(const seqlock_t *sl)

读单元访问共享资源的时候调用此函数,此函数会返回顺序锁的顺序号;

unsigned read_seqretry(const seqlock_t *sl,unsigned start)

读结束以后调用此函数检查在读的过程中有没有对资源进行写操作,如果有的话就要重读;

6、信号量

信号量有一个“信号量值”,用来控制访问共享资源的访问数量,相当于通过“信号量值”控制访问资源的线程数。

如果将“信号量值”设置大于 1,那么这个信号量就是“计数型信号量”,它允许多个线程同时访问共享资源。

如果将“信号量值”设置为0,那么这个信号量就是“二值信号量”,它具有互斥访问共享资源的作用。

需要包含文件“#include <semaphore.h>

1)、信号量结构体semaphore

struct semaphore {

    raw_spinlock_t       lock;

    unsigned int      count;

    struct list_head  wait_list;

};

#define __SEMAPHORE_INITIALIZER(name, n)             \

{                               \

    .lock      = __RAW_SPIN_LOCK_UNLOCKED((name).lock),  \

    .count     = n,                     \

    .wait_list = LIST_HEAD_INIT((name).wait_list),       \

}

并将“name.count”设置为n;

2)、信号量函数

#define DEFINE_SEMAPHORE(name)  \

    struct semaphore name = __SEMAPHORE_INITIALIZER(name, 1)

声明信号量结构变量为name,并将信号量的值“name. count”设置为1;这是一个“计数型信号量”。

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

{

    static struct lock_class_key __key;

    *sem = (struct semaphore) __SEMAPHORE_INITIALIZER(*sem, val);

    lockdep_init_map(&sem->lock.dep_map, "semaphore->lock", &__key, 0);

}

初始化信号量结构变量sem,并将“sem->count”设置为val;

void down(struct semaphore *sem)

获取信号量,但会导致休眠,进入休眠状态的线程不能被信号打断,因此不能在中断中使用

int down_trylock(struct semaphore *sem)

尝试获取信号量,如果能获取到信号量就获取,并且返回0。如果不能就返回非0,并不会进入休眠。

int down_interruptible(struct semaphore *sem)

获取信号量,进入休眠状态的线程可以被信号打断。

void up(struct semaphore *sem)

释放信号量

int down_killable(struct semaphore *sem)

进入休眠状态的线程可以被唤醒,中断获取信号量的操作;

3)、举例:

struct semaphore sem; /* 定义信号量 */

sema_init(&sem, 1); /* 初始化信号量 */

down(&sem);          /* 申请信号量 */

/* 临界区 */

up(&sem); /* 释放信号量 */

4)、信号量的特点:

信号量可以使等待资源线程进入休眠状态,因此适用于那些占用资源比较久的场

合,因此信号量不能用于中断中,因为信号量会引起休眠,中断是不能进入休眠的。

如果共享资源的持有时间比较短,那就不适合使用信号量。因为频繁的休眠、切换线程引起的开销要远大于信号量带来的那点优势。

7、互斥体

需要包含文件“#include <mutex.h>

struct mutex {

    atomic_long_t     owner;

    spinlock_t    wait_lock;

};

#define DEFINE_MUTEX(mutexname) \

    struct mutex mutexname = __MUTEX_INITIALIZER(mutexname)

声明互斥结构变量mutexname,并初始化;

void mutex_init(struct mutex *lock);

并初始化互斥结构变量lock

void mutex_lock(struct mutex *lock)

获取 mutex,也就是给 mutex 上锁。如果获取不到就进休眠;

void mutex_unlock(struct mutex *lock)

释放 mutex,也就给 mutex 解锁;

int mutex_trylock(struct mutex *lock)

尝试获取mutex,如果成功就返回 1,如果失败就返回 0;

int mutex_is_locked(struct mutex *lock)

判断mutex_是否被获取,如果是的话就返回1,否则返回 0;

int mutex_lock_interruptible(struct mutex *lock)

使用此函数获取信号量失败进入休眠以后可以被信号打断;

举例:

struct mutex lock; /* 定义一个互斥体 */

mutex_init(&lock); /* 初始化互斥体 */

mutex_lock(&lock); /* 上锁 */

/* 临界区 */

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

使用“互斥体”时,需要注意如下几点:

①、互斥体可以导致休眠,因此不能在中断中使用互斥体,在中断中只能使用自旋锁

②、和信号量一样,“互斥体”保护的临界区可以调用引起阻塞的API函数。

因为一次只有一个线程可以持有“互斥体”,因此,必须由“互斥体”的持有者释放“互斥体”。

③、“互斥体”不能递归上锁和解锁。

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

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

相关文章

【数学建模】线性规划

针对未来可能的数学建模比赛内容&#xff0c;我对学习的内容做了一些调整&#xff0c;所以先跳过灰色关联分析和模糊综合评价的代码&#xff0c;今天先来了解一下运筹规划类——线性规划模型。 背景&#xff1a; 某数学建模游戏有三种题型&#xff0c;分别是A&#xff0c;B&am…

远程办公、企业内网服务器的Code-Server上如何配置使用CodeGeeX插件

很多小伙伴都会在工作中使用code-server&#xff0c;比如说远程办公&#xff0c;当你需要在家访问你的工作环境&#xff0c;亦或者是你们公司的Docker是放入服务器中。code-server 无疑是最好的选择&#xff0c;它可以让你通过互联网安全地连接到远程服务器上的开发环境并且使用…

【保姆级】GPT的Oops问题快速解决方案

GPT的"Oops"问题通常指的是GPT在处理请求时突然遇到错误或无法提供预期输出的情况。要快速解决这个问题&#xff0c;可以尝试以下分步策略&#xff1a; 确认问题范围&#xff1a; 首先&#xff0c;确认问题是偶发的还是持续存在的。如果是偶发的&#xff0c;可能是临…

微博热搜榜单采集,微博热搜榜单爬虫,微博热搜榜单解析,完整代码(话题榜+热搜榜+文娱榜和要闻榜)

文章目录 代码1. 话题榜2. 热搜榜3. 文娱榜和要闻榜 过程1. 话题榜2. 热搜榜3. 文娱榜和要闻榜 代码 1. 话题榜 import requests import pandas as pd import urllib from urllib import parse headers { authority: weibo.com, accept: application/json, text/pl…

win10磁盘删除卷里面数据怎么恢复 win10磁盘删除卷怎么恢复

大家好&#xff0c;我是你们的小助手&#xff0c;今天我们来聊一下一个非常重要的话题——win10磁盘删除卷里面数据怎么恢复&#xff1f;相信很多小伙伴都曾经遇到过这种情况&#xff0c;不小心把重要的文件删掉了&#xff0c;或者格式化了整个磁盘&#xff0c;导致数据丢失。那…

Halcon中的交集、补集、全选和反选等操作

1、交集&#xff1a;两个ROI相交的部分 dev_open_window (0, 0, 512, 512, black, WindowHandle) gen_circle (ROI_0, 65, 150, 43) gen_circle (ROI_1, 155, 180, 63) * 交集&#xff1a;两个ROI相交的部分 intersection (ROI_0, ROI_1, RegionIntersection) dev_set_color (…

ResNet学习笔记

一、residual结构 优点&#xff1a; &#xff08;1&#xff09;超深的网络结构(突破1000层) &#xff08;2&#xff09;提出residual模块 &#xff08;3&#xff09;使用Batch Normalization加速训练(丢弃dropout) 解决问题&#xff1a; &#xff08;1&#xff09; 梯度消失和…

基于springboot+vue的企业人事管理系统

一、系统架构 前端&#xff1a;vue | element-ui 后端&#xff1a;springboot | mybatis-plus 环境&#xff1a;jdk1.8 | mysql | maven | node14 | redis 二、代码及数据库 三、功能介绍 01. 登录页 02. 首页 03. 员工入职 04. 部门员工管理-部门管理 05. 部门…

聚观早报 | 阿里巴巴计划投资韩国;魏牌蓝山新车型曝光

聚观早报每日整理最值得关注的行业重点事件&#xff0c;帮助大家及时了解最新行业动态&#xff0c;每日读报&#xff0c;就读聚观365资讯简报。 整理丨Cutie 3月15日消息 阿里巴巴计划投资韩国 魏牌蓝山新车型曝光 蔚来提出长寿命电池解决方案 OpenAI与多家出版商合作 零…

Python之Web开发中级教程----创建Django项目

Python之Web开发中级教程----创建Django项目 使用虚拟环境&#xff1a; Workon py3_django3 1.创建Django项目 django-admin startproject name 例&#xff1a;git的本地仓库下新建studentmanager的项目 cd /home/go/work/gtest/ django-admin startproject bookmanager 新…

Qt+FFmpeg+opengl从零制作视频播放器-3.解封装

解封装&#xff1a;如下图所示&#xff0c;就是将FLV、MKV、MP4等文件解封装为视频H.264或H.265压缩数据&#xff0c;音频MP3或AAC的压缩数据&#xff0c;下图为常用的基本操作。 ffmpeg使用解封装的基本流程如下&#xff1a; 在使用FFmpeg API之前&#xff0c;需要先注册API&a…

福派斯猫粮的适口性有何特点?

亲爱的朋友们&#xff0c;今天我想和大家分享一下福派斯猫粮的适口性特点。作为一位养猫多年的铲屎官&#xff0c;我深知猫粮的适口性对于猫咪的健康和幸福是多么重要。那么&#xff0c;福派斯猫粮在这方面究竟有何独到之处呢&#xff1f; 1️⃣ 首先&#xff0c;福派斯猫粮的口…

【Java多线程】关于多线程的一些案例 —— 单例模式中的饿汉模式和懒汉模式以及阻塞队列

目录 1、单例模式 1.1、饿汉模式 2.1、懒汉模式 2、阻塞队列 2.1、BlockingQueue 阻塞队列数据结构 1、单例模式 对框架和设计模式的简单理解就是&#xff0c;这两者都是“大佬”设计出来的&#xff0c;让即使是一个代码写的不太好的“菜鸡程序员”也能写出还可以的代码…

全球化服务能力,助力企业拓展海外市场,仓储物流行业解决方案

随着全球化的加速推进&#xff0c;越来越多的企业开始将目光投向海外市场&#xff0c;寻求更广阔的发展空间。然而&#xff0c;海外市场的拓展并非易事&#xff0c;需要企业具备强大的全球化服务能力。作为通信行业的领军企业&#xff0c;中国联通凭借其强大的网络资源和技术实…

求职分享123

阿里学长 简历&#xff1a;github上找&#xff0c;填信息 项目&#xff1a; 1. 教研室项目 2. github上下载项目下来做 3. 对于项目&#xff0c;要把个人工作详细地准备下来。 4. 给面试官挖坑。 5. 企业实习是巨大的加分项。 知识储备 刷题 不卷&#xff0c;leetcod…

同步和异步程序的关联和区别是?Guide to Synchronous and Asynchronous Code

2024/3/12 发布 正在寻觅一份前端开发工作&#xff0c;如果您觉得这篇文章对你有所帮助&#xff0c;这是我的简历1 在这篇文章中你能学习和理解&#xff1a;NodeJS是如何工作、如何处理所有发送给服务器的函数&#xff08;无论同步或者异步&#xff09;和请求、Event Loops in …

rk36566 uboot - dm 模型数据结构与常见接口

文章目录 一、数据结构1、udevice2、driver3、uclass4、uclass\_driver5、 总结6、device\_probe 二、常用接口1、udevice 创建接口1) device_bind_with_driver_data2) device_bind3) device_bind_by_name 2、uclass 操作函数1) uclass_get2) uclass_get_name3) uclass_find_de…

java中的日期类

1.1 第一代日期类 第一代日期时间API主要有java.util.Date和日期时间格式化有关的java.text.DateFormat及其子类。 1.1.1 Date类 JDK1.0就在java.util包下面提供了Date类用于表示特定的瞬间&#xff0c;可以精确到毫秒。   通过API或源码&#xff0c;可以看出Date类的大部…

vscode 导入前端项目

vscode 导入前端项目 导入安装依赖 运行 参考vscode 下载 导入 安装依赖 运行 在前端项目的终端中输入npm run serve

C#,数值计算,数据测试用的对称正定矩阵(Symmetric Positive Definite Matrix)的随机生成算法与源代码

C.Hermite 1、对称矩阵 对称矩阵(Symmetric Matrices)是指以主对角线为对称轴,各元素对应相等的矩阵。在线性代数中,对称矩阵是一个方形矩阵,其转置矩阵和自身相等。1855年,埃米特(C.Hermite,1822-1901年)证明了别的数学家发现的一些矩阵类的特征根的特殊性质,如称为埃…