1.实验名称

进程同步

  1. 实验目的
  2. 加强对进程同步和互斥的理解,学会使用信号量解决资源共享问题。
  3. 熟悉Linux进程同步原语。
  4. 掌握信号量wait/signal原语的使用方法,理解信号量的定义、赋初值及wait/signal操作。
  1. 实验内容

(1) 编写程序,使用Linux操作系统中的信号量机制模拟实现生产者消费者问题。设有一个生产者和一个消费者,缓冲区可以存放产品,生产者不断生成产品放入缓冲区,消费者不断从缓冲区中取出产品,消费产品。

(2) 编写好一个C语言程序,在GCC中进行编译。

  1. 实验原理或流程图

进程同步和互斥的基本概念

互斥(Mutex):确保多个进程或线程不会同时访问共享资源。互斥通常通过锁机制实现,当一个进程或线程获取了锁,其他进程或线程必须等待直到锁被释放。

同步(Synchronization):协调多个进程或线程的执行顺序,以确保它们在正确的时间执行正确的操作。同步机制可以确保进程或线程按照特定的顺序执行,或者在某些条件下等待其他进程或线程。

信号量(Semaphore):是一种同步机制,可以用来实现互斥和同步。信号量是一个整数变量,可以被进程或线程通过wait(P操作)和signal(V操作)来改变其值。

Linux进程同步原语

Linux提供了多种进程同步原语,包括互斥锁(mutexes)、读写锁(read-write locks)、条件变量(condition variables)、信号量(semaphores)等。这些原语可以帮助开发者在多线程或多进程环境下保护共享资源,避免竞态条件。

POSIX信号量#include<semaphore.h>

POSIX信号量是由POSIX标准定义的,它提供了跨平台的线程和进程同步机制。POSIX信号量有两种形式:

无名信号量(Unnamed Semaphores):

无名信号量是在进程地址空间中创建的,它们只在创建它们的进程和任何随后继承它们的子进程中可见。无名信号量是通过sem_init函数创建的,并且在使用完毕后通过sem_destroy函数销毁。

无名信号量是轻量级的,因为它们不涉及内核和用户空间之间的上下文切换,适用于线程间同步。

有名信号量(Named Semaphores):

有名信号量是全局可见的,它们可以通过名称在不同进程间共享。有名信号量是通过sem_open函数创建的,并且可以在不同的程序之间共享。

有名信号量更重,因为它们需要在内核中维护,适用于不同进程间的同步。

信号量删除函数:sem_close(),sem_unlink()

信号量操作:sem_wait(),sem_trywait(),semtimedwait(),sem_post(),sem_getvalue()

System V信号量#include<sys/sem.h>

System V信号量是较旧的信号量机制,它是特定于UNIX系统的。

nt semget(key_t key,int num_sems,int sem_flags);

int semctl(int sem_id,int sem_num,int cmd,union semun arg);

int semop(int sem_id,struct sembuf *sops,size_t nsops);

使用步骤:

①使用semget()函数创建或获取信号量。不同进程通过使用同一个信号量键值来获得同一个信号量。

②使用semctl()函数的SETVAL:操作初始化信号量。

③使用sempop()函数进行信号量的PV操作,这是实现进程同步或互斥的核心工作。

④如果不需要信号量,则从系统中删除它,此时使用shmctl()函数的IPC_RMID操作

信号量wait/signal原语

信号量是进程间或线程间同步的一种机制,它通过wait(P操作)和signal(V操作)两个原语来实现:

wait(P操作):等待信号量。如果信号量的值大于0,wait操作会将其减1,然后继续执行。如果信号量的值为0,进程或线程将被阻塞,直到信号量的值变为正数。

signal(V操作):释放信号量。signal操作会将信号量的值增加1。如果有任何进程或线程因为等待该信号量而被阻塞,那么它们将被唤醒,并且其中一个(通常是FIFO顺序)将获得信号量,继续执行。

信号量的定义、赋初值及wait/signal操作

定义信号量:在Linux中,可以使用 <semaphore.h> 头文件中定义的 sem_t 类型来声明一个信号量。

sem_t sem;

初始化信号量:使用 sem_init 函数初始化信号量,并为其赋予一个初始值。

sem_init(&sem, 0, value); // value是信号量的初始值

wait操作(P操作):当进程或线程需要访问共享资源时,它会执行wait操作来减少信号量的值。

sem_wait(&sem); // 等同于sem_wait(&sem),因为sem_wait宏展开后就是P操作

signal操作(V操作):当进程或线程完成对共享资源的访问后,它会执行signal操作来增加信号量的值。

sem_post(&sem); // 等同于sem_post(&sem),因为sem_post宏展开后就是V操作

销毁信号量:当信号量不再需要时,应该使用 sem_destroy 函数来销毁它。

sem_destroy(&sem);

通过使用信号量,开发者可以控制对共享资源的访问,确保在任何时候只有一个进程或线程能够访问资源,或者按照特定的顺序访问资源,从而避免数据不一致和竞态条件。

5. 实验过程或源代码

#include <semaphore.h>

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <pthread.h>

#define MAX 256

//char buffer[MAX]; // 直接声明为固定大小的数组,避免动态分配内存

char *buffer;//可以用数组,可以用指针。

sem_t empty; // 定义同步信号量empty

sem_t full;  // 定义同步信号量full

sem_t mutex; // 定义互斥信号量mutex

void *producer(void *arg) // 生产者

{

    sem_wait(&empty); // empty的P操作

    sem_wait(&mutex); // mutex的P操作

    printf("input something to buffer: ");

    buffer = (char *)malloc(MAX); // 给缓冲区分配内存空间

    fgets(buffer, MAX, stdin); // 输入产品至缓冲区

    sem_post(&mutex); // mutex的V操作

    sem_post(&full); // full的V操作

    return NULL;

}

void *consumer(void *arg) // 消费者

{

    sem_wait(&full); // full的P操作

    sem_wait(&mutex); // mutex的P操作

    printf("read product from buffer: %s\n", buffer); // 从缓冲区取出产品

    memset(buffer, 0, MAX); // 清空缓冲区

    sem_post(&mutex); // mutex的V操作

    sem_post(&empty); // empty的V操作

    return NULL;

}

int main()

{

    pthread_t producer_thread;

    pthread_t consumer_thread;

    int ret;

    sem_init(&empty, 0, 10); // 设置empty到初值为10

    sem_init(&full, 0, 0);  // 设置full到初值为0

    sem_init(&mutex, 0, 1); // 设置mutex到初值为1

    ret = pthread_create(&producer_thread, NULL, producer, NULL); // 创建生产者线程

    ret = pthread_create(&consumer_thread, NULL, consumer, NULL); // 创建消费者线程

    pthread_join(producer_thread, NULL); // 等待生产者线程结束

    pthread_join(consumer_thread, NULL); // 等待消费者线程结束

    sem_destroy(&empty); // 删除信号量

    sem_destroy(&full);

    sem_destroy(&mutex);

    printf("The End...\n");

    return 0;

}

编译代码文件,加上-lpthread,调用该库,之后运行。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