Windows® CE 系统中的同步机制

看到篇好文章,呵呵,独乐乐,不如众乐乐

 

本文转自http://blog.csdn.net/thl789/archive/2006/01/17/582246.aspx ,转载请注明出处

 

 

摘要 ... 1

目录 ... 1

一、 WinCE进程 /线程模型概览 ... 1

二、临界区( Critical Section) ... 2

三、互斥体( Mutex) ... 3

四、信号量( Semaphore) ... 4

五、事件( Event) ... 5

六、消息队列( MsgQueue P2P) ... 6

七、互锁函数( Interlocked Function) ... 8

八、 Wait函数 ... 8

总结 ... 10

参考资料以及进一步阅读 ... 10

关于作者 ... 10

 

一、 WinCE 进程 / 线程模型概览

WinCE 操作系统实现了进程 / 线程两级管理模型。连同内核进程和系统进程,以及应用进程一起, WinCE 共支持 32 个进程。进程还可以有自己的线程,进程默认有一个主线程,线程分为 256 个优先级别, WinCE 调度程序按照线程优先级高低来调度。进程是 WinCE 中最小资源分配的单元,线程是 WinCE 的最小调度单元。

本文讲述的同步机制有些只适用于线程间同步,有些既能用于线程间同步又能用于进程间同步,下面讨论到某一种机制的时候,再具体详述其适用场景。

二、临界区( Critical Section )

(本节内容适用于 WinCE 1.0 及以上版本 )

WinCE 实现了操作系统理论里的临界区管理。临界区内含有对临街资源的访问。通过对临界区进行有效管理,使得某一时刻最多只能有一个线程进入临界区,实现对临界资源的保护。

考虑下面用临界区实现两个线程对临界资源互斥访问的情形。 The 1st Thread 和 The 2nd Thread 都要调用 Func_CriticalSection() 函数,而 Func_CriticalSection() 内部会对某一临界资源进行操作,为了保护这一临界资源,我们用一个 WinCE 的 CriticalSection 来实现。

图一是该解决方案的一个场景。 The 1st Thread 和 The 2nd Thread 进入临界区之前已经创建( new )并初始化( InitializeCriticalSection() )了一个临界区。试图进入该临界区的线程首先必须获得进入该临界区(通过 EnterCriticalSection() / TryEnterCriticalSection() )的资格 ,如果临界区内没有线程,它就能进入,否则必须被挂起等待。进入临界区的线程可以对临界资源进行操作( OpOnSharedResources() )。操作完成之后退出临界区( LeaveCriticalSection () ),以允许其它线程进入。图一中第一个线程进入临界区还未退出之前,第二个线程因执行 EnterCriticalSection() 而 一直在被挂起等待,第一个线程退出临界区之后,第二个线程从等待中被唤醒,按照相应的调度机制重新竞争获得 CPU ,从而继续执行,完成临界区内的操作。

 图一、应用临界区(CriticalSection)实现同步

图一、应用临界区( CriticalSection )实现同步

利用临界区可以实现对临界资源的互斥操作, WinCE 的临界区应用在同一进程内,亦即实现的是同一进程内的线程间同步,不能应用在进程之间。

三、互斥体( Mutex )

(本节内容适用于 WinCE 1.01 及以上版本 )

互斥体( Mutex )顾名思义就是实现对共享资源实现互斥访问的。 WinCE 中的互斥体的使用规则如下(按线程之间的同步为例):

◇   互斥体可以是匿名互斥体也可以是命名互斥体;
◇   线程创建互斥体的时候可以指定创建完毕它是否就立即拥有该互斥体;
◇   某一时刻最多只有一个线程拥有给定的互斥体;
◇   拥有互斥体的线程可多次获得该互斥体;
◇   线程可用 CreateMutex 或 wait函数 来获得互斥体。

