【《现代操作系统 第4版》】5、进程间的通信之同步

操作系统 --- 进程通信(IPC) 目录一、进程间的通信? 二、为什么进程间通信需要操作系统支持?三、进程间通信的方法 3.1 共享存储3.2 消息传递(消息队列)3.2.1 直接通信方式【点名道姓的消息传递】3.2.2 间接通信方式【以“信箱”作为中间实体进行消息传递】3.3 管道通信(FIFO)【读写数据自由度没有共享内存方式高】 四、总结 进程通信是指进程之间的信息交换。 定义:进程间通信(Inter-Process Communication,IPC)是指两个进程之间产生数据交互。 举例:进程是分配系统资源的单位(包括内存 阅读详情

同步与互斥

为了克服并发问题中的竞态条件引入了锁机制从而使得多个进程之间能够互斥的访问共享资源。
而为了实现进程间的同步又引入了信号量和管程等模型。
互斥与同步

生产者-消费者问题

考虑这样一个问题,有两个进程共享一个公共的固定大小的缓冲区。其中一个是生产者进程,一个是消费者进程。生产者进程向缓冲区中写入数据,消费者进程从缓冲区中消费数据。当生产者将数据写入缓冲区的时,如果缓冲区已满则让其睡眠,等到消费者从缓冲区中取出一个或多个数据项时再唤醒它。同样的,当消费者试图从缓冲区中取数据,如果缓冲区为空则让其睡眠,等到生产者向其中放入一个新的数据再唤醒它。

#define N 100			/* 缓冲区 slot 槽的数量 */
int count = 0			/* 缓冲区数据的数量 */							
  
// 生产者
void producer(void){
  int item;
   
  while(TRUE){				    
    item = produce_item()	/* 生成下一项数据 */			   
    if(count == N) {		/* 如果缓存区是满的,就会阻塞 */
      sleep();									
    }
        
    insert_item(item);		/* 把当前数据放在缓冲区中 */    
    count = count + 1;		/* 增加缓冲区 count 的数量 */			
    if(count == 1){ 		/* 缓冲区是否为空? */
      wakeup(consumer);					
    }
  }
}

// 消费者
void consumer(void){ 
  int item;
  
  while(TRUE){    
  	if(count == 0){         /* 如果缓冲区是空的,就会进行阻塞 */				
      sleep();
    }    
      
   	item = remove_item();	/* 从缓冲区中取出一个数据 */		    
    count = count - 1		/* 将缓冲区的 count 数量减一 */    
    if(count == N - 1){		/* 缓冲区满嘛? */			
      wakeup(producer);		
    }
   
    consumer_item(item);	/* 打印数据项 */			
  }  
}

假设 sleep 和 wakeup 代表的是进程间的通信中的睡眠和唤醒原语。在不允许进程进入临界区之前执行 sleep 会阻塞进程而不是忙等待。
上面代码中会产生竞争条件,因为 count 是没加锁的共享资源。

有可能出现下面这种情况:缓冲区为空,此时消费者刚好读取 count 的值发现它为 0 。此时调度程序决定暂停消费者并运行生产者。生产者生产了一条数据并把它放在缓冲区中,然后增加 count 的值, 随后生产者调用 wakeup 来唤醒消费者。但是,消费者此时在逻辑上并没有睡眠(还未执行sleep就被切换了),所以 wakeup 信号会丢失。当消费者下次启动后,它会查看之前读取的 count 值,发现它的值是 0 ,然后在此进行睡眠。不久之后生产者会填满整个缓冲区从而睡眠,这样两个进程就会永远睡眠了。

引起上面问题的本质是 唤醒尚未进行睡眠状态的进程会导致唤醒丢失。如果它没有丢失,则一切都很正常。一种快速解决上面问题的方式是增加一个唤醒等待位(wakeup waiting bit)。当一个 wakeup 信号发送给仍在清醒的进程后,该位置为 1 。之后,当进程尝试睡眠的时候,如果唤醒等待位为 1 ,则该位清除,而进程仍然保持清醒。但是,如果进程数量很多,就需要加入更多的唤醒等待位,从根本上说这其实没有解决问题。

信号量(semaphore)

初识信号量

信号量是E.W.Dijkstra在1965年提出的一种方法,它使用一个取值范围大于等于0的整型变量(sem)来表示系统资源的数量。它既可以实现互斥,又可以实现同步。上例中的显示器就是信号量的一种互斥的实现。

信号量通常有两种原子操作——P 和 V

  • P
    • sem 减1
    • 若 sem<0,则进程将睡眠,否则继续执行
  • V
    • sem 加1
    • 若 sem≤0,表示现在有一个或多个进程在该信号量上睡眠,则唤醒一个等待进程

举个生活的例子来解释下:
在火车上上厕所时,如果厕所是空,门上的显示器就会显示"无人",如果有人进去并把门锁上后,门上显示器就变为"有人",这样后来者看到门上显示"有人"就会在外等待而不会冒然闯进来。

这其实就是使用信号量实现的一种互斥方式,其中门上的显示器就扮演了"信号量"的角色。它的值为1,A看到显示器的标识为“无人”(sem=1)就进入厕所并锁上了门(A进行一次P操作,sem = 0);后面的B也想上厕所,但是看到了显示器上“有人”这个标识(B进行一次P操作,sem = -1),所以B会等待。当A出来时(A进行一次V操作,sem = 0),这时B就会被唤醒,继续执行了。

信号量的特征

  • 信号量是被保护的整数变量
    • 初始化完成后,只能通过 P 和 V 操作修改
    • 由操作系统保证,PV操作是原子操作
  • P 可能阻塞,V 不会阻塞
  • 通常假定信号量是“公平的”
    • 线程不会被无限期阻塞在 P 操作
    • 假定信号量等待按先进先出排队

信号量的实现

classSemaphore {
	int sem;
	WaitQueue q; 
}

Semaphore::P() {
   sem--;
   if (sem < 0) {
        Add this thread t to q;
        block(p);
     }
}

Semaphore::V() {
    sem++; 
    if (sem <= 0) {
        Remove a thread t from q;
        wakeup(t);        
    }
}

信号量的分类

  • 可分为两种信号量
    • 二进制信号量:资源数目为0或1。常用于实现互斥访问。
    • 计数信号量:资源数目为任何非负值。常用于实现条件同步(一个进程等待另一个进程的事件发生)

信号量实现互斥访问

每个临界区设置一个信号量,其初值为1。这种实现互斥的信号量又叫做互斥量(mutex)。

mutex = new Semaphore(1);

必须成对使用P、V 操作,且PV操作不能次序错误、重复或遗漏

mutex->P(); // P操作保证互斥访问临界资源
Critical Section;
mutex->V();	//V操作在使用后释放临界资源

信号量实现条件同步

每个条件同步设置一个信号量,其初值为0

mutex = new Semaphore(0);

同步
线程A先进行P操作后被阻塞,随后线程B执行完事件后进行V操作,唤醒线程A,这样就实现了线程A等待线程B的事件先发生。

用信号量解决有界缓冲区的生产者-消费者问题

对上例的生产者-消费者问题进行简单的归纳:

  • 一个或多个生产者在生成数据后放在一个缓冲区里
  • 单个消费者从缓冲区取出数据处理
  • 任何时刻只能有一个生产者或消费者可访问缓冲区

问题分析

  • 任何时刻只能有一个线程操作缓冲区(互斥访问)
  • 缓冲区空时,消费者必须等待生产者(条件同步)
  • 缓冲区满时,生产者必须等待消费者(条件同步)

代码实现

信号量
#define N 100 			/* 定义缓冲区槽的数量 */
typedef int semaphore; 	/* 信号量是一种特殊的 int */
semaphore mutex = 1; 	/* 二进制信号量mutex,控制临界区的访问 */
semaphore empty = N; 	/* 计数信号量empty,统计 buffer 空槽的数量 */
semaphore full = 0; 	/* 计数信号量full,统计 buffer 满槽的数量 */
生产者
void producer(void){ 
  int item;  
    
  while(1){				
    item = producer_item();	 /* 产生放在缓冲区的一些数据 */	
    p(&empty);	 			 /* N表示可以由多个生产者进行P操作  */
    p(&mutex);	    		 /* 进入关键区域  */
    insert_item(item);  	 /* 把数据放入缓冲区中 */
    v(&mutex);	    		 /* 离开临界区 */
    v(&full);			    /* 将 buffer 满槽数量 + 1 */								
  }
}
消费者
void consumer(void){
  int item;
   
  while(1){    
    p(&full);				/* 缓存区满槽数量 - 1 */    
    p(&mutex);				/* 进入缓冲区 */	    
    item = remove_item();	/* 从缓冲区取出数据 */    
    v(&mutex);				/* 离开临界区 */    
    v(&empty);				/* 将空槽数目 + 1 */    
    consume_item(item);		/* 处理数据 */							
  }  
}

注意事项

**注意P操作的顺序:**实现互斥的P操作一定要在实现同步的P操作之后,否则可能引发“死锁”。

假设将生产者代码中的两个 p 操作交换一下次序,即先 p(&mutex) 后 p(&empty)。如果缓冲区完全满了, p(&mutex) 后 mutex 为0,p(&empty) 后为 -1,生产者将阻塞。这样一来,当消费者下次试图访问缓冲区时,再进行 p(&mutex) 时, 由于生产者阻塞未执行 v(&mutex) 操作,使得 mutex 的值此时为 0,消费者执行 p(&mutex) 后导致消费者也将阻塞。两个进程都将永远地阻塞下去,无法再进行有效的工作,这种不幸的状况称作死锁(dead lock)。

信号量的缺陷

  • 理解和编写代码比较困难
  • 容易出错(比如使用的信号量已经被另一个线程占用或忘记释放信号量)
  • 不能够处理死锁问题

总的来说,直接使用信号量来编程对人的要求比较高。为了降低这种复杂度,计算机科学家引入了管程模型。

管程(monitor)

初识管程

管程是一个由过程、变量及数据结构等组成的一个集合。任何时候管程中只能有一个活跃的进程,正在管程中的进程可临时放弃管程的互斥访问,等待事件出现时恢复。

通常情况下,当进程调用管程中的程序时,该程序的前几条指令会检查管程中是否有其他活跃的进程。如果有的话,调用进程将被挂起,直到另一个进程离开管程才将其唤醒。如果没有活跃进程在使用管程,则该调用进程可直接进入。

通过临界区自动的互斥,管程比信号量更容易保证并行编程的正确性。但是管程是某些编程语言的特性,由编译器来识别并用某种方式对其互斥作出保证。JAVA有管程,但是C、Pascal 以及大多数其他编程语言都没有管程。

管程的使用

在生产者-消费者问题中,很容易将针对缓冲区满和缓冲区空的测试放到管程过程中,但是生产者在发现缓冲区满的时候如何阻塞呢? 为了使进程在无法继续运行时被阻塞,管程模型引入了条件变量以及相关的两个操作 wait 和 signal。

当一个管程程序发现它不能运行时(例如,生产者发现缓冲区已满),它会在某个条件变量(如 full)上执行 wait 操作。这个操作造成调用进程阻塞,并且还将另一个以前等在管程之外的进程调入管程。另一个进程,比如消费者可以通过执行 signal 来唤醒阻塞的调用进程。如果在一个条件变量上有若干进程都在等待,则在对该条件执行 signal 操作后,系统调度程序只能选择其中一个进程恢复运行。

条件变量不是计数器。条件变量也不能像信号量那样积累信号以便以后使用。所以,如果向一个条件变量发送信号,但是该条件变量上没有等待进程,那么信号将会丢失。也就是说,wait 操作必须在 signal 之前执行。

wait 和 signal 操作看起来像是前面提到的 sleep 和 wakeup 。但是 sleep 和 wakeup 之所以会失败是因为当一个进程想睡眠时,另一个进程试图去唤醒它。使用管程则不会发生这种情况。管程程序的自动互斥保证了这一点,如果管程过程中的生产者发现缓冲区已满,它将能够完成 wait 操作而不用担心调度程序可能会在 wait 完成之前切换到消费者。甚至,在 wait 执行完成并且把生产者标志为不可运行之前,是不会允许消费者进入管程的。

管程的组成

  • 锁:控制管程代码的互斥访问
  • 0或者多个条件变量:管理共享数据的并发访问
    管程的组成
    由于管程是互斥的,进入不到管程的进程会被挂在entry queue上。进入管程中的进程在进行操作时,有可能对某个共享变量的操作由于某个条件得不到满足而无法进行,此时该进程就会被挂到该条件变量的等待队列上,并释放锁,使得其他入口队列中的进程进入管程。

条件变量

  • 条件变量是管程内的等待机制
    • 进入管程的线程因资源被占用而进入等待状态
    • 每个条件变量表示一种等待原因,对应一个等待队列
  • Wait()操作
    • 将自己阻塞在等待队列中
    • 唤醒一个等待者或释放管程的互斥访问
  • Signal()操作
    • 将等待队列中的一个(或全部)进程唤醒
    • 如果等待队列为空,则等同空操作

条件变量的实现如下:
条件变量的实现
由于进入管程的进程一定是拥有锁的进程,所以在进行wait操作时,等待队列中的数量加1,并将进程添加到等待队列,同时会释放该进程一样的锁,这样再后面进行调度的时候,被选中的其他进程才能获得锁,进入管程中。在 signal 操作中,如果在条件变量的等待队列中没有任何进程,则该操作相当于什么也没做。

在管程中,整形变量 numWaiting 表示当前挂在条件变量上的等待队列中的进程数,而semaphore表示的是信号量的个数。
在管程中,整形变量增加后不一定会有对应的减法操作,但是信号量的PV操作是一定会成对出现,有加必有减。

用管程解决生产者-消费者问题

C++实现

Class BoundedBuffer {
    …
    Lock lock;
    int count = 0;
    Condition notFull, notEmpty;
}

BoundedBuffer 类就是一个管程,它定义了一把锁和两个条件变量,并用count来记录缓冲区的大小。

BoundedBuffer::Producer(c) {
    lock->Acquire();
    while (count == n)
        notFull.Wait(&lock);
    Add c to the buffer;
    count++;
    notEmpty.Signal();
    lock->Release();
}

管程的定义决定了进入整个代码块时必须是互斥的,如果是用信号量则只需要在进行" Add c to the buffer;"操作的前后进行PV操作即可。
总体上和信号量的实现差不多,只是一些细节上有些不同。

BoundedBuffer::Consumer(c) {
    lock->Acquire();
    while (count == 0)    
      notEmpty.Wait(&lock);
    Remove c from buffer;
    count--;
    notFull.Signal();
    lock->Release();
}

Java实现

生产者

class Producer extends Thread {
    private Our_monitor mon;
	Producer (Our_monitor mon) {
		this.mon = mon;
	}
  
    public void run(){											// run 包含了线程代码
      int item;
      while(true){												// 生产者循环
        item = produce_item();
        mon.insert(item);
      }
    }
    private int produce_item(){...}						        // 实际生产
  }

消费者

class Consumer extends Thread {
    private Our_monitor mon;
   	Consumer (Our_monitor mon) {
		this.mon = mon;
	}
    public void run( ) {										 // run 包含了线程代码
   	  int item;
      while(true){
        item = mon.remove();
		consume_item(item);
      }
    }
    private int produce_item(){...}						        // 实际消费
  }

管程

class Our_monitor {									
	static final int N = 100;								    // 定义缓冲区大小
    private int buffer[] = new int[N];
    private int count = 0,lo = 0,hi = 0;			            // 计数器和索引
    
    private synchronized void insert(int val){
      if(count == N){
        go_to_sleep();											// 如果缓冲区是满的,则进入休眠
      }
	  buffer[hi] = val;									        // 向缓冲区插入内容
      hi = (hi + 1) % N; 										// 找到下一个槽的为止
      count = count + 1;										// 缓冲区中的数目自增 1 
      if(count == 1){
        notify();												// 如果消费者睡眠,则唤醒
      }
    }
    
    private synchronized void remove(int val){
      int val;
      if(count == 0){
        go_to_sleep();											// 缓冲区是空的,进入休眠
      }
      val = buffer[lo];											// 从缓冲区取出数据
      lo = (lo + 1) % N;										// 设置待取出数据项的槽
      count = count - 1;										// 缓冲区中的数据项数目减 1 
      if(count = N - 1){
        notify();												// 如果生产者睡眠,唤醒它
      }
      return val;
    }
    
    private void go_to_sleep() {
      try{
        wait( );
      }catch(Interr uptedExceptionexc) {};
    }
  }      

主应用

public class ProducerConsumer {
  Our_monitor mon = new Our_monitor();      
  Producer p = new Producer(mon);					        // 初始化一个生产者线程
  Consumer c = new Consumer(mon);					        // 初始化一个消费者线程
   
  public static void mian(String[] args) {
  	p.start();
  	c.start();
  }
}

Our_monitor 中包含了缓冲区、管理变量以及两个同步方法。当生产者在 insert 内活动时,它保证消费者不能在 remove 方法中运行,从而保证更新变量以及缓冲区的安全性,并且不用担心竞争条件。变量 count 记录在缓冲区中数据的数量。变量 lo 是缓冲区槽的序号,指出将要取出的下一个数据项。类似地,hi 是缓冲区中下一个要放入的数据项序号。允许 lo = hi,含义是在缓冲区中有 0 个或 N 个数据。

Java 中的同步方法与其他经典管程有本质差别:Java 没有内嵌的条件变量。
然而,Java 提供了 wait 和 notify 分别与 sleep 和 wakeup 等价。

Hansen 管程与 Hoare 管程

当调用signal唤醒等待队列中的进程后,管程中的进程就会有2个或以上,为了保持管程中的互斥性,此时应该保留哪个呢?

Brinch Hansen 和 Hoare 在对进程唤醒上有所不同,Hoare 建议让新唤醒的进程继续运行,而挂起其他进程。而 Brinch Hansen 建议让执行 signal 的进程必须先退出管程后再运行其他进程。通常采用 Brinch Hansen 的建议,因为它在概念上更简单,并且更容易实现。
进程唤醒

Hansen 管程

Hansen-style :Producer(){
  lock->acquire();
  while (count == n) {
       notFull.wait(&lock); 
  }
  Add  thing;
  count++;
  notEmpty.signal();
  lock->release();
}

在 Hansen 管程中signal操作仅仅是一个提示,收到消息的进程需要重新检查条件

Hoare 管程

Hoare-style: Producer(){
  lock->acquire();
  if (count == n) {
       notFull.wait(&lock); 
  }
  Add thing;
  count++;
  notEmpty.signal();
  lock->release();
}

虚假唤醒

注意到 Hansen 管程 和 Hoare 管程中的区别,前者用的while,后者用的if。这是由于他们不同的唤醒策略导致的。如果将 Hansen 管程模型中的while改为if则会出现虚假唤醒的情况。

假设存在生产者进程A、B,它们现在都被挂起在notFull条件变量的等待队列中,消费者C执行 count–,此时 count=n-1, 随后执行 signal 操作后释放锁并退出管程。进程A、B争夺锁,假设A抢到锁,进入管程。由于是 if 语句,所以程序直接向下执行,执行 count++后 count=n,然后释放锁退出管程;之后进程B进入管程,重新该过程,count 的最终值为 n+1。显然在进程A执行完后,由于count又恢复到了N,进程B进入管程后应该挂起等待,但由于使用的是 if 来检查条件而不是 while 从而导致进程B被虚假唤醒了。

管程的缺陷

与管程和信号量有关的另一个问题是,这些机制都是设计用来解决访问共享内存的一个或多个 CPU 上的互斥问题的。通过将信号量放在共享内存中并用 TSL 或 XCHG 指令来保护它们,可以避免竞争。但是如果是在分布式系统中,可能同时具有多个 CPU 的情况,并且每个 CPU 都有自己的私有内存,它们通过网络相连,那么这些原语将会失效。因为信号量太低级了,而管程在少数几种编程语言之外无法使用,所以还需要其他方法。

消息传递(messaage passing)

消息传递有两个原语 send 和 receive。它们像信号量而不像管程,是系统调用而不是语言级别。示例如下:

send(destination, &message);

receive(source, &message);

send 方法用于向一个给定的目标发送一条消息,receive 从一个给定的源接受一条消息。
如果没有消息,接受者可能被阻塞,直到接受一条消息或者带着错误码返回。

消息传递系统的设计要点

消息传递系统现在面临着许多信号量和管程所未涉及的问题和设计难点,尤其对那些在网络中不同机器上的通信状况。比如:

  • 如何防止消息丢失?

    • 为了防止消息丢失,发送方和接收方可以达成一致:一旦接受到消息后,接收方马上回送一条特殊的 确认(acknowledgement) 消息。如果发送方在一段时间间隔内未收到确认,则重发消息。
  • 消费者如何区分新的消息和一条重发的老消息?

    • 通常采用在每条原始消息中嵌入一个连续的序号来解决此问题。如果接受者收到一条消息,它具有与前面某一条消息一样的序号,就知道这条消息是重复的,可以忽略。
  • 如何命名进程的问题,以便在发送或接收调用中清晰的指明进程?

  • 身份验证的问题,客户端怎么知道它是在与一个真正的文件服务器通信?

用消息传递解决生产者-消费者问题

#define N 100					/* buffer 中槽的数量 */						 

void producer(void){
  
  int item;
 	message m;		  			/* buffer 中槽的数量 */								
  
  while(TRUE){    
    item = produce_item();		/* 生成放入缓冲区的数据 */				    
    receive(consumer,&m);		/* 等待消费者发送空缓冲区 */						    
    build_message(&m,item); 	/* 建立一个待发送的消息 */					    
    send(consumer,&m);			/* 发送给消费者 */					
  }
}

void consumer(void){
  
  int item,i;
  message m;
   
  for(int i = 0;i < N;i++){						    
    send(producer,&m);		    /* 发送N个缓冲区 */						
  }
  while(TRUE){
    receive(producer,&m);	    /* 接受包含数据的消息 */						    
  	item = extract_item(&m);	/* 将数据从消息中提取出来 */				    
    send(producer,&m);			/* 将空缓冲区发送回生产者 */					    
    consume_item(item);			/* 处理数据 */					
  }
}

假设所有的消息都有相同的大小,并且在尚未接受到发出的消息时,由操作系统自动进行缓冲。在该解决方案中共使用 N 条消息,这就类似于一块共享内存缓冲区的 N 个槽。消费者首先将 N 条空消息发送给生产者。当生产者向消费者传递一个数据项时,它取走一条空消息并返回一条填充了内容的消息。通过这种方式,系统中总的消息数量保持不变,所以消息都可以存放在事先确定数量的内存中。

如果生产者的速度要比消费者快,则所有的消息最终都将被填满,等待消费者,生产者将被阻塞,等待返回一条空消息。如果消费者速度快,那么情况将正相反:所有的消息均为空,等待生产者来填充,消费者将被阻塞,以等待一条填充过的消息。

屏障(barrier)

最后一个同步机制是准备用于进程组而不是进程间的生产者-消费者情况的。

在某些应用中划分了若干阶段,并且规定,除非所有的进程都就绪准备着手下一个阶段,否则任何进程都不能进入下一个阶段,可以通过在每个阶段的结尾安装一个屏障来实现这种行为。当一个进程到达屏障时,它会被屏障所拦截,直到所有的屏障都到达为止。屏障可用于一组进程同步,如下图所示
屏障

在上图中我们可以看到,有四个进程接近屏障,这意味着每个进程都在进行运算,但是还没有到达每个阶段的结尾。过了一段时间后,A、B、D 三个进程都到达了屏障,各自的进程被挂起,但此时还不能进入下一个阶段呢,因为进程 B 还没有执行完毕。结果,当最后一个 C 到达屏障后,这个进程组才能够进入下一个阶段。

屏障简而言之就是,筹齐龙珠才能召唤神龙。

避免锁:读-复制-更新(RCU)

类似读写锁

没看太懂,感觉类似读写锁

https://zhuanlan.zhihu.com/p/89439043?utm_source=wechat_session

手把手教你学simulink(42.1)--储能系统场景示例:基于Simulink的电池+超级电容器混合储能系统 混合储能系统(HESS)结合了电池和超级电容器的优点,能够在满足高功率需求的同时提供较长的持续放电时间。这种组合特别适合用于电动汽车、可再生能源发电系统、电网频率调节等应用场景。本项目将详细介绍如何在MATLAB/Simulink中搭建一个完整的基于电池和超级电容器的混合储能系统,并提供具体的代码实现和设计步骤。如果你需要实现更复杂的控制逻辑,可以使用MATLAB Function模块。以下是一个简单的例子,展示了如何在该模块中实现一个条件逻辑,以分配电池和超级电容器的充放电任务。Matlab。 阅读详情

相关推荐

详解ReID的各部分组成及Trick——基于FastReID

这一系列博客将基于京东开源的FastReID进行扩充,详细介绍了ReID的各个组成部分,一些有用的Trick,评价指标,常用数据集等,详细内容见下方的链接,总结不易,如有理解不正确之处,麻烦各位批评指正。 一、FastReID的中的baseline文件配置 二、Training strategy 三、Pre-processing 四、Backbone 五、Aggregation 六、Head 七、Loss 八、Distance Metric 九、Post-processing 十、Evaluation 十一

藏晖的博客 9162

管程机制处理进程同步问题

进程同步机制有很多,主要有PV操作和信号量,管程,以及消息传递等。 由于PV操作的使用分散在各个进程之中,不利于对临界资源的统一管理,还有PV操作的使用需要小心,若顺序不对,或忘一个没写,很容易陷入死锁,所以,管程就能解决上面问题。 管程的基本思路是:将分散在各个进程中的临界区集中起来进行统一控制和管理,并且将系统中的共享资源用数据结构抽象的描述出来,然后对临界区的访问通过管程进行统一

best_fiends_zxh的博客 4450

英飞凌AURIX™ Development Studio(ADS)工程调试与烧录实战指南

本文详细介绍了英飞凌AURIX™ Development Studio(ADS)的工程调试与烧录实战指南。从调试环境搭建、硬件连接到断点设置、变量监控等核心调试技巧,帮助开发者快速掌握ADS安装使用教程,提升多核处理器开发效率。

k8l9m0n的博客 570

pthread分别使用互斥量、条件变量、信号量实现barrier

导入 在多线程编程里,路障(Barrier)是一个非常有利的工具。大概有以下几种应用场景; 希望各线程重新同步。有一些线程可能在某些阶段执行的相对较快,如果我们需要它们同步进入下一阶段,就要用到Barrier。 调试程序。多线程程序不像单线程程序那样可以很方便进行调试。有些时候一些线程的一些变量值被不经意修改导致在运行下一阶段时出现bug。这时候就需要使用Barrier,让大家都停一停,打印自己的变量信息。 pthread不提供路障的实现,需要我们自己手动实现。本文提供了三种实现方式。 建模 barri

weixin_45793220的博客 2000

MATLAB/Simulink与ThingSpeak集成:打通物联网数据与工业算法仿真的闭环

在工业算法开发与控制系统设计中,模型仿真与真实世界数据的脱节是常见的工程挑战。其核心原理在于构建一个双向数据流闭环,将虚拟的仿真模型与物理世界的传感器网络连接起来。这一技术方案的核心价值在于大幅缩短算法迭代周期,实现数据驱动的设计与验证。典型的应用场景包括数字孪生构建、预测性维护和硬件在环(HIL)测试的云端化。通过将MATLAB/Simulink这一强大的模型化设计环境,与ThingSpeak物联网云平台进行深度集成,工程师能够轻松地将实时或历史的物联网数据流直接注入仿真模型作为输入,同时将仿真结果实时回

weixin_34220179的博客 469

Vue+Echarts监控大屏实例四:智慧农业监控模板实例

本实例通过Vue-Echarts实现了智慧农业监控模板实例界面,学习并使用了vue-echarts的使用,接入高德地图并展示,添加了全屏切换、加载动画小鼓、站点切换及退出等相关功能,提供源码下载。

Java、前端、小程序学习笔记、问题汇总、项目实例分享,有需要请联系。 7667

OS进程同步与通信

信号量机制 信号量用于互斥 P(S) 临界区 V(S) ----- P(S) 临界区 V(S) 生产者消费者: typedef int semaphore //信号量值设置为1就是互斥量 semaphore mutex = 1; //同一时刻只有一个进程可以读写缓冲区 semaphore empty = N; //“空”的数目,缓冲...

weixin_30617695的博客 230

NVIDIA平台Xavier/TX2板摄像头驱动开发与TP2850方案开发

本文介绍了在NVIDIA平台上开发Xavier/TX2板摄像头驱动的过程,并结合TP2850方案实现了多路模拟相机输入。我们提供了初始化摄像头和连接TP2850方案的示例代码,并介绍了相关的步骤和注意事项。需要注意的是,以上示例代码仅提供了基本的摄像头初始化和与TP2850通信的框架,你需要根据具体的需求进行进一步开发和配置。你可能需要参考摄像头和TP2850的数据手册以及相关的开发文档,以了解更多详细的配置和操作。此外,你还需要一个支持TP2850方案的硬件设备,并将其连接到Xavier/TX2板上。

PixelLancer的博客 592

PV操作解决经典进程同步问题

使用PV操作解决操作系统中的经典同步问题

墨城烟柳ベ旧人殇的博客 3368

基于YOLOv26的卫星遥感环境监测系统

本文介绍了一个基于YOLOv26的卫星遥感环境监测系统。该系统采用分层架构设计,包含数据采集、预处理、目标检测、图像分析、变化检测等核心模块。技术栈包括YOLOv26、PyTorch、OpenCV等深度学习框架和图像处理工具,以及GDAL、rasterio等地理信息处理库。系统支持获取多种卫星数据源(如Sentinel、Landsat、MODIS)的遥感图像,并进行时间序列分析。通过代码示例展示了数据采集模块的实现,包括卫星图像获取和模拟生成功能。该系统可应用于环境要素检测、变化监测等场景,为环境监测提供智

一键难忘的博客 934

一文掌握 MongoDB 详细安装与配置(Windows / Linux / macOS 全平台)

本文详细介绍了MongoDB社区版7.0+在Windows、Linux和macOS三大操作系统上的完整安装、配置与安全加固流程。主要内容包括:各平台推荐安装方式(Windows使用MSI安装包、Linux通过官方仓库、macOS通过Homebrew)、基本配置参数说明、服务启动方法以及关键安全措施(如创建管理员用户和启用身份验证)。文章还提供了系统最低要求、下载地址、数据目录设置建议以及生产环境使用注意事项,适合开发、测试及生产环境部署参考。

数据知道的博客 6708

操作系统,关于静态优先级调度,时间片轮转调度以及生产者与消费者问题的作业。

主要对生产与消费,静态优先级,时间轮转片题目的解答。

weixin_63393915的博客 984

十一、Cadence ic 617以及assura使用总结

1.617的端口和514不同,没有网络属性,使用时不能直接放在原理图上,如果要直接放在图上要接线并且放上标签。2.617的端口也不能直接放在线上,514中将端口直接放在线上是可以使端口和线连接,但617看似连接,但拉出来线没有交点,实际上没有连接。这里set switches里的选项表示不同的模块,也就是选择一个就只对这一部分进行DRC验证,要验证整个版图的DRC就要把所有的选项选中,然后一个个改正。LVS中也是一样,要全部选中改正。

0 2294

操作系统课设之Linux 进程间通信

操作系统课程设计 Linux进程通信 Linux消息通信机制

RongLin02的博客 5740

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

Linux —— 进程间通信

进程间通信方式的介绍:管道通信、system V通信......

M的博客 6664

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

操作系统实验三进程间通信

操作系统实验三进程间通信

Time木的博客 1万+

IMSL CNL 7.0.0 (x86-64)Crack

IMSL CNL 7.0.0 (x86-64)Crack intel的算法库破解文件,这是C++版本的破解文件

【Linux学习】进程间通信

【Linux学习】进程间通信 管道的概念:管道想必大家都不陌生,它是Unix中最古老的进程间通信的形式,允许一个进程的输出成为另一个进程的输入。在操作系统中,管道是一种特殊的文件,常用于在父进程和子进程之间或者在两个独立的进程之间传递数据在命令行中,我们常常用管道连接多个命令,形成了一个数据处理的流水线。

Chris在Coding的博客 5787

进程间通信

了解进程间的通信方式,例如管道(匿名管道和命名管道)、共享内存、消息队列、信号量

冯同学的博客 7495

操作系统实验——进程创建与进程间通信

操作系统实验——进程创建与进程间通信 实验内容 一、创建进程树 二、vfork以及execl的使用 三、创建进程以及进程间通信(管道)的综合运用

ws15168689087的博客 7364

操作系统 —— 进程间的通信

进程间的通信,这些细节,你值得拥有!!!

lyzzs222的博客 2098

Linux进程间通信

在系统中,进程之间可能会存在特定的协同工作的场景。一个进程要把自己的数据交付给另一个进程,让其进行处理。两个进程要互相通信其实并不容易,因为进程具有独立性,想要通信必须先看到一份公共资源(一段内存),而这个资源不可能属于任何一个进程,这样就违背独立原则,而是由操作系统管理,所以进程间通信的本质:其实是由OS参与,提供一份所有通信进程都能看到的公共资源。

qq_52363432的博客 2117

进程间的通信

进程间通信(IPC:Inter Processes Communication) 进程是一个独立的资源分配单元,不同进程(这里所说的进程通常指的是用户进程)之间的资源是独立的,没有关联,不能在一个进程中直接访问另一个进程的资源(例如打开的文件描述符)。进程不是孤立的,不同的进程需要进行信息的交互和状态的传递等,因此需要进程间通信。在Linux下,要想查看这些信号和编码的对应关系,可使用命令:kill ­l。

2401_83430041的博客 1566

操作系统——进程间的通信

操作系统——进程间的通信的简单介绍

weixin_73622063的博客 1260

进程间通信--管道【Linux操作系统】

进程间通信的本质是打破进程隔离性数据流动:传递信息,支持协作资源共享:避免冗余,提升效率行为协调:同步操作,防止竞态条件选择合适的 IPC 机制需权衡性能需求、开发复杂度、系统架构本地高性能:优先共享内存 + 信号量跨网络扩展:选择套接字或 RPC简单通知:使用信号或管道。

2301_80058734的博客 1330

【Linux———进程间的通信】

本篇博客的主要内容主要介绍了进程之间是如何进行通信的,了解进程间的通信对操作系统的学习相当重要,请耐心观看!提示:以下是本篇文章正文内容,下面案例可供参考进程间通信简称IPC(Interprocess communication),进程间通信就是在不同进程之间传播或交换信息。管道是Unix中最古老的进程间通信的形式,我们把从一个进程连接到另一个进程的数据流称为一个“管道”,在Linux中主要包括匿名管道和命名管道。在linux命令中,| 就是管道,可以将输出的信息读取给后面的命令处理。

Z15255808023的博客 1022

操作系统复习总结:教材《操作系统(第5版)》

操作系统复习总结:教材《操作系统(第5版)》 文章目录操作系统复习总结:教材《操作系统(第5版)》操作系统基础基础中断和异常系统调用进程和线程基础进程调度算法及大题进程间关系及通信死锁存储连续空间分配不连续空间分配页式管理段式管理段页式管理设备I/O磁盘文件 操作系统基础 基础 操作系统的层次 机器层 → 硬件 操作系统层 → 系统 系统层 → 系统软件 应用层 → 应用程序 概念 操作系统是一种系统软件,是软、硬件资源的控制中心。它以尽量合理有效的方法组织单个或多个用户以多任务方式共享计算机

Jancy2265的博客 1903
上一篇: 【《现代操作系统 第4版》】3、调度算法
下一篇: 【《现代操作系统 第4版》】6、进程间的通信之经典的IPC问题
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值