异构计算关键技术之多线程技术(四)

异构计算关键技术之多线程技术(四)

最近遇到了一个项目,需要写一个用户态的测试程序(独立进程),用来测试FPGA PCIe DMA的性能,具体的要求如下:

1. 需要一个主线程,用来配置FPGA的寄存器,同时启动从线程;2. 如果不进行人为干涉,子线程一直进行FPGA的相关操作,比如下发trigger信号、配置burst次数、数据长度;3. 进行人为干涉,子线程退出,并返回子线程执行的一些信息,提供给主线程做统计和计算信息;

下面我们直接给出相关的核心代码,结合线程的理论进行分析:

...
...static int run = 0;
static int round = 0;...typedef struct _param
{struct util_mem *util;int burst;int len;
}param;void *
recv_perf(void *data)
{...cpu_set_t mask;CPU_ZERO(&mask);CPU_SET(14, &mask);sched_setaffinity(0, sizeof(cpu_set_t), &mask);.../* len and burst*/reg_write(..., addr, (p->len&0x0000ffff)|((p->burst&0x0000ffff)<<16)));...while(run) {/* trigger */reg_write(..., addr, &rdata);while(times < p->burst) {data_size = recv(...,...,...);if (data_size == xxx) {...times++;}}times = 0;cnt++;}round = cnt * p->burst;pthread_exit(0);
}int main(int argc, char **argv)
{...pthread_t tid;ret = start(...);ret = pthread_create(&tid, NULL, recv_perf, &data);if (ret < 0) {}else {}pthread_detach(tid);while(1) {ch = getchar();if (ch = 's') {run = 0;...break;}}...avg = (float)recv_total/round;...
}

一、代码设计分析

这段代码非常实用,整体思想如下:

  • 主线程main函数,首先做了FPGA系统的一些初始化功能,然后起了一个从线程recv_perf();

  • 从线程主要是根据传递的参数发送给FPGA,让FPGA一直做DMA操作;

  • 主从线程分离detach();

  • 主线程while(1)循环,用来控制从线程的结束,同时通过全局变量进行传递参数;

  • 最后计算FPGA的统计信息;


在这里插入图片描述

二、C++多线程编程知识点归纳

1. 主线程和子线程的区别

我们先看看线程是如何创建起来的:

进程仅仅是一个容器,包含了线程运行中所需要的数据结构等信息。一个进程创建时,操作系统会创建一个线程,这就是主线程。而其他的从线程,却要主线程的代码来创建,也就是由程序员来创建。

主线程

main()函数均视为主线程,除了“不包含在thread里面的程序”,均视为主线程;

子线程

包含在thread = new thread()里面均视为子线程;

main函数

main()函数作为入口开始运行,是一个进程,同时也是一个线程。在现在的操作系统中,都是多线程的。

2. 线程的创建与参数传递

这个实例中,我们需要做一个子线程,用来一直执行FPGA的操作,同时我们需要传递FPGA的配置参数,下发给FPGA寄存器空间。

linux下的多线程程序,需要使用pthread.h,链接时需要使用libthread.a。

线程的创建需要通过pthread_create来完成,声明如下:

#include <pthread.h>int pthread_create(pthread_t *thread, pthread_attr_t *attr, void* (start_routine)(void*), void *arg);
  • thread:是一个指针,线程创建成功时,用以返回创建的线程ID;
  • attr:线程属性,NULL表示使用默认;
  • start_rountine:函数指针,指被创建的线程函数;
  • arg:该参数指向传递给线程函数的参数;

实例中,接收函数recv_perf(),同时传递的参数结构体data;

3. 线程的退出

多线程中,终止执行的方式有3种,分别是:

1. 线程执行完成后,自行终止;
2. 线程执行种,遇到了pthread_exit()或者return;
3. 线程在执行过程种,接收到了其他线程发送的“终止执行”的信息,然后终止执行;

第一种很容易理解,不做讨论。

pthread_exit()和return
:

return

return 关键字用于终止函数执行,必要时还能将函数的执行结果反馈给调用者。
return 关键字不仅可以用于普通函数,线程函数中也可以使用它。

pthread_exit()

<pthread.h>头文件中,提供有一个和 return 关键字相同功能的 pthread_exit() 函数。
和之前不同,pthread_exit() 函数只适用于线程函数,而不能用于普通函数。
void pthread_exit(void*retval);

retval是void*类型的指针,可以指向任何类型的数据,它指向的数据作为线程退出的返回值。

pthread_exit()和return()的区别

  • return:不仅会终止主线程执行,还会终止其他子线程的执行;
  • pthread_exit():只会终止当前线程,不会影响到其他线程的执行;

实际场景中,想要终止某个子线程,强烈建议使用pthread_exit()函数。

pthread_cancel
:

一个线程还可以向另一个线程发送“终止执行”的信号(后续称为“cancel”信号),这时候需要调用pthread_cancel()函数。


int pthread_cancel(pthread_t thread);

参数thread用于接收cancel信号的目标线程。

对于接收cancel信号后,结束执行的目标线程,等同于该线程自己执行如下语句:

pthread_exit(PTHREAD_CANCELED);

也就是说,当一个线程被强制终止时,它会返回pthread_cancel这个宏。

然后对于我们这个设计,巧妙的使用了run这个全局变量,用来控制子线程执行,同时利用全局变量来进行计算,是个很好的策略。

这是因为子线程在detach()以后,就无法再返回子线程的资源,会出现core。

4. detach()

detach()的作用是将子线程和主线程的关联分离,也就是说detach()后子线程在后台独立继续执行,主线程无法再获得子线程的控制权。

即使主线程结束,子线程未执行也不会结束。当主线程结束时,由运行时库负责清理和子线程相关的资源。

detach()同时也带来了一些问题,如子线程要访问主线程的对象,而主线中的对象又因为主线程结束而被销毁,导致程序崩溃。

5. 把进程/线程绑定到特定的cpu核上运行

某个进程需要较高的运行效率时,就有必要考虑将其绑定到单独的核上运行,以减小由于在不同的核上调度造成的开销。

把某个进程/线程绑定到特定的cpu核上后,该进程就会一直在此核上运行,不会再被操作系统调度到其他核上。但绑定的这个核还是可能会被调度运行其他应用程序的。(可以做隔离)

查看绑定情况

taskset -p pid

显示的是十进制,需要转换成2进制,每个1对应一个cpu(cpu从0开始)

启动时绑定

taskset -c xxx,yyy ./pcie_perf&

启动应用程序的时候绑定。

启动后绑定

taskset -cp 1,2,5,11 9865  将进程9864绑定到#1、#2、#5、#11号核上面。taskset -cp 1,2,5-11 9865  将进程9864绑定到#1、#2、#5~#11号核上面。

代码绑定

...
cpu_set_t mask;
CPU_ZERO(&mask);
CPU_SET(14, &mask);
sched_setaffinity(0, sizeof(cpu_set_t), &mask);
...

三、未完待续

欢迎关注知乎:北京不北,+vbeijing_bubei欢迎+V:beijing_bubei欢迎关注douyin:near.X (北京不北)获得免费答疑,长期技术交流。

四、参考文献

https://blog.csdn.net/qq_41854911/article/details/118718824

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

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

相关文章

Python之字符串操作大全(29种方法)

本章详细介绍了常用的29种字符串操作方法及代码示例。 1. 重复输出字符串 print(x * 20) 输出&#xff1a;xxxxxxxxxxxxxxxxxxxx 2. 通过索引获取字符串 print(hello world[2:5]) 输出&#xff1a;llo 3. in 判断字符是否在字符串内 print(e in hello world) 输出&…

指针的函数传参的详细讲解(超详细)

如果对指针基础知识已经有可以直接跳到 函数的指针传参与解引用&#xff0c;哪里不明白可以评论&#xff0c;随时解答。 目录 所以就有了一句话&#xff1a;指针就是地址&#xff0c;地址就是指针 对于指针在C语言中&#xff0c;指针类型就是数据类型&#xff0c;是给编译器…

Effective C++ 学习笔记 条款23 宁以non-member、non-friend替换member函数

想象有个class用来表示网页浏览器。这样的class可能提供的众多函数中&#xff0c;有一些用来清除下载元素高速缓存区&#xff08;cache of downloaded elements&#xff09;、清除访问过的URLs的历史记录&#xff08;history of visited URLs&#xff09;、以及移除系统中的所有…

诚意满满之MySQL实现事务隔离的秘诀:锁与MVCC

如果对事务没有太多理解&#xff0c;可以看前面三篇&#xff1a; 诚意满满之讲透事务 诚意满满之讲透事务隔离级别 诚意满满之MySQL如何实现原子性、持久性 不看前两篇也没有关系&#xff0c;知识点是独立的。 MySQL的四个事务隔离级别&#xff1a;读未提交、读已提交、可重…

GoLang:云原生时代致力于构建高性能服务器的后端语言

Go语言的介绍 概念 Golang&#xff08;也被称为Go&#xff09;是一种编程语言&#xff0c;由Google于2007年开始设计和开发&#xff0c;并于2009年首次公开发布。Golang是一种静态类型、编译型的语言&#xff0c;旨在提供高效和可靠的软件开发体验。它具有简洁的语法、高效的编…

JS-12-关键字this、apply()、call()

一、对象的方法 在一个对象中绑定函数&#xff0c;称为这个对象的方法。 示例&#xff1a; 1、对象&#xff1a; var xiaoming {name: 小明,birth: 1990 }; 2、给xiaoming绑定一个函数。比如&#xff0c;写个age()方法&#xff0c;返回xiaoming的年龄&#xff1a; var x…

SwiftUI的context Menu

SwiftUI的 context Menu 现在来演示一下如何使用 SwiftUI 的 Context Menu 。 代码&#xff1a; import SwiftUIstruct ContextMenuBootCamp: View {State var bgColor: Color .purplevar body: some View {VStack(alignment: .leading, spacing: 10.0) {Image(systemName: …

音视频实战---从音视频文件中提取h264裸流