看下面应用互斥体的情景。 Thread1 创建并拥有了一个互斥体 g_hMutex[ 序列 1&2] 。互斥体 g_hMutex 是定义的全局量, thread2 可访问到, Thread2 用 WaitForSingleObject() 试图获得该互斥体,因为此时 g_hMutex 是被 Thread1 拥有的,所以 Thread2 被挂起 [ 序列 3] 。 Thread1 执行了一些操作之后,又用 wait 函数试图再次获得了该互斥体,因为此时 g_hMutex 的拥有者还是 Thread1 ,所以 Thread1 立即再次获得了该互斥体 [ 序列 4-6] 。 Thread1 对互斥体 g_hMutex 保护的共享资源操作完毕,释放该互斥体 [ 序列 7] ,但是因为 Thread1 两次获得了 g_hMutex ,所以 g_hMutex 的拥有权并没有交出。等到 Thread1 再次释放互斥体 g_hMutex[ 序列 8] 之后, Thread1 才失去了 g_hMutex 的拥有权, Thread2 可竞争 g_hMutex 的拥有权,如能成功拥有,就可从等待状态被唤醒,完成对共享资源的访问操作。

 图二、应用互斥体(Mutex)实现同步

图二、应用互斥体( Mutex )实现同步

不 知道从上面的描述,读者有又没有看出互斥体与临界区之间的区别。使用上,它们都实现的对共享资源的互斥访问,但是临界区是多个线程对同一段程序的执行,这 段程序会访问到临界资源,所以它们是同一个进程内的多个线程;而互斥体的应用情景是在线程之间独立执行,可以不是程序上的重叠,只是一个线程执行到共享资 源的时候,有可能别的线程也要访问该共享资源,所以要用互斥体来保护该共享资源。

由于互斥体上述的应用范围,它不但能应用在同一进程内的线程之间,也能应用在进程之间。进程之间可以通过命名互斥体来实现。一个进程通过为 CreateMutex() 指定一个名字做参数来获得已经存在的互斥体的句柄,处理过程如下面程序所示。

  HANDLE  hMutex;

  hMutex = CreateMutex (
                NULL,                       //
                FALSE,                      // Mutex object NOT initially owned
                TEXT("NameOfMutexObject")); // Muetx Name

  if (NULL == hMutex)
  {
    // Something wrong, deal with it here.
  }
  else
  {
    if ( ERROR_ALREADY_EXISTS == GetLastError () )
    {
      // CreateMutex() opened existing mutex."
      // ...
    }
    else
    {
      // CreateMutex() created new mutex."
      // ...
    }
  }

进程获得已经存在的互斥体的句柄之后,就可以如线程之间同步规则那样来实现进程之间的互斥体使用。

四、信号量( Semaphore )

(本节内容适用于 WinCE 3.0 及以上版本 )

信号量实体有一个数值指示当前该信号量使用情况,当前值的大小处于零和最大值之间。用下列操作原语实现信号量的同步操作(用线程间同步来说明):

◇ P ( S, num ) :如果信号量当前值减去 num 大于零,执行该操作的线程获得信号量,可继续执行,同时信号量的当前值减小 num ;否则访问线程被挂起等待
◇ V ( S, num ) :信号量的当前值增加 num (增加之后仍不大于最大值),如果有等待该信号量的线程被挂起,唤醒等待线程并按照相应的调度机制参与调度。

信号量一般用来控制某类共享资源,最大值标识该类资源的数目,执行 P 操作是申请一定数目这类资源, V 操作是释放一定数目的这类资源。在 WinCE 的信号量实现中,并未实现 OpenSemaphore , P 操作是用 wait函数 来实现的,而 V 操作由 ReleaseSemaphore 来实现。

看下面用信号量来控制数量为 2 的某类共享资源的使用情景。

图三、用信号量(Semaphore)实现同步  

图三、用信号量( Semaphore )实现同步

Thread1 创建一个控制 2 个共享资源的信号量 [ 序列 1&2] ,并且自己用 WaitForSingleObject() 来申请一个资源,因为当前可用的这类资源有 2 个,所以它就获得了其中的一个 [ 序列 3&4] 。同样地, Thread2 获得了另外一个资源 [ 序列 5&6] 。但是当 Thread3 也申请这类资源的时候,因为此时已经没有这类资源,信号量的值为零,它就被挂起 [ 序列 7] 。拥有这类资源的线程释放掉一个资源 [ 序列 8&9] ,并且满足能满足 Thread3 申请资源数目的要求, Thread3 竞争获得了该资源 [ 序列 10] 。

信号量是实现同步的基本方法,在几乎所有的多任务操作系统里面都做了信号量的实现,其它一些同步机制其实可以通过信号量来实现。如果把信号量的最大值和初始值均设置为 1 ,那么它就可实现互斥体 ,即保证对共享资源互斥访问的保护。如果把信号量的初始值设置为 0 ,等待别的线程 ReleaseSemaphore 来唤醒它,那么它就可实现事件( Event ) 机制。

