范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文
国学影视

linux内核同步问题

  linux内核同步问题
  Linux内核设计与实现 十、内核同步方法
  手把手教Linux驱动5-自旋锁、信号量、互斥体概述
  ==基础概念:==
  并发:多个执行单元同时进行或多个执行单元微观串行执行,宏观并行执行
  竞态:并发的执行单元对共享资源(硬件资源和软件上的全局变量)的访问而导致的竟态状态。
  临界资源:多个进程访问的资源
  临界区:多个进程访问的代码段
  ==并发场合:==
  1、单CPU之间进程间的并发:时间片轮转,调度进程。 A进程访问打印机,时间片用完,OS调度B进程访问打印机。
  2、单cpu上进程和中断之间并发:CPU必须停止当前进程的执行中断;
  3、多cpu之间
  4、单CPU上中断之间的并发
  ==使用偏向:==
  需求
  建议加锁方式
  低开销、短期加锁
  优先自旋锁
  长期锁定
  优先互斥锁
  中断上下文加锁
  自旋锁
  需要睡眠的持有锁(单线程)
  互斥锁
  需要睡眠的持有锁(多线程)
  信号量1、信号量(semaphore)
  ==信号量用于进程之间的同步,进程在信号量保护的临界区代码里面是可以睡眠的(需要进行进程调度),这是与自旋锁最大的区别。==
  信号量又称为信号灯,它是用来协调不同进程间的数据对象的,而最主要的应用是共享内存方式的进程间通信。本质上,信号量是一个计数器,它用来记录对某个资源(如共享内存)的存取状况。它负责协调各个进程,以保证他们能够正确、合理的使用公共资源。它和spin lock最大的不同之处就是:无法获取信号量的进程可以睡眠,因此会导致系统调度。1.1、特点
  1、==用于进程与进程之间的同步==
  2、==允许多个进程进入临界区代码执行,临界区代码允许睡眠;==
  3、信号量本质是==基于调度器的==,在UP和SMP下没有区别;进程获取不到信号量将陷入休眠,并让出CPU;
  4、不支持进程和中断之间的同步
  5、==进程调度也是会消耗系统资源的,如果一个int型共享变量就需要使用信号量,将极大的浪费系统资源==
  6、信号量可以用于多个线程,用于资源的计数(有多种状态)1.2、常用函数
  ==信号量加锁以及解锁过程:==
  sema_init(&sp->dead_sem, 0); /初始化/
  down(&sema);
  临界区代码
  up(&sema);
  ==信号量定义:==struct semaphore {     raw_spinlock_t        lock;     unsigned int        count;     struct list_head    wait_list; };
  ==信号量初始化:==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); }
  ==dowm函数实现:==static inline int __sched __down_common(struct semaphore *sem, long state,                                 long timeout) {     struct task_struct *task = current;/*当前进程代表的结构体*/     struct semaphore_waiter waiter;      list_add_tail(&waiter.list, &sem->wait_list);     waiter.task = task;     waiter.up = false;      for (;;) {         if (signal_pending_state(state, task))             goto interrupted;         if (unlikely(timeout <= 0))             goto timed_out;         __set_task_state(task, state);         raw_spin_unlock_irq(&sem->lock);         timeout = schedule_timeout(timeout);         raw_spin_lock_irq(&sem->lock);         if (waiter.up)             return 0;     }   timed_out:     list_del(&waiter.list);     return -ETIME;   interrupted:     list_del(&waiter.list);     return -EINTR; } static noinline void __sched __down(struct semaphore *sem) {     __down_common(sem, TASK_UNINTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT); }  void down(struct semaphore *sem) {     unsigned long flags;      raw_spin_lock_irqsave(&sem->lock, flags);/*自旋锁*/     if (likely(sem->count > 0))         sem->count--;     else         __down(sem);     raw_spin_unlock_irqrestore(&sem->lock, flags);  }
  ==up函数实现:==void up(struct semaphore *sem) {     unsigned long flags;      raw_spin_lock_irqsave(&sem->lock, flags);/*自旋锁*/     if (likely(list_empty(&sem->wait_list)))         sem->count++;     else         __up(sem);     raw_spin_unlock_irqrestore(&sem->lock, flags);  }1.3、实现原理
  信号量一般可以用来标记可用资源的个数。
  举2个生活中的例子:我们要坐火车从南京到新疆,这个"任务"特别的耗时,只能在车上等着车到站,但是我们没有必要一直睁着眼睛等着车到站,最好的情况就是我们上车就直接睡觉,醒来就到站,这样从人(用户)的角度来说,体验是最好的,对比于进程,程序在等待一个耗时的任务的时候,没有必须要占用CPU,可以暂停当前任务使其进入休眠状态,当等待的事件发生之后再由其他任务唤醒,这种场景采用信号量比较合适。我们在等待电梯、等待洗手间,这种场景需要等待的事件并不是很多,如果我们还要找个地方睡一觉,然后等电梯到了或者洗手间可以用了再醒来,那很显然这也没有必要,我们只需要排好队,刷一刷抖音就可以了,对比于计算机程序,比如驱动在进入中断例程,在等待某个寄存器被置位,这种场景需要等待的时间很短暂,系统开销远小于进入休眠的开销,所以这种场景采用自旋锁比较合适。
  ==dowm函数实现原理解析:==
  (1)down
  判断sem->count是否 > 0,大于0则说明系统资源够用,分配一个给该进程,否则进入__down(sem);
  (2)__down
  调用__down_common(sem, TASK_UNINTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT);其中TASK_UNINTERRUPTIBLE=2代表进入睡眠,且不可以打断;MAX_SCHEDULE_TIMEOUT休眠最长LONG_MAX时间;
  (3)list_add_tail(&waiter.list, &sem->wait_list);
  把当前进程加入到sem->wait_list中;
  (3)先解锁后加锁;
  进入__down_common前已经加锁了,先把解锁,调用schedule_timeout(timeout),当waiter.up=1后跳出for循环;退出函数之前再加锁;2、原子变量(atomic)
  Linux内核ARM构架中原子变量的底层实现研究
  rk3288 原子操作和原子位操作
  原子变量适用于只共享一个int型变量;2.1、特点
  1、原子操作是指不被打断的操作,即它是最小的执行单位。
  2、最简单的原子操作就是一条条的汇编指令(不包括一些伪指令,伪指令会被汇编器解释成多条汇编指令)2.2、常用函数
  ==常见函数:==#define ATOMIC_INIT(i)    { (i) }    /*初始化原子变量*/ #define atomic_inc(v)        atomic_add(1, v)   /*原子变量加1*/ #define atomic_dec(v)        atomic_sub(1, v)   /*原子变量减1*/  #define atomic_inc_and_test(v)    (atomic_add_return(1, v) == 0)   /*原子变量加1并测试是否等于0*/ #define atomic_dec_and_test(v)    (atomic_sub_return(1, v) == 0)   /*原子变量减1并测试是否等于0*/2.3、实现原理
  ==以atomic_inc为例介绍实现过程==
  在Linux内核文件archarmincludeasmatomic.h中。 执行atomic_read、atomic_set这些操作都只需要一条汇编指令,所以它们本身就是不可打断的。 需要特别研究的是atomic_inc、atomic_dec这类读出、修改、写回的函数。
  但是atomic_add在内核中是很难找到的,因为没有这个直接的声明。而是一种宏实现。
  所以atomic_add的原型是下面这个宏:#define ATOMIC_OPS(op, c_op, asm_op)                         ATOMIC_OP(op, c_op, asm_op)                         ATOMIC_OP_RETURN(op, c_op, asm_op)                     ATOMIC_FETCH_OP(op, c_op, asm_op)  ATOMIC_OPS(add, +=, add)#define ATOMIC_OP(op, c_op, asm_op)                     static inline void atomic_##op(int i, atomic_t *v)             {                                         unsigned long tmp;                             int result;                                                                      prefetchw(&v->counter);                             __asm__ __volatile__("@ atomic_" #op " "             "1:    ldrex    %0, [%3] "                         "    " #asm_op "    %0, %0, %4 "                     "    strex    %1, %0, [%3] "                         "    teq    %1, #0 "                         "    bne    1b"                                 : "=&r" (result), "=&r" (tmp), "+Qo" (v->counter)             : "r" (&v->counter), "Ir" (i)                         : "cc");                             }
  atomic_add等效于:static inline void atomic_add(int i, atomic_t *v)             {                                         unsigned long tmp;                             int result;                                                                      prefetchw(&v->counter);                             __asm__ __volatile__("@ atomic_" #op " "             "1:    ldrex    %0, [%3] "                         "    " #asm_op "    %0, %0, %4 "                     "    strex    %1, %0, [%3] "                         "    teq    %1, #0 "                         "    bne    1b"                                 : "=&r" (result), "=&r" (tmp), "+Qo" (v->counter)             : "r" (&v->counter), "Ir" (i)                         : "cc");                             }
  result(%0) tmp(%1) (v->counter)(%2) (&v->counter)(%3) i(%4)
  注意:根据内联汇编的语法,result、tmp、&v->counter对应的数据都放在了寄存器中操作。如果出现上下文切换,切换机制会做寄存器上下文保护。
  (1)ldrex %0, [%3]
  意思是将&v->counter指向的数据放入result中,并且(分别在Local monitor和Global monitor中)设置独占标志。
  (2)add %0, %0, %4
  result = result + i
  (3)strex %1, %0, [%3]
  意思是将result保存到&v->counter指向的内存中,此时 Exclusive monitors会发挥作用,将保存是否成功的标志放入tmp中。
  (4) teq %1, #0
  测试strex是否成功(tmp == 0 ??)
  (5)bne 1b
  如果发现strex失败,从(1)再次执行。3、自旋锁(spinlock)
  Spinlock 是内核中提供的一种比较常见的锁机制,==自旋锁是"原地等待"的方式解决资源冲突的==,即,一个线程获取了一个自旋锁后,另外一个线程期望获取该自旋锁,获取不到,只能够原地"打转"(忙等待)。由于自旋锁的这个忙等待的特性,注定了它使用场景上的限制 —— 自旋锁不应该被长时间的持有(消耗 CPU 资源),一般应用在==中断上下文==。3.1、特点
  1、spinlock是一种死等机制
  2、信号量可以允许多个执行单元进入,spinlock不行,一次只能允许一个执行单元获取锁,并且进入临界区,其他执行单元都是在门口不断的死等
  3、由于不休眠,因此spinlock可以应用在中断上下文中;
  4、由于spinlock死等的特性,因此临界区执行代码尽可能的短;3.2、常用函数
  ==spinlock加锁以及解锁过程:==
  spin_lock(&devices_lock);
  临界区代码
  spin_unlock(&devices_lock);
  ==spinlock初始化==#define spin_lock_init(_lock)                 do {                                 spinlock_check(_lock);                     raw_spin_lock_init(&(_lock)->rlock);         } while (0)
  ==进程和进程之间同步==static __always_inline void spin_lock(spinlock_t *lock) {     raw_spin_lock(&lock->rlock); }
  ==本地软中断之间同步==static __always_inline void spin_lock_bh(spinlock_t *lock) {     raw_spin_lock_bh(&lock->rlock); }
  ==本地硬中断之间同步==static __always_inline void spin_lock_irq(spinlock_t *lock) {     raw_spin_lock_irq(&lock->rlock); }
  ==本地硬中断之间同步并且保存本地中断状态==#define spin_lock_irqsave(lock, flags)                 do {                                     raw_spin_lock_irqsave(spinlock_check(lock), flags);     } while (0)
  ==尝试获取锁==static __always_inline int spin_trylock(spinlock_t *lock) {     return raw_spin_trylock(&lock->rlock); }3.3、实现原理
  ==arch_spinlock_t结构体定义如下:==#define TICKET_SHIFT    16  typedef struct {     union {         u32 slock;/*union*/         struct __raw_tickets { #ifdef __ARMEB__  /*大端模式*/             u16 next;             u16 owner; #else/*小端模式*/             u16 owner;             u16 next; #endif         } tickets;     }; } arch_spinlock_t;
  ==arch_spin_lock的实现如下:==static inline void arch_spin_lock(arch_spinlock_t *lock) {     unsigned long tmp;     u32 newval;     arch_spinlock_t lockval;      prefetchw(&lock->slock);/*从lock中取出slock*/     __asm__ __volatile__( "1:    ldrex    %0, [%3] " "    add    %1, %0, %4 " "    strex    %2, %1, [%3] " "    teq    %2, #0 " "    bne    1b"     : "=&r" (lockval), "=&r" (newval), "=&r" (tmp)     : "r" (&lock->slock), "I" (1 << TICKET_SHIFT)     : "cc");      while (lockval.tickets.next != lockval.tickets.owner) {         wfe();/*等待,系统开销很大*/         lockval.tickets.owner = ACCESS_ONCE(lock->tickets.owner);     }      smp_mb(); }
  lockval(%0) newval(%1) tmp(%2) &lock->slock(%3) 1 << TICKET_SHIFT(%4)
  (1)ldrex %0, [%3]
  把lock->slock的值赋值给lockval;并且(分别在Local monitor和Global monitor中)设置独占标志。
  (2)add %1, %0, %4
  newval =lockval +(1<<16); 相当于next+1;
  (3)strex %2, %1, [%3]
  newval =lockval +(1<<16); 相当于next+1;
  意思是将newval保存到 &lock->slock指向的内存中,此时 Exclusive monitors会发挥作用,将保存是否成功的标志放入tmp中。
  (4) teq %2, #0
  测试strex是否成功
  (5)bne 1b
  如果发现strex失败,从(1)再次执行。
  通过上面的分析,可知关键在于strex的操作是否成功的判断上。而这个就归功于ARM的Exclusive monitors和ldrex/strex指令的机制。
  (6)while (lockval.tickets.next != lockval.tickets.owner)
  如何lockval.tickets的next和owner是否相等。相同则跳出while循环,否则在循环内等待判断;
  *(7)wfe()和smp_mb() 最终调用#define barrier() asm volatile("": : :"memory") *
  阻止编译器重排,保证编译程序时在优化屏障之前的指令不会在优化屏障之后执行。
  ==arch_spin_unlock的实现如下:==static inline void arch_spin_unlock(arch_spinlock_t *lock) {     smp_mb();     lock->tickets.owner++;     dsb_sev(); }
  退出锁时:tickets.owner++3.4、死锁以及解决办法
  ==出现死锁的情况:==
  1、拥有自旋锁的进程A在内核态阻塞了,内核调度B进程,碰巧B进程也要获得自旋锁,此时B只能自旋转。 而此时抢占已经关闭,(单核)不会调度A进程了,B永远自旋,产生死锁。
  2、进程A拥有自旋锁,中断到来,CPU执行中断函数,中断处理函数,中断处理函数需要获得自旋锁,访问共享资源,此时无法获得锁,只能自旋,产生死锁。
  ==如何避免死锁:==
  1、如果中断处理函数中也要获得自旋锁,那么驱动程序需要在拥有自旋锁时禁止中断;
  2、自旋锁必须在可能的最短时间内拥有
  3、避免某个获得锁的函数调用其他同样试图获取这个锁的函数,否则代码就会死锁;不论是信号量还是自旋锁,都不允许锁拥有者第二次获得这个锁,如果试图这么做,系统将挂起;
  4、锁的顺序规则(a) 按同样的顺序获得锁;b) 如果必须获得一个局部锁和一个属于内核更中心位置的锁,则应该首先获取自己的局部锁 ;c) 如果我们拥有信号量和自旋锁的组合,则必须首先获得信号量;在拥有自旋锁时调用down(可导致休眠)是个严重的错误的;)3.5、其他类型的spinlock
  ==rw(read/write)spinlock:==
  加锁逻辑:
  1、假设临界区内没有任何的thread,这个时候任何的读线程和写线程都可以键入
  2、假设临界区内有一个读线程,这时候信赖的read线程可以任意进入,但是写线程不能进入;
  3、假设临界区有一个写线程,这时候任何的读、写线程都不可以进入;
  4、假设临界区内有一个或者多个读线程,写线程不可以进入临界区,但是写线程也无法阻止后续的读线程继续进去,要等到临界区所有的读线程都结束了,才可以进入,可见:==rw(read/write)spinlock更加有利于读线程;==
  ==seqlock(顺序锁):==
  加锁逻辑:
  1、假设临界区内没有任何的thread,这个时候任何的读线程和写线程都可以键入
  2、假设临界区内没有写线程的情况下,read线程可以任意进入;
  3、假设临界区有一个写线程,这时候任何的读、写线程都不可以进入;
  4、假设临界区内只有read线程的情况下,写线程可以理解执行,不会等待,可见:==seqlock(顺序锁)更加有利于写线程;==3.6、spinlock的不足之处
  读写速度:CPU > 一级缓存 > 二级缓存 > 内存,因此某一个CPU0的lock修改了,其他的CPU的lock就会失效;那么其他CPU就会依次去L1 L2和主存中读取lock值,一旦其他CPU去读取了主存,就存在系统性能降低的风险;4、互斥体(mux)
  mutex用于互斥操作。
  互斥体只能用于一个线程,资源只有两种状态(占用或者空闲)4.1、特点
  1、mutex的语义相对于信号量要简单轻便一些,在锁争用激烈的测试场景下,mutex比信号量执行速度更快,可扩展
  性更好,
  2、另外mutex数据结构的定义比信号量小;、
  3、同一时刻只有一个线程可以持有mutex
  4、不允许递归地加锁和解锁
  5、当进程持有mutex时,进程不可以退出。
  • mutex必须使用官方API来初始化。
  • mutex可以睡眠,所以不允许在中断处理程序或者中断下半部中使用,例如tasklet、定时器等4.2、常用函数
  ==常见操作:==
  struct mutex mutex_1;
  mutex_init(&mutex_1);
  mutex_lock(&mutex_1)
  临界区代码;
  mutex_unlock(&mutex_1)
  ==常见函数:==mutex_lock(struct mutex*)  为指定的mutex上锁,如果不可用则睡眠 mutex_unlock(struct mutex*)  为指定的mutex解锁 mutex_trylock(struct mutex*)  尝试获取指定的mutex,如果成功则返回1;否则锁被获取,返回值是0 mutex_is_lock(struct mutex*)   如果锁已被征用,则返回1;否则返回0
  =