1、使用avformat_alloc_context分配解复用器上下文内存 2、使用avformat_open_input打开音视频文件或网络流 3、使用avformat_find_stream_info获取码流信息 4、使用 av_find_best_stream获取视频流下标 5、分配编码数据av_packet_alloc内存空间 6、使用av_init_packet初始…

Filebeat rpm方式安装及配置

一、使用服务器root用户、filebeat8.11.1版本,rpm安装方式进行安装 curl -L -O https://artifacts.elastic.co/downloads/beats/filebeat/filebeat-8.11.1-x86_64.rpm sudo rpm -vi filebeat-8.11.1-x86_64.rpm 二、配置核心的采集文件、使用inputs热更方式、配置filebeat本身…

LLVM-3.5 —— 01记,编译 LLVM 3.5.0 clang and clang-query

包括编译&#xff1a;clang clang-tools-extra 0, prepare env sudo apt install llvm sudo apt install clang 使用最新的g 会出错。 1, source code $ git clone --recursive $ cd llvm-project $ git checkout llvmorg-3.5.0 $ cp -r ./clang ./llvm/tools/ $ mkdir llv…

LeetCode202.快乐数

202快乐数 编写一个算法来判断一个数 n 是不是快乐数。 「快乐数」 定义为&#xff1a; 对于一个正整数&#xff0c;每一次将该数替换为它每个位置上的数字的平方和。 然后重复这个过程直到这个数变为 1&#xff0c;也可能是 无限循环 但始终变不到 1。 如果这个过程 结果为 1&…

python爬虫(10)之get()函数

1、headers 用于设置请求包中的请求头信息&#xff08;在很多网站会在那个请求包头加一层验证来防止他人爬取数据&#xff09; 当然前面已经讲过它是在哪里找 2、params 是用于模拟在发送动态请求时携带动态参数这种常用于那种在搜索框来进行爬取的行为 3、timeout 设置超…

冥想与AI:打造定制的放松体验

如今&#xff0c;在浏览网页或社交网络时&#xff0c;您似乎很难对一条条心理健康信息无动于衷。遇到这种情况的可不只是您。当今不断变化的时代给人们平添压力&#xff0c;企业纷纷利用智能技术满足人们的减压需求&#xff0c;让人们的生活多一些平和从容。 冥想就是一种练习呼…

客户满意度提升:电商平台使用API改善客户服务体验

客户满意度是电商平台成功的关键指标之一&#xff0c;而提供卓越的客户服务体验是提升满意度的重要途径。API&#xff08;应用程序编程接口&#xff09;在这一过程中扮演着至关重要的角色&#xff0c;因为它能够提高服务效率、个性化用户体验并确保信息的一致性。以下是电商平台…

HDOJ 2044

一只小蜜蜂… Problem Description 有一只经过训练的蜜蜂只能爬向右侧相邻的蜂房&#xff0c;不能反向爬行。请编程计算蜜蜂从蜂房a爬到蜂房b的可能路线数。 其中&#xff0c;蜂房的结构如下所示。 Input 输入数据的第一行是一个整数N,表示测试实例的个数&#xff0c;然后是N…

高效Go编程: encoding/csv标准库深度解析

高效Go编程: encoding/csv标准库深度解析 引言了解encoding/csv库CSV文件的基本结构encoding/csv库的核心功能应用场景 读取CSV文件基本步骤代码示例处理不同的分隔符错误处理 处理CSV数据数据解析代码示例处理不规则数据代码示例 写入CSV文件基本步骤代码示例自定义设置错误处…

Vue组件之间的通信方式

文章目录 组件间通信的概念组件间通信的分类组件间通信的方案父组件将方法传递给子组件&#xff08;props&#xff09;子组件向父组件传值(emit)通过 ref 属性获取DOM元素EventBus p a r e n t 或 parent 或 parent或 root a t t r s 与 attrs 与 attrs与 listeners provide 与…

Kotlin编程权威指南学习知识点预览

一、变量、常量和类型&#xff1a; 变量、常量以及 Kotlin 基本数据类型。变量和常量在 应用程序中可用来储值和传递数据。类型则用来描述常量或变量中保存的是什么样的数据。 1、声明变量: // 变量定义关键字 —— 变量名 —— 类型定义 —— 赋值运算符 —— 赋值var na…

cesium wall 扩散墙(动态立体墙效果 Primitive 方法)

cesium wall 扩散墙(动态立体墙效果)以下为源码直接复制可用 1、实现思路 1、此效果运用cesium 中 Primitive 方法,通过传入中心点、半径、顶点数、颜色来进行加载。 2、运用 Math 方法 对传进来的中心点、半径、定点数,来计算个顶点经纬度。 3、通过Primitive 方法中upda…

643.子数组最大平均数

题目&#xff1a;给你一个由 n 个元素组成的整数数组 nums 和一个整数k。 找出平均数最大且长度为 k 的连续子数组&#xff0c;并输出该最大平均数。 任何误差小于10^-5 的答案都将被视为正确答案。 解题思路&#xff1a;规定了子数组的长度为k&#xff0c;因此可以通过寻找子…