Client-Server问题

2023-11-19

1. 实验内容与要求

  1. 需要创建客户Client和服务器Server两个进程,它们通过管道进行通信
  2. Client进程派生3个生产者线程,一个管道线程,共享一个20个slots的缓冲区。每个生产者线程随机产生一个数据,打印出来自己的id(进程、线程)以及该数据,通过管道发送线程发送给Server进程
  3. Server进程派生一个管道线程,3个消费者线程,共享一个20个slots的缓冲区。管道线程把数据接收后,分发给消费者线程处理
  4. 生产者生成数据的间隔和消费者消费数据的间隔,按照负指数分布来控制,各有一个控制参数, lambda_c,lambda_s
  5. 运行的时候,开两个窗口,一个./client lambda_c ,另一个./server lambda_s,要求测试不同的参数组合,打印结果,截屏放到作业报告里。

2. 实验问题分析

生产者进程中存在3个生产者线程、一个管道线程以及一个20个slots的缓冲区。生产者线程负责向缓冲区写入一个数据,管道线程负责从缓冲区读出一个数据,当生产者将缓冲区写满时要等待管道线程将缓冲区中的数据拿出才可以继续写入。这里可以使用full-empty-mutex锁加上共享一个长度20的循环队列实现,这保证了同一时间只有一个线程在对缓冲区进行读/写操作。

在另一边,消费者进程类似地存在3个消费者线程、一个管道线程以及一个20个slots的缓冲区。其中消费者线程负责从缓冲区读出一个数据,管道线程负责向缓冲区写入一个数据,当缓冲区被写满后管道线程需要等待消费者线程读取数据后才能继续向缓冲区写入。这里同样可以使用full-empty-mutex锁加上共享一个长度20的循环队列实现。

当生产者的管道线程读取到生产者缓冲区中的数据后,会将数据传送到管道中。消费者的管道线程在获取到读写锁后,以FIFO的方式从管道中读取数据放入缓冲区。值得注意的一点是,当消费者的缓冲区被写满后,消费者的管道线程将无法获得对缓冲区的写入锁,管道也将被堵塞。

生产者线程和消费者线程生产和消费数据有一定的时间间隔,此间隔按照负指数分布且存在于读写缓冲区之前,即生产者和消费者线程在休息了一定时间后才开始争取读写锁。这点体现了多线程的作用。

3. 实验环境

操作系统环境:VMware + Ubuntu 20.04

编程语言: C语言

4.功能流程图

5.代码实现

 

缓冲区数据结构

使用循环队列来完成缓冲区20个slot的定义,并创建空指针用于之后的共享内存。需要注意server和client两个进程间缓冲区不共享,创建共享内存时应取不同的名字。这里以client.c中为例。

//client.c
#define BUFFER_SIZE 20//缓冲区slot数
/定义缓冲区
typedef struct buffer_struct{
    int rear;//尾指针,用于写入
    int front;//头指针,用于读出
    int buffer[BUFFER_SIZE];//长度20的int数组
}buffer_struct;
void *ptr;//创建空指针,用于访问共享内存
int main(int argc, char *argv[])
{
    //声明缓冲区对象并清零
    buffer_struct bf;
    memset(&bf, 0, sizeof(buffer_struct));
//......
//创建共享内存
    int shm_fd = shm_open("client_buf", O_CREAT | O_RDWR, 0666);//server.c中”server_buf”
    ftruncate(shm_fd, sizeof(buffer_struct));
    ptr = mmap(0, sizeof(buffer_struct), PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0);
//......
}

定义锁

定义full-empty-mutex锁结构,full表示有多少个满的缓冲区,empty表示有多少个空的缓冲区,mutex为标准的读写锁。使用sem_t对buffer上锁时需要注意锁在server.c与client.c两个线程之间不共享,命名不能一致。具体的使用在具体的线程函数中展示,下面以client.c为例展示锁的定义与初始化