信号量机制可以用在同一进程内的线程之间同步,也可以用在进程之间的同步。进程间同步的实现方法如同互斥体的此类实现。

五、事件( Event )

(本节内容适用于 WinCE 1.0 及以上版本 )

WinCE 系统中广泛用到事件( Event )机制来实现线程之间的协调工作,具体表现在:

◇ 通知一个线程什么时候去执行它的特定的任务
◇ 标识事件的发生

WinCE 中的线程操作原语有 CreateEvent() , SetEvent()/PulseEvent() , ResetEvent() 等。创建 Event 的时候在 CreateEvent() 的参数中指定 Event 的初始状态(触发的 / 未触发的),还要指定事件是否手动复位(手动复位是只有用 ResetEvent() 才能把事件状态显式地设置为未触发的,自动复位是等待该事件的线程等待事件到来之后,系统自动把该事件的状态复位为未触发的)。线程等待事件仍然用 wait函数 。

下面是使用 Event 同步的简单情况:

 图四、用事件(Event)实现同步

图四、用事件( Event )实现同步

线程 Thread1 执行过程中,要等到某个条件满足(事件触发),所以它创建了一个事件 Event (参数设置为:手动复位,初始条件为未触发的),用 WaitForSingleObject() 来等待这个事件。线程 Thread2 执行了一些操作之后,满足了 Thread1 的条件,用 SetEvent 来触发该事件。

       除了可以用 SetEvent() 来触发事件之外,也可以用 PulseEvent() 来触发,区别是 PulseEvent() 触发该事件之后把它又复位。

       另外,也可以把命名事件用于进程之间的同步。实现方法同互斥体中的描述。

六、消息队列( MsgQueue P2P )

(本节内容适用于 WinCE.net 4.0 及以上版本 )

消息队列通信机制如同建立了一个管道,管道的双方通过分别建立到管道的两端,与管道的读端口建立连接的进程可以从该端口读取消息( Message ),与管道的写端口建立连接的进程可以写入消息( Message )到管道。管道内消息组成了一个 FIFO ( F irst I n F irst O ut )的队列,从读端口读取消息是读取队列的头,写入消息到写端口是在队列尾部追加一个消息。

WinCE 中关于 MsgQueue 的操作函数主要有:

◇ CreateMsgQueue() 创建一个消息队列。在该函数的参数中指定消息队列的名字,消息队列的最大数目,每个消息的最大长度,对该消息队列可进行读还是写操作等。因为调用一次 CreateMsgQueue 函数,只能指定读或者写这样的二选一的消息队列,所以一般需要用相同的消息队列名字做参数两次调用该函数,分别创建读消息队列和写消息队列,它们的返回值分别被读进程和写进程用 OpenMsgQueue() 打开用于读取消息和写入消息。
◇ OpenMsgQueue() 打开消息队列并建立与相应端口的连接。进程与读端口建立连接之后,可用返回的句柄从消息队列中读取消息;进程与写端口建立连接之后,可用返回的句柄写入消息到消息队列中。
◇ CloseMsgQueue() 断开与消息队列相应的端口之间的连接,并关闭由 CreateMsgQueue() 或 OpenMsgQueue() 创建或打开的消息队列。
◇ ReadMsgQueue() 如同从普通文件中读取数据一样,用于从消息队列中读取消息。可以指定读取消息时,如果消息队列为空,读进程是被挂起还是直接返回。
◇ WriteMsgQueue() 如同写数据到普通文件中一样,用于写消息到消息队列中。可以指定写入消息时,如果消息队列已满,写进程是被挂起还是直接返回。

下图是 MsgQueue 应用的典型场景。

 图五、用消息队列(MsgQueue)实现同步

图五、用消息队列( MsgQueue )实现同步

这种场景下的执行过程为:

◇ 主进程 MainProcess 创建了名为“ Reader/Writer MsgQueue ”的读和写的消息队列,并分别返回 hMsgQ_r_m 和 hMsgQ_w_m[ 序列 1-4] 。
◇ 读进程 ReaderProcess 以主进程的 ProcessId 和 hMsgQ_r_m 为参数,通过 OpenMsgQueue() 与 MainProcess 消息队列的读端口建立连接 [ 序列 5&6] 。
◇ ReaderProcess 与消息队列建立连接之后,用 WaitForSingleOnject(hMsg_r) 看消息队列中是否有消息,因为此时消息队列为空,所以 ReaderProcess 被挂起 [ 序列 7] 。
◇ 写进程 WriterProcess 以主进程的 ProcessId 和 hMsgQ_w_m 为参数,通过 OpenMsgQueue() 与 MainProcess 消息队列的写端口建立连接 [ 序列 8&9] 。
◇ WriterProcess 与消息队列建立连接之后,用 WaitForSingleOnject(hMsg_w) 看消息队列中消息是否满,因为此时消息队列为空,未满,所以 WriterProcess 不会被挂起 [ 序列 10&11] 。
◇ WriterProcess 写消息到消息队列中 [ 序列 12&13] 。
◇ 因为消息队列中已经有了消息, ReaderProcess 从挂起状态被唤醒 [ 序列 14] 。
◇ ReaderProcess 继续执行,从消息队列中读取 WriterProcess 刚才写入的消息。

消息队列除可用于同步之外,主要用于进程之间的数据传递,另外消息队列也可以用于同一进程中的线程之间同步,但是既然线程之间能直接传递数据,又何必那么麻烦呢。

七、互锁函数( Interlocked Function )

(本节内容适用于 WinCE 1.0 及以上版本)

除了上面各节的同步方法之外, WinCE 还提供了一些用于原子操作的互锁函数,这些函数在执行过程中,不会因为线程的调度引起的当前线程被抢占而打断函数内的操作。

这些函数主要有:

InterlockedIncrement
InterlockedDecrement
InterlockedExchange
InterlockedTestExchange
InterlockedCompareExchange
InterlockedCompareExchangePointer
InterlockedExchangePointer
InterlockedExchangeAdd

八、 Wait 函数

(本节内容适用于 WinCE 1.0 及以上版本 )

Wait 函数不是特指的某一个函数,而是指 wait 的系列函数。 wait 函数并不是 WinCE 同步机制中的一种,但是 WinCE 的很多同步机制要用到 wait 函数,这些在前面讲述各个同步方法的时候也已有论述。

一般地,执行 wait 函数时,如果等待的同步对象条件不满足,那么执行 wait 函数的进程 / 线程会被挂起,当然也可以给它们设置等待的超时时间,超过给定时间,不管条件是否满足,它们会自动从等待状态苏醒。等待既可以等待某一个条件,也可以等待多个条件中的一个, WinCE 不支持等待多个条件同时满足,如果有这种需要,要自己实现。

Wait 函数原型如下:

DWORD WaitForSingleObject (HANDLE hHandle, DWORD dwMilliseconds );

DWORD WaitForMultipleObjects (
  DWORD nCount,              // No. of object handles in the array.
  CONST HANDLE* lpHandles,   // Pointer to an array of object handles.
  BOOL fWaitAll,             // MUST be FALSE in WinCE
  DWORD dwMilliseconds       // Timeout (0, mills, or INFINITE)
);

DWORD MsgWaitForMultipleObjects (
  DWORD nCount,             // No. of object handles in the array.
  LPHANDLE pHandles,        // Pointer to an array of object handles.
  BOOL fWaitAll,            // MUST be FALSE in WinCE
  DWORD dwMilliseconds,     // Timeout (0, mills, or INFINITE)
  DWORD dwWakeMask          // Input types for which an input event object handle
);

前面讲述各种同步机制的时候都是以 WaitForSingleObject() 来说明的,这里就不再赘述它了。

WaitForMultipleObjects() 和 MsgWaitForMultipleObjects() 可以用来等多个同步对象,它们之间的区别就是 MsgWaitForMultipleObjects() 还等待 dwWakeMask 参数中指定的输入事件,即这些事件发生时,等待的进程 / 线程也能被唤醒。