美国这次是不是要放下身段,对从中国进口的医疗用品免除关税?美国特朗普政府已对从中国进口的100多种医疗用品(包括口罩手套消毒湿巾等)免除进口关税美国这次是不是要放下身段?反思一下疫情刚爆发时对中国的过激反应?美国是不是因为这次疫情,第一次阿特兹异响频发,出现异响,全额退换成空头支票近日,汽车门网发布了8月的投诉数据,各级别车型的投诉指数排行也相继出炉。值得一提的是,在8月中型车投诉指数排行中,曾经深陷异响门的马自达阿特兹,再次因异响问题跃居投诉排行榜榜首。从秋天的第一台电脑,用一台完美的电脑来温暖你男朋友的小心心导读说实话老程实在不知道秋分这个节气竟然也要开始发红包了,照此下去,以后是不是冬至也要发红包,毕竟是入冬第一份火锅,然后夏至是不是要有入夏的第一份冰淇淋了,难道情人节,妇女节,儿童关于30系列显卡买不到,老程有话要说等等党们再坚持坚持导读这段时间很多小伙伴找我打听30系列显卡什么时候铺货到市场,关于这个问题,老程现在基本上会天天骚扰一下市场里面的经销商,尤其是影驰在临沂地区的代理商,基本上我每天都会发信息问一下闲鱼网吧倒闭价格699闲鱼上卖垃圾电脑的套路解析导读相信很多对电脑或者DIY电脑感兴趣的人都会在闲鱼上面搜索硬件,有些对电脑不太懂的小伙伴也可能上闲鱼找一些合适的电脑,这让很多组装电脑的JS们找到了一个省房租水电,并且没有宣传费一体机,一个尴尬电脑的产物,为什么一体机电脑发展不起来导读今天有小伙伴过来找我想装一台一体机,老程翻遍了自己的仓库也没有找到一个一体机的屏幕,突然才想起来自己已经好久没有组装一体机了,原来收的一些一体机屏幕已经卖给网上的魔改电商,他们孩视宝护眼台灯孩子护眼好帮手台灯作为家庭中学习和办公的必备工具之一,但是台灯的质量也参差不齐,有的动辄上千元的台灯买回来之后,发现还不如几百元的台灯护眼效果好。尤其家中有儿童的家庭选择更是慎重再慎重。根据资料华为路由WS5200四核版详细评测除了快还有这些功能华为路由WS5200自前年发行以后,就成为了300元以下路由器的爆款,加宽四天线MUMIMO多设备收发技术以及华为独有的5G优选技术的加成,无疑秒杀了竞争对手。但是这还不算完,华为华为路由Q2Pro子母路由器横评华为荣耀路由器大乱斗说起路由器,市场上的产品真是有点满目琳琅,同时价格也有高有低,头上有长角的也有不长角的,但是到底什么样的路由器才是好路由器,或者什么样的路由器才是最适合的路由器,毕竟每个家庭的房屋这款手机用了真的回不去了吗一加7Pro深度体验(上篇)一加系列的手机在发布之前都是极客范儿或者数码圈所必须关注,西装暴徒和不讲究的已经被牢牢地钉在了一加手机身上,那么这次在一加7Pro身上又有哪些新标签呢,想必大家应该都知道了,90H华为移动路由4G路由2Pro深度体验插网插卡随你选路由器算是家庭网络分享的必备工具,但是市面上大多数路由器都是有线连接,即使也有可随身携带的热点共享器,但是其不稳定的网络信号又成为了消费者们的痛点。那么有没有一款路由器,插上网线后
新手走向高手须掌握的Linux终端技巧快捷键Tab命令补全Ctrlc结束程序,但不终止终端Ctrla将光标移至输入行首,相当于Home键Ctrle将光标移至输入行末,相当于End键Ctrlk删除从光标所在位置到行末CtCyberTruck发布会曾演示失败,特斯拉公布防弹玻璃新专利IT之家5月31日消息据外媒TechTimes报道,特斯拉在近日披露了用于特斯拉电动皮卡上的防弹玻璃的新专利。该专利显示,电动皮卡的防弹玻璃采用新型多层玻璃堆结构,在2焦耳的冲击力iPhone13全新消息曝光,4000mAh120Hz高刷,还有40W快充很快就要到6月了,苹果已经确定了在6月举办WWDC开发者大会,届时将有可能发布全新的iOS15系统,而大众最关心的,自然还是iPhone13系列的相关消息了。目前,外媒再次曝光了i曙光!半导体材料开启国产替代元年!围堵下的中国芯提速了美国最近两年对中国的芯片产业极度打压,无论是华为还是中芯国际都多少受到冲击。这么做的目的也很明显,就是为了让中国的芯片技术被西方压制,让中国一直在半导体芯片上被卡脖子。只不过,中国浩祥科普什么是B端互联网的新存储体系?什么是B端互联网的新存储体系?(1)是信息基建(包括服务器IDC和存储等)(2)是网络安全,包括等保2。0带来的态势感知等新方向(3)互联网流量端和SaaS云化软件站在这个时点上,面板巨头京东方直接出手交互商用大屏这背后意图何在?据奥维云网统计,交互式液晶大屏在过去的2018年不仅在教育市场继续挤压投影产品的空间,而且在商务会议市场也是表现强势,成绩非常出众。2018年会议平板市场进入井喷期,市场销量规模将系统架构如何升级到https?一个脚本帮你搞定,且永久免费现在很多站长都会考虑将自己的站点从http升级到https,不仅是基于安全的考虑,有的也是因为第三方平台的限制,如谷歌浏览器会将http站点标记为不安全的站点,微信平台要求接入的微国内首份自动驾驶路测报告8车企跑15万公里未出事故日前,中国首份自动驾驶路测报告,由北京市交通委公安交通管理局经济和信息化局正式对外发布。在这份名为北京市自动驾驶车辆道路测2018年度工作报告的文件中,北京自动驾驶管理小组披露了8ToB领域风起云涌,中国创业进入下半场,残酷厮杀早有定数?如果用简单的行业术语来定义ToB和ToC,ToB就是面向企业推出的服务,ToC就是面向个人消费者推出的服务,两者付费客户的群体是截然不同的。C端市场的兴起归功于中国庞大的人口基数红锐龙2600平台用完之后,我是不打算再购买英特尔的处理器了本来很早就打算购买英特尔家的8700K处理器了,因为我手里之前为了体验RGB编程功能,特意购买了一块微星的Z370主板。但是很不巧,原以为8700K散片能降到2000元以下,没想到这项只有中国才拥有的专利,或将引领全球未来10年的厨房变革。近几年,科学技术发展日新月异,在各种新兴行业兴起的同时,也有一些科创公司深耕传统行业,为传统行业注入新鲜血液,甚至有可能实现大换血。智能厨房虚拟图厨房电器就是一个较为典型的传统行业