#define NUM_THREADS 3//线程数
//定义锁
sem_t *full;
sem_t *empty;
sem_t *mutex;
int main(int argc, char *argv[])
{
//......
//打开具名信号量并初始化 
    full = sem_open("cfull", O_CREAT, 0666, 0);//server.c中为”sfull”
    empty = sem_open("cempty", O_CREAT, 0666, 0);//server.c中为”sempty”
    mutex = sem_open("cmutex", O_CREAT, 0666, 0);//server.c中为”smutex”
    sem_init(full, 1, 0);//初始时有0个满的slot
    sem_init(empty, 1, BUFFER_SIZE);//20个空位对应20个slot
sem_init(mutex, 1, 1);//读写锁,一次只能有一个线程读写
//......
//创建3个服务器和一个管道
    pthread_t tid[NUM_THREADS];
    pthread_t tid0;
    pthread_attr_t attr[NUM_THREADS];
    pthread_attr_t attr0;
    for (int i = 0; i < NUM_THREADS; i++){
        pthread_attr_init(&attr[i]);
        pthread_create(&tid[i], &attr[i], client, &lambda_c);//生产者线程
        //server.c中为pthread_create(&tid[i], &attr[i], server, &lambda_s);
    }
    pthread_attr_init(&attr0);
pthread_create(&tid0, &attr0, pipe_func, &lambda_c);//管道线程,server.c中改&lambda_s
    for (int j = 0; j < NUM_THREADS; j++){
        pthread_join(tid[j], NULL);
    }
    pthread_join(tid0, NULL);
    return 0;
}

时间函数

设x为随机变量,λ为参数,则令x符合负指数分布的概率密度函数和概率分布函数为

其中概率分布函数是概率密度函数的积分。则x有x=-ln(1-F(x;lambda))/lambda,其中可以用rand()函数生成F(x;λ),因为其是在(0,1)上的随机分布。

 

//符合负指数分布的时间函数
double sleep_time(double lambda_c){
    double r;
    r = ((double)rand() / RAND_MAX);
    while(r == 0 || r == 1){
        r = ((double)rand() / RAND_MAX);
    }
    r = (-1 / lambda_c) * log(1-r);
    return r;
}

 在定义了管道int pipef;和读入lambda_c或在server.c中的lambda_s后,我们进入四个主要函数的讲解。在int main中读入参数

int main(int argc, char *argv[])
{
//......
    if (argc != 2){
        printf("The number of supplied arguments are false.\n");
        return -1;
    }
    if (atof(argv[1]) < 0){
        printf("The lambda entered should be greater than 0.\n");
        return -1;
    }
double lambda_c = atof(argv[1]);//读取lambda p转化为数字
//......
}

client.c中的生产者函数

/生产者函数
void *client(void *temp){
    double lambda_c = *(double *)temp;
    do{
        double interval_time = lambda_c;
        unsigned int sleepTime = (unsigned int)sleep_time(interval_time);
        sleep(sleepTime);//生产数据的间隔
        int item = rand()%999+1;//生产一个1-999之间的数
        buffer_struct *shm_ptr = ((buffer_struct *)ptr);//指向共享的缓冲区struct
        sem_wait(empty);//empty--
        sem_wait(mutex);//mutex--
        printf("Sleep Time: %d s | Producing the data %d to buffer[%d] by thread %ld in process %d.\n", sleepTime, item,shm_ptr->rear,gettid(), getpid());
        shm_ptr->buffer[shm_ptr->rear] = item;//向缓冲区输入item
        shm_ptr->rear = (shm_ptr->rear+1) % BUFFER_SIZE;//尾指针后移
        sem_post(mutex);//mutex++
        sem_post(full);//full++
    }while(1);
    pthread_exit(0);
}

client.c中的管道函数

void *pipe_func(void *temp)
{
    int *rec = (int *)malloc(sizeof(int));
    pipef = open("./pipe_func", O_WRONLY);//以只写的方式打开管道文件
    if(pipef < 0)
    {
        printf("open pipef error is %s\n", strerror(errno));
        *rec = -1;
        pthread_exit((void *)rec);
        return NULL;
    }
    do{
        sem_wait(full);//full--
        sem_wait(mutex);//mutex--
        buffer_struct *shm_ptr = ((buffer_struct *)ptr);//指向共享的缓冲区struct
        int item  = shm_ptr->buffer[shm_ptr->front];//从头指针读出
        char str[5];
        sprintf(str+strlen(str), "%d", item);//将item转化为string形式
        write(pipef, str, sizeof(str));//向管道写入item的string
        shm_ptr->front = (shm_ptr->front+1) % BUFFER_SIZE;//头指针后移
        memset(str, 0, sizeof(str));//string空间清零
        sem_post(mutex);//mutex++
        sem_post(empty);//empty++
    }while(1);
    pthread_exit(0);
}

server.c中的管道函数