用 WaitForMultipleObjects() 等待的多个同步对象的句柄放在参数 lpHandles 数组中,同步对象的句柄的数目放在参数 nCount 中。 dwMilliseconds 指定了等待的超时参数:如果指定为 0 ,该函数等待每个同步对象之后,不管触发与否都直接返回;如果指定为 INFINITE ,该函数等待每个同步对象,直到有一个同步对象被触发,否则执行该函数的运行实体将一直被挂起;如果指定为非 0 ,非 INFINITE 的一个数值,那么不管 等待的同步对象是否被触发,到了指定的时间,执行该函数而被挂起的运行实体也会被唤醒。因哪个同步对象被触发而返回还是因超时而返回,可以从返回值中来判定,返回值为 WAIT_TIMEOUT ,是因为超时; 返回值为 WAIT_OBJECT_0 到 WAIT_OBJECT_0 + nCount -1 之间的数时,可以按顺序找到具体那个同步对象被触发 。

下面是 WaitForMultipleObjects 的典型应用。

  HANDLE hSynchObjects[EVENT_COUNT];
  DWORD dwEvent;

  /* Put event handles in hEvents */
  // ...
 
  dwEvent = WaitForMultipleObjects (
                      EVENT_COUNT,        // Number of objects in an array
                      hSynchObjects,      // Array of objects
                      FALSE,              // MUST be FALSE
                      500);               // timeout, 0.5s

  switch (dwEvent)
  {
    case WAIT_TIMEOUT:
      // Handle for timeout
      break;
     
    case WAIT_OBJECT_0 + 0:
      // Handle the 1st event
      break;
     
    case WAIT_OBJECT_0 + 1:
      // Handle the 2nd one
      break;

    ...
   
    case WAIT_OBJECT_0 + EVENT_COUNT -1:
      // Handle the final one
      break;

    default:
      // Error: Not an anticipant one, handle it.
      break;
  }

总结

本文探讨了 WinCE 中的各种同步机制的用法,并给出了它们的典型应用场景。关于它们进一步的高级话题,将在后续文章中探讨。

参考资料以及进一步阅读

1) MSDN
2) UML Reference Manual, 2nd Edition
3 ) Abraham Silberschatz, Peter Baer Galvin, Greg Gagne. Operating System Concepts, 6th Edition. John Wiley & Sons, Inc/ 高等教育出版社影印 , 2002.5
4
) David R. Butenhof/ 于磊,曾刚 . Programming with POSIX Threads. Addison Wesley/ 中国电力出版社 , 2003

 

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

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

相关文章

Nova Suspend/Rescue 操作详解 - 每天5分钟玩转 OpenStack(35)

本节我们讨论 Suspend/Resume 和 Rescue/Unrescue 这两组操作。 Suspend/Resume 有时需要长时间暂停 instance,可以通过 Suspend 操作将 instance 的状态保存到宿主机的磁盘上。当需要恢复的时候,执行 Resume 操作,从磁盘读回 instance 的状态…

关于奇偶校验

关于奇偶校验 奇校验(Odd Parity):所有传送的数位(含字符的各数位和校验位)中,“1”的个数为奇数,如: 1 0110,0101 0 0110,0101 偶校验(Even Parity):所有传送的数位&am…

More C++ Idioms

http://en.wikibooks.org/wiki/More_C%2B%2B_Idioms

Waveform Audio 驱动(Wavedev2)之:WAV API模拟

Waveform Audio 驱动(Wavedev2)之:WAV API模拟 Waveform 驱动对Windows Mobile来说是一个非常重要的驱动,控制着所有有关声音的操作,包括喇叭、耳机、麦克、听筒等。 要 想对驱动的整个架构和流程都非常的了解,我们必须从…

.NET Core计划弃用project.json

Microsoft最终宣布project.json实验失败,将转回使用.csproj文件。但是转变不会马上发生,最近发布的.NET Core RC2(又称tooling preview 1)将继续使用.xproj 以及project.json。\u0026#xD;\n\u0026#xD;\n从.NET Core RTM/tooling p…

移位运算与乘法