void *pipe_func(void *temp)
{
    int *rec = (int *)malloc(sizeof(int));
    pipef = open("./pipe_func",O_RDONLY);//以只读的方式打开管道文件
    if(pipef < 0)//判断管道文件是否存在
    {
        printf("open pipef error is %s\n", strerror(errno));
        *rec = -1;
        pthread_exit((void *)rec);
        return NULL;
    }
    do{
        int item;
        char str[5];
        if(read(pipef, str, sizeof(str)) > 0)//读取str。read函数在管道为空时自动拥塞,不进行读取
        {
            sem_wait(empty);//empty--
            sem_wait(mutex);//mutex--
            buffer_struct *shm_ptr = ((buffer_struct *)ptr);//指向共享的缓冲区struct
            sscanf(str,"%d",&item);//将str转化为int形式的item
            shm_ptr->buffer[shm_ptr->rear] = item;//将item写入缓冲区
            shm_ptr->rear = (shm_ptr->rear+1) % BUFFER_SIZE;//尾指针后移
            memset(str, 0, sizeof(str));//清除str中的内容
            sem_post(mutex);//mutex++
            sem_post(full);//full++
        }
    }while(1);
}

server.c中的消费者函数

void *server(void *temp){
    double lambda_s = *(double *)temp;
    do{
        double interval_time = lambda_s;
        unsigned int sleepTime;
        sleepTime = (unsigned int)sleep_time(interval_time);
        sleep(sleepTime);//消费一段时间
        buffer_struct *shm_ptr = ((buffer_struct *)ptr);//指向共享的缓冲区struct
        sem_wait(full);//full--
        sem_wait(mutex);//mutex--
        int item  = shm_ptr->buffer[shm_ptr->front];//读取缓冲区内容
        printf("Sleep Time: %d s | Consuming the data %d from the buffer[%d] by the thread %ld in  the process %d.\n", sleepTime, item, shm_ptr->front, gettid(), getpid());
        shm_ptr->front = (shm_ptr->front+1) % BUFFER_SIZE;//头指针后移
        sem_post(mutex);//mutex++
        sem_post(empty);//empty++
    }while(1);
}

6.实验过程与结果

根据实验要求5.运行的时候,开两个窗口,一个./client lambda_c,另一个./server lambda_s,要求测试不同的参数组合,打印结果。

当λc=λs=0.3时,写入速度与读取速度相似。client先启动,server的读取与client的写入相差的数量虽有波动但较为固定。

 当λc=0.5>λs=0.1时,写入速度比读取速度快。当写入较多时client的缓冲区已满,写入速度有所下降。

 当λc=0.1<λs=0.5时,写入速度比读取速度慢,几乎一有写入就会被读取。

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Client-Server问题 的相关文章

  • C# 静态类型不能用作参数

    public static void SendEmail String from String To String Subject String HTML String AttachmentPath null String Attachme
  • 如何从字符串中提取子字符串直到遇到第二个空格?

    我有一个像这样的字符串 o1 1232 5467 1232 5467 1232 5467 1232 5467 1232 5467 1232 5467 如何仅提取 o1 1232 5467 要提取的字符数并不总是相同 因此 我只想提取直到遇到
  • EntityHydrate 任务失败

    我最近安装了 Visual Studio 11 Beta 和 Visual Studio 2010 之后 我无法在 Visual Studio 2010 中构建依赖于 PostSharp 的项目 因此我卸载了 Visual Studio 1
  • 在 C 语言中,为什么数组的地址等于它的值?

    在下面的代码中 指针值和指针地址与预期不同 但数组值和地址则不然 怎么会这样 Output my array 0022FF00 my array 0022FF00 pointer to array 0022FF00 pointer to a
  • 无法继承形状

    为什么我不能使用继承 a 的类Shapes class http msdn microsoft com en us library ms604615 28v vs 90 29 我需要延长Rectangle具有一些方法的类 但我想以与使用相同
  • C# 中一次性对象克隆会导致内存泄漏吗?

    检查这个代码 class someclass IDisposable private Bitmap imageObject public void ImageCrop int X int Y int W int H imageObject
  • Selenium - C# - Webdriver - 无法找到元素

    在 C 中使用 selenium 我试图打开浏览器 导航到 Google 并找到文本搜索字段 我尝试下面的 IWebDriver driver new InternetExplorerDriver C driver Navigate GoT
  • 用于在标头更改时重新编译的简单 C 项目的示例 makefile

    有谁有完整的 makefile 可以执行以下操作 如果 HEADER 文件发生更改 则重建项目 cpp 文件在 makefile 中列出 头文件未在 makefile 中列出 头文件允许与 cpp 文件具有不同的名称 部分cpp文件没有头文
  • 为什么这个 makefile 在“make clean”上执行目标

    这是我当前的 makefile CXX g CXXFLAGS Wall O3 LDFLAGS TARGET testcpp SRCS main cpp object cpp foo cpp OBJS SRCS cpp o DEPS SRCS
  • Linux 上的 RTLD_LOCAL 和dynamic_cast

    我们有一个由应用程序中的一些共享库构成的插件 我们需要在应用程序运行时更新它 出于性能原因 我们在卸载旧插件之前加载并开始使用新插件 并且只有当所有线程都使用旧插件完成后 我们才卸载它 由于新插件和旧插件的库具有相同的符号 我们dlopen
  • Ubuntu systemd 自定义服务因 python 脚本而失败

    希望获得有关 Ubuntu 中的 systemd 守护进程服务的一些帮助 我写了一个 python 脚本来禁用 Dell XPS 上的触摸屏 这更像是一个问题 而不是一个有用的功能 该脚本可以工作 但我不想一直启动它 这就是为什么我想到编写
  • wordexp 失败时我们需要调用 wordfree 吗?

    wordexp 失败时我们需要调用 wordfree 吗 在某些情况下 调用 wordfree 似乎会出现段错误 例如 当 wordfree 返回字符串为 foo bar 的错误代码时 这在手册页中并不清楚 我已经看到在某些错误情况下使用了
  • 如何在 Javascript 中连接 C# ActiveX 事件处理程序

    我尝试使用几个代码片段将 ActiveX 对象与 Javascript 事件处理程序挂钩 我无法确定为什么事件处理程序没有被调用 带有项目的 Github 存储库 https github com JesseKPhillips Csharp
  • 在 azure blob 存储中就地创建 zip 文件

    我将文件存储在 Blob 存储帐户内的一个容器中 我需要在第二个容器中创建一个 zip 文件 其中包含第一个容器中的文件 我有一个使用辅助角色和 DotNetZip 工作的解决方案 但由于 zip 文件的大小最终可能达到 1GB 我担心在进
  • 读取依赖步行者输出

    I am having some problems using one of the Dlls in my application and I ran dependency walker on it i am not sure how to
  • 以编程方式创建 Blob 存储容器

    我有一个要求 即在创建公司时 在我的 storageaccount 中创建关联的 blob 存储容器 并将容器名称设置为传入的字符串变量 我已尝试以下操作 public void AddCompanyStorage string subDo
  • Unity3D - 将 UI 对象移动到屏幕中心,同时保持其父子关系

    我有一个 UI 图像 它的父级是 RectTransform 容器 该容器的父级是 UI 面板 而 UI 面板的父级是 Canvas 我希望能够将此 UI 图像移动到屏幕中心 即画布 同时保留父级层次结构 我的目标是将 UI 图像从中心动画
  • 更改“/usr/bin/”的所有权:不允许操作

    我刚刚改变了我的所有者 犯了一个巨大的错误 usr bin from root对一个普通人user 每当我尝试执行时 sudo chown root usr bin这给了我 chown changing ownership of usr b
  • 如果将变量设置为等于新对象,旧对象会发生什么?

    假设我们有一个 X 类not有一个超载的operator 功能 class X int n X n 0 X int n n n int main X a 1 an object gets constructed here more code
  • 声明一个负长度的数组

    当创建负长度数组时 C 中会发生什么 例如 int n 35 int testArray n for int i 0 i lt 10 i testArray i i 1 这段代码将编译 并且启用 Wall 时不会出现警告 并且似乎您可以分配