移位运算与乘法 题目描述 已知d为一个8位数,请在每个时钟周期分别输出该数乘1/3/7/8,并输出一个信号通知此时刻输入的d有效(d给出的信号的上升沿表示写入有效) 信号示意图 波形示意图 timescale 1ns/1ns module multi_sel( input [7:0]d …

双极型数字集成电路

在双极型数字集成电路中,除了TTL电路以外,还有二极管-三极管逻辑(Diode-Transistor Logic,简称DTL)、高阈值逻辑(High Threshold Logic,简称HTL)、发射极耦合逻辑(Emitter Coupled Logic,简称ECL)和集成注入…

Snapshot Instance 操作详解 - 每天5分钟玩转 OpenStack(36)

本节我们通过日志详细讨论 instance 的 snapshot 操作。 有时候操作系统损坏得很严重,通过 Rescue 操作无法修复,那么我们就得考虑通过备份恢复了。当然前提是我们之前对instance做过备份。 Nova 备份的操作叫 Snapshot,其工作原理是对 insta…

Rebuild Instance 操作详解 - 每天5分钟玩转 OpenStack(37)

上一节我们讨论了 snapshot,snapshot 的一个重要作用是对 instance 做备份。 如果 instance 损坏了,可以通过 snapshot 恢复,这个恢复的操作就是 Rebuild。 Rebuild 会用 snapshot 替换 instance 当前的镜像文件,同时保持 instanc…

突发传输模式

突发传输模式 突发传输(Burst transmission),一般也称为数据突发,其在通信领域中一般指在短时间内进行相对高带宽的数据传输。 突发传输一般表示的是两个设备之间进行数据传送的一种模式,也可将其称为突发模式下的数据传输。而突发(Burst)是指在同一行中相邻的存储单元…

Waveform Audio 驱动(Wavedev2)之:WAV 驱动解析

Waveform Audio 驱动(Wavedev2)之:WAV 驱动解析 上篇文章中,我们模拟了WAV API。现在进入我们正在要解析的Wave 驱动的架构。我们了解一个驱动的时候,先不去看具体跟硬件操作相关的东西,而是从流程入手,把整个流程搞清…

Perl参考书籍

Effective Perl programming - Writing Better Pro..> Network_Programming_With_Perl.chm OReilly,.Learning.Perl.(2005),.4Ed.BBL.LotB.chm PERL语言编程.pdf Perl DBI编程.pdf …

Unshelve Instance 操作详解 - 每天5分钟玩转 OpenStack(39)

上一节我们 shelve instance 到 Glance,本节讨论如何通过 unshelve 操作恢复该 instance。 因为 Glance 中保存了 instance 的 image,unshelve 的过程其实就是通过该 image launch 一个新的 instance,nova-scheduler 也会调度合适的计算节点来…

位拆分与运算

位拆分与运算 题目描述 现在输入了一个压缩的16位数据,实际上包含了四个数据[3:0][7:4][11:8][15:12] 按照sel选择输出四个数据的相加结果,并输出valid_out信号(在不输出时候拉低) 0:输出[3:0][7:4] 1:输出…

得意而忘乎形:谈葛水平的水墨画

不知道葛水平早年学过画没有,只知道她搞过戏曲,好像也有过舞台表演的经验。现在主要写小说,成绩斐然,创作之余画水墨。贾平凹看了她的画后说:“每个人都有绘画潜质,只是大与小和开发与不开发。”这话有理。…

Eclipse安装Perl插件

http://www.cnblogs.com/emanlee/archive/2012/08/11/2633701.html Eclipse安装Perl EPIC插件,在Eclipse中开发Perl项目 前提: 已经安装了 Eclipse,Java,Perl(例如 ActivePerl)。 步骤: 启动Eclipse,Eclipse中&#x…

初识Android应用程序的五大基本组件

5 Basic Components 1. Activity 2. Service 3. Broadcast Receiver 4. Content Provider 5. Intent Activity —— 应用表示层( 基类 Activity ) 应…

风鬣霜蹄马王出

今年夏天,在内蒙古莱德马业繁殖基地的母马放养草场,我看到一群特殊的马点缀在绿色的草原之中。它们的颜色令我大开眼界:黑、白、枣红、黄骠、铁青……远远望去,五颜六色,神态各异。莱德马业的负责人朱方清告诉我,这里除…

多功能数据处理器

多功能数据处理器 题目描述 根据指示信号select,对输入信号a,b实现不同的运算。输入信号a,b为8bit有符号数,当select信号为0,输出a;当select信号为1,输出b;当select信号为2,输出ab;当select信号为3&#x…

Live Migrate 操作 - 每天5分钟玩转 OpenStack(42)

Migrate 操作会先将 instance 停掉,也就是所谓的“冷迁移”。而 Live Migrate 是“热迁移”,也叫“在线迁移”,instance不会停机。 Live Migrate 分两种: 源和目标节点没有共享存储,instance 在迁移的时候需要将其镜像…