随机推荐

  • rhel的一些配置

    ip etc sysconfig network scripts ifcfg eth0 DEVICE eth0 NM CONTROLLED yes ONBOOT yes BOOTPROTO dhcp BOOTPROTO static IPA
  • 消息转换器统一对null值处理

    import java nio charset Charset import java util ArrayList import java util List import org springframework context anno
  • STM32 CAN通信理解(是半双工还是全双工?)

    STM32F429 CAN通信 CAN 是控制器局域网络 Controller Area Network 的简称 它是由研发和生产汽车电子产品著称的德国 BOSCH 公司开发的 并最终成为国际标准 ISO11519 是国际上应用最广泛的现场
  • 数据结构--二叉树的二叉链表实现

    1 二叉树的二叉链表示意图 二叉链表的每个结点由三个域组成 数据域 左指针域和右指针域 左右指针分别用来保存左右孩子结点的存储地址 2 二叉链表实现二叉树 2 1 头文件及其定义 BTNode h pragma once typedef c
  • 利用umi实现前端自定义打包

    背景 目前项目有这么一个需求 一套前端代码里面有两个项目 这两个项目一个是设备页面 一个是平台页面 因为这两个项目有共同的页面和代码 所有就写在一个仓库里面 出现的问题是 打包项目A并且也会随带的项目B的代码 这就会造成打包体积的增大 打包
  • java bean的使用

    这是我最终尝试成功的项目页面截图 网页效果 注意的点 1 Dog类 bean类 必须拥有一个无参构造器 这和spring里的bean的设计是一个理念 spring借鉴于javabean 2 通过getXxx和setXxx方法来访问成员变量
  • SpringBoot系列(五):SpringBoot整合Mybatis实现多表关联查询

    摘要 本文我们将继续分享介绍Spring Boot在整合Mybatis开发企业级应用时其他典型的业务场景 即Mybatis是如何实现多表关联查询时将查询结果集与对象进行映射的 主要的内容包含 一对一的表关联 和 一对多 多对多的表关联 查询
  • python语言合法命名-Python中的标识符命名方法及使用注意事项

    Python中的标识符是由用户定义的名称 用来指定变量名称 函数名称 类名称 模块等名称 1 标识符的命名规则 1 不能使用Python的保留字 关键字 来命名标识符 2 Python标识符合法的字符包括 26个大小写字母 a z A Z
  • Linux中select poll和epoll的区别

    select的本质是采用32个整数的32位 即32 32 1024来标识 fd值为1 1024 当fd的值超过1024限制时 就必须修改FD SETSIZE的大小 这个时候就可以标识32 max值范围的fd 对于单进程多线程 每个线程处理多
  • STM32笔记:使用SysTick测量程序的运行时间

    本实验用来测量延时函数 如下 的运行时间 void Delay IO u32 nCount for nCount 0 nCount SysTick c include SysTick h IO u32 TimingDelay 0 void
  • 自己的第一个程序 — QT聊天软件 小感

    其实当初师兄给我这个任务的时候还是在2010年的11月份 那个时候刚到项目上的我超级自信 总认为只要自己想做 稍微看下书 动下手就可以写出来了 所以当师兄问我多久可以做好的时候 我开始满怀信心的说一个星期 后来细想下有点悬 说还是2个星期吧
  • VUE props 实现原理(源码解析)

    VUE props 实现原理 源码解析 前言 我们在使用组件的时候 都会给组件传入一些属性 但是在使用时 却只是关注了它传递数据的功能 没有想过它是怎样的一个原理 具体是怎么实现的 其实我们平时写出来的组件 本质上就是一个template模
  • 屏蔽SSH服务的弱密码算法

    前言 等保测试 1 目标主机SSH服务存在RC4 CBC或None弱加密算法 2 如果配置为CBC模式的话 SSH没有正确地处理分组密码算法加密的SSH会话中所出现的错误 解决办法 仅保留CTR加密算法 参考文章 1 编辑 ssh 配置文件
  • Windows 域基础环境搭建-Win_Server_2003

    文章目录 1 Windows 域基础环境搭建 Win Server 2003 1 1 Windows域简介 1 1 1 Windows域作用 1 1 2 Windows计算机查看所在域方式 1 1 3 域控 DC 1 2 Windows域环
  • 如何用Selenium做自动化测试

    我们做自动化测试如果想提高一下自己的软实力 我建议还是学着多写写脚本 这样能锻炼自己的编程能力 让我们以开放测试的思维去测试产品 而不是为了测试而测试 那样我觉得没什么意义 当然这样不是说其他不写脚本 简单的自动化没意义 本人不反对 认真对
  • 【AI之路】使用huggingface_hub优雅解决huggingface大模型下载问题

    文章目录 前言 一 Hugging face是什么 二 准备工作 三 下载整个仓库或单个大模型文件 1 下载整个仓库 2 下载单个大模型文件 总结 附录 前言 Hugging face 资源很不错 可是国内下载速度很慢 动则GB的大模型 下
  • C/C++ algorithm头文件的max()、min()、abs()和math头文件的fabs()

    对于一般的数据类型 整型和浮点型 我们经常要得到最大值 最小值和绝对值 本文章利用一些头文件的函数 进行展示 函数的参数及使用在程序代码及注释中体现 代码如下 Project 最值及绝对值 Date 2018 07 31 Author Fr
  • CTP 学习笔记

    CTP 学习笔记 前言 综合交易平台 Comprehensive Transaction Platform CTP 是专门为期货公司开发的一套期货经纪业务管理系统 由交易 风险控制和结算三大系统组成 前后研究了两个多星期 CTP 各种查资料
  • 高斯混合模型(GMM)推导

    数据表示如下 X X X 观测数据 X x 1
  • Client-Server问题

    1 实验内容与要求 需要创建客户Client和服务器Server两个进程 它们通过管道进行通信 Client进程派生3个生产者线程 一个管道线程 共享一个20个slots的缓冲区 每个生产者线程随机产生一个数据 打印出来自己的id 进程 线