tag 标签: linux

相关帖子
相关博文
  • 2024-1-22 22:24
    193 次阅读|
    0 个评论
    1、Ftrace是什么 Ftrace是Function Trace的简写,由 Steven Rostedt 开发的,从 2008 年发布的内核 2.6.27 中开始就内置了。 Ftrace是一个系统内部提供的追踪工具,旨在帮助内核设计和开发人员去追踪系统内部的函数调用流程。 随着Ftrace的不断完善,除了追踪函数调用流程的作用外,还可以用来调试和分析系统的延迟和性能问题,并发展成为一个追踪类调试工具的框架。 除了Ftrace外,追踪类调试工具还包括: Tracing overview 2、Ftrace的实现原理 为了帮助我们更好的使用Ftrace,我们有必要简单了解Ftrace的实现原理。 2.1 Ftrace框架图 Ftrace的框架图如下: 在这里插入图片描述 由框架图我们可以知道: ftrace包括多种类型的tracers,每个tracer完成不同的功能 将这些不同类型的tracers注册进入ftrace framework 各类tracers收集不同的信息,并放入到Ring buffer缓冲区以供调用。 2.2 Ftrace是如何记录信息的 Ftrace采用了静态插桩和动态插桩两种方式来实现。 静态插桩 : 我们在Kernel中打开了CONFIG_FUNCTION_TRACER功能后,会增加一个-pg的一个编译选项,这个编译选项的作用就是为每个函数入口处,都会插入bl mcount跳转指令,使得每个函数运行时都会进入mcount函数。 Ftrace一旦使能,对kernel中所有的函数插桩,这带来的性能开销是惊人的,有可能导致人们弃用Ftrace功能。 为了解决这个问题,开发者推出了Dynamic ftrace,以此来优化整体的性能。 动态插桩 : 这里的动态,是指的动态修改函数指令。 编译时,记录所有被添加跳转指令的函数,这里表示所有支持追踪的函数。 内核将所有跳转指令替换为nop指令,以实现非调试状态性能零损失。 根据 function tracer 设置,动态将被调试函数的nop指令,替换为跳转指令,以实现追踪。 总而言之,Ftrace记录数据可以总结为以下几个步骤 : 打开编译选项-pg,为每个函数都增加跳转指令 记录这些可追踪的函数,并为了减少性能消耗,将跳转函数替换为nop指令 通过flag标志位来动态管理,将需要追踪的函数预留的nop指令替换回追踪指令,记录调试信息。 3、如何使用Ftrace 3.1 配置详解 CONFIG_FTRACE=y #启用了Ftrace CONFIG_FUNCTION_TRACER=y #启用函数级别的追踪器 CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y #表示内核支持图形显示 CONFIG_FUNCTION_GRAPH_TRACER=y #以图形的方式显示函数追踪过程 CONFIG_STACK_TRACER=y #启用堆栈追踪器,用于跟踪内核函数调用的堆栈信息。 CONFIG_DYNAMIC_FTRACE=y #启用动态Ftrace,允许在运行时启用和禁用Ftrace功能。 CONFIG_HAVE_FTRACE_NMI_ENTER=y #表示内核支持非屏蔽中断(NMI)时进入Ftrace的功能 CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y #表示内核支持通过mcount记录函数调用关系。 CONFIG_FTRACE_NMI_ENTER=y #表示内核支持通过mcount记录函数调用关系。 CONFIG_FTRACE_SYSCALLS=y #系统调用的追踪 CONFIG_FTRACE_MCOUNT_RECORD=y #启用mcount记录函数调用关系。 CONFIG_SCHED_TRACER=y #支持调度追踪 CONFIG_FUNCTION_PROFILER=y #启用函数分析器,主要用于记录函数的执行时间和调用次数 CONFIG_DEBUG_FS=y #启用Debug文件系统支持 上面只是介绍了部分配置,更多详细配置可自行了解。 并且上述配置不一定全部打开,勾选自己需要的即可,通常我们选择CONFIG_FUNCTION_TRACER和CONFIG_HAVE_FUNCTION_GRAPH_TRACER即可,然后编译烧录到开发板。 3.2 挂载debugfs文件系统 Ftrace是基于debugfs调试文件系统的,所以我们的第一步就是先挂载debugfs。 mount-tdebugfsnone/sys/kernel/debug 此时我们能够在/sys/kernel/debug下看到内核支持的所有的调试信息了。 #cd/sys/kernel/debug/ #ls asocgpioregmap bdiieee80211sched_debug blockmemblocksched_features clkmmc0sleep_time device_componentmmc1suspend_stats devices_deferredmtdtracing dma_bufoppubi extfragpinctrlubifs fault_around_bytespm_qoswakeup_sources 3.3 traceing目录介绍 在/sys/kernel/debug目录下,包含的是kernel所有的调试信息,本章只关注与tracing目录,下面挑选一些比较重要的属性文件来分析。 万变不离其宗,如此复杂的框架,设计人员已经提供了README文件,里面详解了各个属性文件的含义,我建议抛弃本文,看README吧:) 3.3.1 trace trace来清除。 3.3.2 trace_pipe trace_pipe 和 trace 一样,都是记录当前的追踪内容,但它和 trace 不一样的是: 对 trace_pipe 的读操作将会阻塞,直到有新的追踪数据进来为止; 当前从trace_pipe 读取的内容将被消耗掉,再次读 trace_pipe 又会阻塞到新数据进来为止。 简单的来说,cat trace_pipe是堵塞读取,有数据就读,没数据就等待;而cat trace有没有数据都是直接返回的 3.3.3 tracing_on tracing_on:向 tracing_on 写入 1,启用追踪;向 tracing_on 写入 0,停止追踪。 追踪使用 ring buffer 记录追踪数据。修改 tracing_on 不会影响 ring buffer 当前记录的内容。 3.3.4 current_tracer current_tracer 表示当前启用的 tracer ,默认为 nop ,即不做任何追踪工作: #catcurrent_tracer nop 3.3.5 available_filter_functions available_filter_functions:可以被追踪的函数列表,即可以写到 set_ftrace_filter,set_ftrace_notrace,set_graph_function,set_graph_notrace 文件的函数列表。 3.3.6 available_tracers available_tracers 文件中包含的是当前编译到内核的 tracer 列表,也表示当前内核支持的tracer列表。 该列表的内容,就是可以写到 current_tracer 的 tracer 名。 #catavailable_tracers function_graph function nop nop:表示为空,不追踪 function:追踪函数调用 function_graph:以图形形式追踪函数调用 3.3.7 buffer_size_kb buffer_size_kb 记录 CPU buffer 的大小,单位为 KB 。 per_cpu/cpuX/buffer_size_kb 记录 每个CPU buffer 大小,单位为 KB 。可通过写 buffer_size_kb 来改变 CPU buffer 的大小。 3.3.8 buffer_total_size_kb buffer_total_size_kb 记录所有 CPU buffer 的总大小,即所有 CPU buffer 大小总和。 如有 128 个 CPU buffer ,每个大小 7KB,则 buffer_total_size_kb 记录的总大小为 128 * 7KB = 896。 buffer_total_size_kb 文件是只读的。 3.3.9 set_ftrace_filter set_ftrace_filter :过滤函数追踪,仅仅追踪写入该文件的函数名。 可填入的参数,可以通过available_filter_functions文件查看当前支持的函数名。 该过滤功能,也有很多其他变体,如追踪某个模块的函数调用等。 官方给的示例: Format::mod: example: echo set_ftrace_filter #该模块必须是已经加载进去的模块 3.3.10 set_ftrace_notrace set_ftrace_notrace:和 set_ftrace_filter 刚好相反,系统禁用对其中列举函数的追踪。 3.3.11 set_ftrace_pid 系统对 set_ftrace_pid 文件中指定的 PID进程进行追踪。 如果开启了 options/function-fork 选项,fork 的子进程的 PID 也会自动加入文件,同时该选项也会引起系统自动将退出进程的 PID 从文件中移除。 3.3.12 set_graph_function 此文件中列出的函数将导致 函数图跟踪器仅跟踪这些函数以及它们调用的函数 。 但是该跟踪的记录,仍然受set_ftrace_filter 和 set_ftrace_notrace 的影响。 3.3.12 set_graph_notrace 与 set_graph_function 类似,但当函数被命中时,将禁用函数图跟踪,直到退出函数。 更多干货可见: 高级工程师聚集地 ,助力大家更上一层楼! 3.4 简单使用示例 一般我们挂载上debugfs后,tracing_on是处于打开状态的。 3.4.1 函数追踪 image-20240110110558815 3.4.2 追踪图形显示 image-20240110110617669 3.4.3 动态过滤追踪 image-20240110110641149 3.4.4 重置追踪 echo tracing_on #关闭trace echo trace #清空当前trace记录 catavailable_tracers #查看当前支持的追踪类型 echo current_tracer #设置当前的追踪类型 echo tracing_on #开启追踪 cattrace #查看追踪结果 4、进阶用法 上述章节,只是介绍了Ftrace最基本的命令,下面来看一下Ftrace在具体问题中的用法! 4.1 追踪任意命令 如何追踪我们执行的命令呢? Ftrace支持追踪特定进程,通过set_ftrace_pid属性来设置指定进程。然后在该进程中,执行特定的命令。 首先我们需要设置好我们的追踪器 mount-tdebugfsnone/sys/kernel/debug cd /sys/kernel/debug/tracing echo tracing_on #关闭追踪器 echo function current_tracer #设置当前追踪类别 在我们设置好追踪器后,使用如下命令,即可追踪我们执行的命令your_command echo trace; echo set_ftrace_pid; echo tracing_on;your_command; echo tracing_on 为什么要写成一条语句? 因为ftrace当打开时,在没有过滤的情况下,瞬间会抓取到内核所有的函数调用,为了更准确的抓取我们执行的命令,所以需要打开trace,执行完命令后,马上关闭。 4.2 追踪指定函数的调用流程 options/func_stack_trace 即可在 trace 结果中获取追踪函数的调用栈。 mount-tdebugfsnone/sys/kernel/debug cd /sys/kernel/debug/tracing echo tracing_on #关闭追踪器 catavailable_filter_functions|grep "xxxxxx" #搜索函数是否存在 echo set_ftrace_filter #设定追踪的函数 echo function current_tracer #设置当前追踪类别 echo options/func_stack_trace #记录堆栈信息 echo trace #清空缓存 echo tracing_on #开始追踪 效果如下 : #cattrace #tracer:function # #entries-in-buffer/entries-written:2/2#P:3 # irqs-off need-resched hardirq/softirq preempt-depth #|||/delay #TASK-PIDCPU#||||TIMESTAMPFUNCTION #||||||||| kworker/1:1-59 ....168.954199:mmc_rescan<-process_one_work kworker/1:1-59 ....168.954248: mmc_rescan process_one_work worker_thread kthread ret_from_fork 0 4.3 追踪指定模块的所有函数 要想我们的ko文件能够被Ftrace记录到,**我们需要在编译模块的时候,加上编译参数-pg**,这点很重要,否则你在available_filter_functions列表中,查找不到你想要的函数。 然后,需要我们设置过滤器,设置方法有以下几种: 按模块直接过滤 : #示例 Format::mod: example: echo set_ftrace_filter 追踪ext3模块内的所有函数 按函数直接过滤 如果该模块内的函数,命名都有一定的规则,可以按照正则表达式来过滤 #示例 echo "mmc*" set_ftrace_filter 过滤包含mmc字符的所有函数 按照函数差异来过滤 如果函数命名没有规律,又想过滤该模块所有函数,该怎么办? 按照加载模块前后的函数差异,写入到文件中来过滤 /tmp/1.txt /tmp/2.txt /tmp/3.txt cat/tmp/3.txt|sed 's/^+//' |awk '{print$1}' #如果diff出来格式前带有+-号,需要手动去掉 set_ftrace_filter 5、自动化管理 Ftrace功能很强大,在内核层面我们通过echo和cat即可获取我们想要的所有信息,但是通过一次一次敲命令显得有些繁琐,自己也对常用的功能整合了一个自动化脚本,能够通过命令行,直接追踪特定模块、函数、命令,极大提高了调试效率。 PS:自动化脚本获取:公~号【嵌入式艺术】 #/root/common_trace.sh Usage:/root/common_trace.sh{module|funcs|funcs_stack| command |clear} /root/common_trace.shmoduleext4 /root/common_trace.shfuncssysfs /root/common_trace.shfuncs_stacksysfs /root/common_trace.sh command sysfs /root/common_trace.shclear 脚本主要实现的功能有: 追踪指定模块,查看所有调用流程 追踪指定函数,查看该函数的调用链 追踪指定函数,获取堆栈信息 追踪用户命令,查看所有调用流程,并可选择指定函数来查看调用流程。 脚本除了command功能外,其他功能都需要手动调用common_trace.sh clear来停止追踪。 6、总结 以上,介绍了Ftrace的由来,实现原理,以及如何使用Ftrace,并最终提供了自动化测试脚本,希望对大家有所帮助。
  • 2024-1-19 14:39
    655 次阅读|
    0 个评论
    B y Toradex 胡珊逢 简介 在嵌入式Linux设备上常使用Qt作为开发图形界面应用的框架,随着web和移动端图形框架技术快速发展,嵌入式Linux也可以从这些技术中受益。由Google开发的Flutter最初用于Android和iOS应用开发,后期加入web、Windwos桌面和Linux桌面的支持。配合适当的渲染引擎,Flutter也可以运行在嵌入式Linux设备上。文章将介绍如何在使用Linux BSP的Verdin iMX8M Plus上运行Flutter应用。 硬件介绍 本次演示使用基于NXP iMX8M Plus SoC 的计算机模块 Verdin iMX8M Plus 。底板为 Dahlia ,该底板可以直接使用Verdin iMX8M Plus 的 HDMI 显示输出。屏幕则使用支持电容屏的 HDMI 显示器,用于测试 Flutter 应用的交互性。 Flutter介绍 Flutter使用Dart语言开发,可以跨平台运行,支持AOT(Ahead of time)预编译,使应用在设备上更高效地运行。对于Flutter应用,用户通常只需要开发apps代码,Framework、Engine和Embedder都油Flutter SDK提供。使用不同的embedder,Flutter app可以运行在不同的平台上。目前官方支持embedder包括Android,iOS,MacOS、Windwos桌面、Linux桌面、Web浏览器 Linux桌面需要依赖GTK和X11,现在的嵌入式Linux系统已经普遍使用Wayland,例如NXP的iMX处理器在新的BSP已经不再支持X11。对于嵌入式Linux系统,可以使用下面两个非官方的embedder。 Flutter-elinux ,这是由Sony 维护的项目,支持 Arm64 和 X64 处理器。可以使用 Wayland 或者 X11 后台,或者直接基于 DRM。 Flutter-pi 是针对树莓派开发和优化的embbeder,其不依赖于 X11 和 GTK 的任何组件。支持 KMS 和 DRI,3D 硬件加速。可以运行在 ARMv7、ARMv8、x86 处理器上。Flutter-pi 拥有更活跃的社区和开发者。结合 flutterpi_tool 工具可以非常容易地开发flutter 应用。 Verdin iMX8M Plus 的 Linux BSP 支持 KMS,其 CPU 也是 Armv8 架构。因此,我们本次选用 Flutter-pi 作为 embedder。关于 Flutter-elinux 在 Toradex 模块上的应用,可以参考 该文章 。 添加meta-flutter meta-flutter 提供了编译flutter-pi 以及 flutter demo 的文件。meta-flutter 依赖 meta-clang layer。Toradex Linux BSP v6 基于 Yocto Project 的 kirkstone 分支。首先,参考 这里 创建Yocto Project 编译环境。然后进入 layers 文件夹,下载 meta-flutter 和 meta-clang。 $ git clone --branch kirkstone https://github.com/meta-flutter/meta-flutter.git $ git clone --branch kirkstone https://github.com/kraj/meta-clang.git 然后在build/conf/bblayers.conf 文件的 BBLAYERS 中添加 meta-flutter 和 meta-clang 的路径。 BBLAYERS ?= " \ ${BBLAYERS_NXP} \ ... ${TOPDIR}/../layers/meta-clang \ ${TOPDIR}/../layerss/meta-flutter \ " 在build/conf/local.conf 文件的结尾添加以下内容。 DEFAULT_TIMEZONE = "Asia/Shanghai" ENABLE_BINARY_LOCALE_GENERATION = "1" IMAGE_LINGUAS:append = " en-us en-gb es-us zh-cn" GLIBC_GENERATE_LOCALES:append = " en_US.UTF-8 es_US.UTF-8 en_GB.UTF-8 zh_CN.UTF-8" IMAGE_INSTALL:append = " tzdata-core tzdata-asia" DISTRO_FEATURES:append = " opengl wayland pam" PACKAGECONFIG:append:pn-weston = " remoting" PACKAGECONFIG:append:pn-flutter-engine = " profile debug" IMAGE_INSTALL:append = " flutter-auto packagegroup-flutter-test-apps \ flutter-pi flutter-gallery" IMAGE_INSTALL:remove = " packagegroup-tdx-qt5" 删除meta-flutter/recipes-platform/packagegroups/ packagegroup-flutter-test-apps.bb 中的 flutter-test-texture-egl。最后编译 tdx-reference-multimedia-image 镜像文件。 $ bitbake tdx-reference-multimedia-image 测试Flutter 应用 禁用自启动的weston 服务。 root@verdin-imx8mp:~# systemctl stop weston.service root@verdin-imx8mp:~# systemctl disable weston.service 镜像中的/usr/share/flutter/gallery/3.16.5/release 中已经包含一个 flutter gallery 应用,现在可以使用 flutter-pi 直接运行。 root@verdin-imx8mp:# cd /usr/share/flutter/gallery/3.16.5/release root@verdin-imx8mp:# flutter-pi --release ./ 在HDMI 显示器上可以看到 gallery 应用的界面。 安装Flutter SDK 和 flutterpi_tool 在Linux 电脑(例如 Ubuntu 23.04)上,首先安装 Flutter SDK。由于 Flutter 和 flutter-pi 两个项目更新都比较活跃,在安装前需求确认 Flutter 版本。在 这里 查看flutter-pi 目前使用的 Flutter 版本。例如撰写文章时其为 3.16.7。在 Flutter SDK 手动安装页面 选择对应的版本进行安装,如flutter_linux_3.16.7-stable.tar.xz。 $ sudo apt-get install clang cmake git ninja-build \ pkg-config libgtk-3-dev liblzma-dev libstdc++-12-dev $ cd FLUTTER_SDK_INSTALL_PATH $ tar vxf flutter_linux_3.16.7-stable.tar.xz $ export PATH="$PATH:`pwd`/flutter/bin" $ flutter doctor -v 上面的flutter 环境诊断工具如果提示缺少 chrome 浏览器,可以安装后再运行。 $ sudo dpkg -i google-chrome-stable_current_amd64.deb $ flutter doctor -v 检查flutter 和 dart 命令是否都来自同一个的 SDK 安装目录。 $ which flutter dart /home/ben/flutter-sdk/flutter/bin/flutter /home/ben/flutter-sdk/flutter/bin/dart 安装完Flutter SDK 后使用下面命令安装 flutterpi_tool $ flutter pub global activate flutterpi_tool $ export PATH="$PATH":"$HOME/.pub-cache/bin" 使用下面命令测试 $ flutterpi_tool --help 如果已经安装好Flutter SDK 和 flutterpi_tool,需要编译其他 flutter 应用时,只需要进入之前 Flutter SDK 的安装目录后执行下面命令,重新配置编译环境即可。 $ cd FLUTTER_SDK_INSTALL_PATH $ export PATH="$PATH:`pwd`/flutter/bin" $ export PATH="$PATH":"$HOME/.pub-cache/bin" 使用flutterpi_tool 编译应用 借助flutterpi_tool 可以非常简单的编译 flutter 应用,例如针对 64 位 Arm CPU 的编译命令 flutterpi_tool build --arch=arm64 --cpu=pi4 --release ,参数说明如下: 参数 说明 Runtime mode --debug Build for debug mode. --profile Build for profile mode. --release Build for release mode. --debug-unoptimized Build for debug mode and use unoptimized engine. (For stepping throughengine code) Target,--arch= arm Build for 32-bit ARM. (armv7-linux-gnueabihf) arm64 Build for 64-bit ARM. (aarch64-linux-gnu) x64 Build for x86-64. (x86_64-linux-gnu) Target,--cpu= generic (default) Don’t use a tuned engine. The generic engine will work on all CPUs of the specified architecture. pi3 Use a Raspberry Pi 3 tuned engine. Compatible with arm and arm64. (-mcpu=cortex-a53+nocrypto -mtune=cortex-a53) pi4 Use a Raspberry Pi 4 tuned engine. Compatible with arm and arm64. (-mcpu=cortex-a72+nocrypto -mtune=cortex-a72) 下面是在Linux电脑上编译flutter gallery应用。由于Verdin iMX8M Plus是基于arm64的Cortex-A53 CPU,因此选择--arch=arm64和--cpu=pi3两个参数。 $ git clone https://github.com/flutter/gallery.git flutter_gallery $ cd flutter_gallery/ $ git checkout 6a8d738c94d0710e229d726729c09fdb5ccaf7ed $ flutter pub get $ flutterpi_tool build --arch=arm64 --cpu=pi3 --release 编译结束后,在build/flutter_assets 目录下可以看到如下文件。 build/flutter_assets/ ├── app.so ├── AssetManifest.bin ├── AssetManifest.json ├── FontManifest.json ├── fonts ├── icudtl.dat ├── libflutter_engine.so ├── NOTICES.Z ├── packages └── shaders flutteri_tool 目前使用新的编译方法生成的文件结构尚不能直接使用 flutter_pi 在设备上直接运行,需要重新调整文件位置,参考 Verdin iMX8M Plus 上的 /usr/share/flutter/gallery/3.16.5/release 的内容。 release/ ├── data │ ├── flutter_assets │ └── icudtl.dat └── lib ├── libapp.so └── libflutter_engine.so 在flutter_gallery/build/flutter_assets/ 目录创建 data 和 lib 两个文件夹,app.so 重命名为 libapp.so 后和 libflutter_engine.so 一起移动到 lib 目录。icudtl.dat 移动到 data 目录下。在 data 目录下创建 flutter_assets 文件夹后,将 build/flutter_assets/ 中剩余的 AssetManifest.bin,AssetManifest.json,FontManifest.json,fonts,NOTICES.Z,packages 和 shaders 均移动到 data/flutter_assets 中。完场后将 data 和 lib 两个目录复制到 Verdin iMX8M Plus 上的 /home/root/flutter_gallery 目录中,例如使用 scp 命令。 $ scp -r build/flutter_assets/ root@imx8mp_ip:/home/root/flutter_gallery 复制好文件后可以在Verdin iMX8M Plus 上的 /home/root/flutter_gallery 看到以下内容。 root@verdin-imx8mp-07320826:~/flutter_gallery# ls * data: flutter_assets icudtl.dat lib: libapp.so libflutter_engine.so 运行下面命令即可启动在电脑上编译的flutter 应用。 root@verdin-imx8mp:~# flutter-pi --release /home/root/flutter_gallery/ 总结 使用Flutter 可以开发跨平台应用,并获得流畅的本地运行体验,在移动端和 Web 端都有广泛的应用。但是针对嵌入式 Linux 的 embedder 目前是第三方维护的,所以在实际应用时需要做详尽的测试。 ​
  • 2024-1-11 17:55
    123 次阅读|
    0 个评论
    本文基于HD6UL-IOT评估板进行验证。HD6UL-IOT基于HD6UL-CORE工业级核心板设计(双网口、双CAN、7路串口),接口丰富,采用3.5寸工控板标准尺寸,适用于工业现场应用需求,亦方便用户评估核心板及CPU的性能。 1.查看dtb文件分区 使用cat/proc/mtd命令查看dtb设备树文件在nand中存储的分区。 2.提取dtb设备树文件 输入ddif=/dev/mtd1of=/home/xxx.dtb在mtd1分区中提取dtb设备树文件到home目录下。 3.反编译dtb设备树文件 提取出dtb文件后,将dtb文件拷贝到linux虚拟机内,进入到linux内核目录scripts/dtc下找到dtc工具使用一下目录对dtb设备树文件进行反编译: ./dtc-Idtb-Odts-oxxx.dtsxxx.dtb
  • 热度 1
    2024-1-10 17:57
    115 次阅读|
    0 个评论
    1引言 通常情况下工程师在调试Qt程序时,需要频繁制作镜像烧录到核心板来测试Qt程序是否完善,这样的操作既费时又费力。这时我们可以通过QtCreator设备功能,定义设备后,在x86_64虚拟机上交叉编译qt程序,将程序远程部署到arm64的机子上,然后远程调试,大大提高开发效率。 2.调试环境 本文基于HD-T113-EVM评估板进行验证,HD-T113-EVM基于HD-T113-S3核心板设计,具备千兆网口、双路CAN-bus、2路RS-232、2路RS-485、LCD、4G/5G、WiFi、音频等,接口丰富,适用于工业现场应用需求,亦方便用户评估核心板及CPU的性能。 Linux 主机 VM16.1.0+Ubuntu18.04 交叉编译器 arm-linux-gnueabihf Qt 版本 Qt5.12.12 核心板版本 HD-T113-S3v1.1 内核版本 Linuxkunos5.4.61#2SMPPREEMPTTueDec1217:29:56CST2023armv7lGNU/Linux 底板版本 HD-T113-EVMv1.2 2.1调试前准备 使win10主机、arm开发板和linux虚拟机处于同一网段且三者能互相ping通 配置虚拟机ip。虚拟机需要按照以下截图步骤进行设置。 2.2交叉编译环境设置 /work/T113_Tina5.0/out/t113/hd-t113-evm-emmc/buildroot/buildroot/host/qt5/bin/qmake 2.2.1编译器 GCC /work/T113_Tina5.0/out/t113/hd-t113-evm-emmc/buildroot/buildroot/host/bin/arm-linux-gnueabihf-gcc G++ /work/T113_Tina5.0/out/t113/hd-t113-evm-emmc/buildroot/buildroot/host/bin/arm-linux-gnueabihf-g++ 2.2.2Kits 2.3创建新设备 设备,添加新的GenericLinuxDevice设备。 2.3.1测试成功 2.3.2测试失败 1-错误描述SSHconnectionfailure 错误分析Noroutetotohst找不到主机 解决方案检查虚拟机与开发板ip是否处于同一网段并且能互相ping通 2-如虚拟机与开发板可以正常ping通但无法ssh登录开发板 原因:一台主机上有多个Linux系统,会经常切换,那么这些系统使用同一ip,登录过一次后就会把ssh信息记录在本地的/.ssh/known_hsots文件中,切换该系统后再用ssh访问这台主机就会出现冲突警告,需要手动删除修改known_hsots里面的内容。 有以下两个解决方案: 1.删除修改known_hsots记录,重新登录,使known_hsots生成新的记录; 2.修改配置文件“~/.ssh/config”,添加下两行, StrictHostKeyCheckingno UserKnownHostsFile/dev/null 重启服务器。 优缺点: 1.需要每次手动删除文件内容,一些自动化脚本的无法运行(在SSH登陆时失败),但是安全性高; 2.SSH登陆时会忽略known_hsots的访问,但是安全性低; Device 2.5部署应用 1-新建项目时,选择Kits为T113 2-编译应用 3-部署应用到开发板 4-提示 部署成功后,可能会提示。kbcommon:ERROR:failedtoadddefaultincludepath/usr/share/X11/xkb 5–运行结果展示
  • 热度 1
    2024-1-4 18:42
    245 次阅读|
    0 个评论
    在Windows Subsystem for Linux (WSL)中,你可以通过修改bashrc文件来禁用Windows下的环境变量。以下是具体步骤: 1. 打开终端:在WSL中,你可以使用Ctrl+Alt+T快捷键来打开终端。 2. 打开bashrc文件:在终端中输入以下命令来打开bashrc文件: ``` nano ~/.bashrc ``` 3. 在bashrc文件中添加以下行: ``` export DISABLE_WINDOWING_VARIABLES=true ``` 4. 保存并关闭bashrc文件:在nano编辑器中,你可以使用Ctrl+O快捷键来保存文件,然后使用Ctrl+X快捷键来关闭编辑器。 5. 使更改生效:在终端中输入以下命令来使更改生效: ``` source ~/.bashrc ``` 现在,你已经成功地在WSL Ubuntu中禁用了Windows下的环境变量。当你运行任何命令时,它将不再使用Windows下的环境变量。 需要注意的是,这种方法只会影响当前会话中的环境变量。如果你想要永久禁用这些环境变量,你需要将上述代码添加到你的bashrc文件中。这样,每次你启动新的bash会话时,这些环境变量都会被自动禁用。
相关资源
  • 所需E币: 0
    时间: 2023-12-25 10:31
    大小: 2.57KB
    上传者: 开心就很好了
    今天我将给大家讲解基于C++的Linux高性能事件驱动网络编程框架的设计方法及技巧,我在文中采取渐进迭代的方式,配合C++11新特性的使用,以及网络编程理论的深度讲解,并手把手带着大家落地实现,助力在网络编程领域有更大的技术提升!Linux系统的性能是指操作系统完成任务的有效性、稳定性和响应速度。Linux系统管理员可能经常会遇到系统不稳定、响应速度慢等问题,例如在Linux上搭建了一个web服务,经常出现网页无法打开、打开速度慢等现象,而遇到这些问题,就有人会抱怨Linux系统不好,其实这些都是表面现象。Linux提供三个「点分十进制字符串表示的IPv4地址和用网络字节序整数表示的IPv4地址之间转换」的接口 publicGraceJSONResultdoLogin(HttpServletRequestrequest,                  HttpServletResponseresponse,                  RegisterLoginBOregisterLoginBO,                  BindingResultresult){  //判断BindingResult是否保存错误的验证信息,如果有,则直接return  if(result.hasErrors()){    Map<String,String>errorMap=getErrors(result);    returnGraceJSONResult.errorMap(errorMap);  }  //获得前端传来的基本信息  StringsmsCode=registerLoginBO.getSmsCode();  Stringmobile=registerLoginBO.getMobile();  //0.校验验证码是否匹配  StringredisSMSCode=redis.get(MOBILE_SMSCODE+mobile);  if(StringUtils.isBlank(redisSMSCode)||!redisSMSCode.equalsIgnoreCase(smsCode)){    returnGraceJSONResult.errorCustom(ResponseStatusEnum.SMS_CODE_ERROR);  }  returnGraceJSONResult.ok();}用户信息其实并不会经常发生变动,所以这块内容完全可以放入缓存,这么一来可以大大减少对数据库的压力。privateAppUsergetUser(StringuserId){  //1.查询redis中是否包含用户信息,如果包含则查询redis返回,如果不包含则查询数据库  StringuserJson=redis.get(REDIS_USER_INFO+":"+userId);  AppUseruser=null;  if(StringUtils.isNotBlank(userJson)){    user=JsonUtils.jsonToPojo(userJson,AppUser.class);  }else{    user=userService.getUser(userId);    //2.由于用户信息不怎么会变动,对于千万级别的网站,这类信息数据不会去查询数据库,完全可以把用户信息存入redis    //哪怕修改信息,也不会立马体现,这也是弱一致性,在这里有过期时间,比如1天以后,用户信息会更新到页面显示,或者缩短到1小时,都可以    //基本信息在新闻媒体类网站是属于数据一致性优先级比较低的,用户眼里看的主要以文章为主,至于文章是谁发的,一般来说不会过多关注    redis.set(REDIS_USER_INFO+":"+userId,JsonUtils.objectToJson(user),1);  }  returnuser;}虽然在表设计的时候把文章阅读数字段进行了设计,但是在大数据量下,文章阅读的累计并发是很高的,在这里我们也是采用redis的计数功能来进行实现。@OverridepublicGraceJSONResultlist(StringarticleId,Integerpage,IntegerpageSize){  if(page==null){    page=COMMON_START_PAGE;  }  if(pageSize==null){    pageSize=COMMON_PAGE_SIZE;  }  PagedGridResultgridResult=         commentPortalService.queryArticleComments(articleId,                           page,                           pageSize);  returnGraceJSONResult.ok(gridResult);}生成html的步骤分为以下几步:定义freemarker生成的html位置配置freemarker基本环境获得ftl模板获得动态数据融合ftl和动态数据,并输出到html@Value("${freemarker.html.target}")privateStringhtmlTarget;@GetMapping("/createHTML")@ResponseBodypublicStringcreateHTML(Modelmodel)throwsException{  //0.配置freemarker基本环境  Configurationcfg=newConfiguration(Configuration.getVersion());  //声明freemarker模板所需要加载的目录的位置  Stringclasspath=this.getClass().getResource("/").getPath();  cfg.setDirectoryForTemplateLoading(newFile(classpath+"templates"));//    System.out.println(htmlTarget);//    System.out.println(classpath+"templates");  //1.获得现有的模板ftl文件  Templatetemplate=cfg.getTemplate("stu.ftl","utf-8");  //2.获得动态数据  Stringstranger=;  model.addAttribute("there",stranger);  model=makeModel(model);  //3.融合动态数据和ftl,生成html  FiletempDic=newFile(htmlTarget);  if(!tempDic.exists()){    tempDic.mkdirs();  }  Writerout=newFileWriter(htmlTarget+File.separator+"10010"+".html");  template.process(model,out);  out.close();  return"ok";}
  • 所需E币: 0
    时间: 2023-12-25 11:06
    大小: 3.48KB
    LinuxSocket网络编程框架主要由3大模块组成:BSDSocketAPIsSocketAbstractionLayerVFSLayerTCP/IP协议在设计和实现上并没有客户端和服务器的概念,在通信过程中所有机器都是对等的。但由于资源(视频、新闻、软件等)都被数据提供者所垄断,所以几乎所有的网络应用程序都很自然地用了客户端/服务器模型,即所有客户端都通过访问服务器来获取所需的资源。BS和CS服务器架构(1)CS架构介绍(clientserver,客户端服务器架构)(2)BS架构介绍(broswerserver,浏览器服务器架构)TCP协议(1)建立连接需要三次握手(2)建立连接的条件:服务器listen时客户端主动发起connect(3)关闭连接需要四次握手(4)服务器或者客户端都可以主动发起关闭packagecom.example.emos.wx.controller.form;importio.swagger.annotations.ApiModel;importlombok.Data;importjavax.validation.constraints.NotBlank;importjavax.validation.constraints.Pattern;@Data@ApiModelpublicclassRegisterForm{  @NotBlank(message="注册码不能为空")  @Pattern(regexp="^[0-9]{6}$",message="注册码必须是6位数字")  privateStringregisterCode;  @NotBlank(message="微信临时授权不能为空")  privateStringcode;  @NotBlank(message="昵称不能为空")  privateStringnickname;  @NotBlank(message="头像不能为空")  privateStringphoto;}在UserController.java中创建login()方法。@PostMapping("/login")@ApiOperation("登陆系统")publicRlogin(@Valid@RequestBodyLoginFormform){intid=userService.login(form.getCode());  Stringtoken=jwtUtil.createToken(id);  Set<String>permsSet=userService.searchUserPermissions(id);  saveCacheToken(token,id);  returnR.ok("登陆成功").put("token",token).put("permission",permsSet);}在CheckinServiceImpl类中,实现抽象方法……publicclassCheckinServiceImplimplementsCheckinService{……publicvoidcreateFaceModel(intuserId,Stringpath){    HttpRequestrequest=HttpUtil.createPost(createFaceModelUrl);    request.form("photo",FileUtil.file(path));    HttpResponseresponse=request.execute();    Stringbody=response.body();    if("无法识别出人脸".equals(body)||"照片中存在多张人脸".equals(body)){      thrownewEmosException(body);    }else{      TbFaceModelentity=newTbFaceModel();      entity.setUserId(userId);      entity.setFaceModel(body);      faceModelDao.insert(entity);    }  }}在CheckinServiceImpl.java类中,实现三个抽象方法。publicclassCheckinServiceImplimplementsCheckinService{……@Override  publicHashMapsearchTodayCheckin(intuserId){    HashMapmap=checkinDao.searchTodayCheckin(userId);    returnmap;  }  @Override  publiclongsearchCheckinDays(intuserId){    longdays=checkinDao.searchCheckinDays(userId);    returndays;  }  @Override  publicArrayList<HashMap>searchWeekCheckin(HashMapparam){    ArrayList<HashMap>checkinList=checkinDao.searchWeekCheckin(param);    ArrayList<String>holidaysList=holidaysDao.searchHolidaysInRange(param);    ArrayList<String>workdayList=workdayDao.searchWorkdayInRange(param);    DateTimestartDate=DateUtil.parseDate(param.get("startDate").toString());    DateTimeendDate=DateUtil.parseDate(param.get("endDate").toString());    DateRangerange=DateUtil.range(startDate,endDate,DateField.DAY_OF_MONTH);    ArrayListlist=newArrayList();    range.forEach(one->{      Stringdate=one.toString("yyyy-MM-dd");      //查看今天是不是假期或者工作日      Stringtype="工作日";      if(one.isWeekend()){        type="节假日";      }      if(holidaysList!=null&&holidaysList.contains(date)){        type="节假日";      }elseif(workdayList!=null&&workdayList.contains(date)){        type="工作日";      }      Stringstatus="";      if(type.equals("工作日")&&DateUtil.compare(one,DateUtil.date())<=0){        status="缺勤";booleanflag=false;        for(HashMap<String,String>map:checkinList){          if(map.containsValue(date)){            status=map.get("status");flag=true;            break;          }        }DateTimeendTime=DateUtil.parse(DateUtil.today()+""+constants.attendanceEndTime);Stringtoday=DateUtil.today();if(date.equals(today)&&DateUtil.date().isBefore(endTime)&&flag==false){          status="";        }      }      HashMapmap=newHashMap();      map.put("date",date);      map.put("status",status);      map.put("type",type);      map.put("day",one.dayOfWeekEnum().toChinese("周"));      list.add(map);    });    returnlist;  }}在EmosWxApiApplicationTests.java类中提供了contextLoads()测试用例方法,我们把生成大量系统消息记录的代码写在其中,程序运行的时候这些消息记录就会写入到MongoDB里面。@SpringBootTestclassEmosWxApiApplicationTests{  @Autowired  privateMessageServicemessageService;  @Test  voidcontextLoads(){    for(inti=1;i<=100;i++){      MessageEntitymessage=newMessageEntity();      message.setUuid(IdUtil.simpleUUID());      message.setSenderId(0);      message.setSenderName("系统消息");      message.setMsg("这是第"+i+"条测试消息");      message.setSendTime(newDate());      Stringid=messageService.insertMessage(message);      MessageRefEntityref=newMessageRefEntity();      ref.setMessageId(id);      ref.setReceiverId(11);//注意:这是接收人ID      ref.setLastFlag(true);      ref.setReadFlag(false);      messageService.insertRef(ref);    }  }}在该页面的模型层里面声明静态数据。list数组保存的是后端Java返回的成员数据,内容上按照部门进行分组。members数组保存的是页面上选择的成员id。#include<stdio.h>#include<sys/socket.h>#include<sys/types.h>#include<stdlib.h>#include<arpa/inet.h>#include<unistd.h>#include<string.h> #defineBACKLOG5 intmain(intargc,char*argv[]){  intfd;  structsockaddr_inaddr;  charbuf[BUFSIZ]={};   if(argc<3){    fprintf(stderr,"%s<addr><port>\n",argv[0]);    exit(0);  }   /*创建套接字*/  fd=socket(AF_INET,SOCK_STREAM,0);  if(fd<0){    perror("socket");    exit(0);  }   addr.sin_family=AF_INET;  addr.sin_port=htons(atoi(argv[2]));  if(inet_aton(argv[1],&addr.sin_addr)==0){    fprintf(stderr,"Invalidaddress\n");    exit(EXIT_FAILURE);  }   /*向服务端发起连接请求*/  if(connect(fd,(structsockaddr*)&addr,sizeof(addr))==-1){    perror("connect");    exit(0);  }  while(1){    printf("Input->");    fgets(buf,BUFSIZ,stdin);    write(fd,buf,strlen(buf));  }  close(fd);  return0;}
  • 所需E币: 0
    时间: 2023-12-19 21:14
    大小: 19.01MB
    上传者: xxxml
    Linux基础知识,非常全面
  • 所需E币: 3
    时间: 2023-12-15 09:34
    大小: 10.67MB
    上传者: 二月半
    Linux命令行与shell脚本编程大全(第4版)(RichardBlum、ChristineBresnahan) 
  • 所需E币: 0
    时间: 2023-12-6 15:17
    大小: 3.8KB
    网络编程概述管道(父子进程)、消息队列(内核经营消息队列)、共享内存(创建一个空间)、信号(通过pid号通信)、信号量(对临界资源,共享内存做P、V控制)。特点:依赖于Linux内核AB两个通信基于内核。缺陷:无法多机通信(不适用与两台不同的电脑)TCP和UDP对比:TCP面向连接(如打电话要先拨号建立连接);UDP是无连接的,即发送数据之前不需要建立连接TCP提供可靠的服务。也就是说,通过TCP连接传送的数据,无差错,不丢失,不重复,且按序到达;UDP尽最大努力交付,即不保证可靠交付TCP面向字节流,实际上是TCP把数据看成一连串无结构的字节流;UDP是面向报文的UDP没有拥塞控制,因此网络出现拥塞不会使源主机的发送速率降低(对实时应用很有用,如IP电话,实时视频会议等)每一条TCP连接只能是点到点的;UDP支持一对一,一对多,多对一和多对多的交互通信TCP首部开销20字节;UDP的首部开销小,只有8个字节TCP的逻辑通信信道是全双工的可靠信道,UDP则是不可靠信道传统的进程间通信借助内核提供的IPC机制进行,但是只能限于本机通信。若要跨机通信,就必须使用网络通信,这就需要用到内核提供给用户的socketAPI函数库。2.1网络字节序大端字节序:也叫高端字节序(网络字节序),是高端地址存放低位数据,低端地址存放高位数据小端字节序:也叫低端字节序,是低地址存放低位数据,高地址存放高位数据。在application.yml文件中,填入SaToken的配置信息,如下:sa-token: #HTTP请求头中哪个属性用来上传令牌 token-name:token #过期时间(秒),设置为30天 timeout:2592000 #临时有效期,设置为3天 activity-timeout:259200 #不允许相同账号同时在线,新登陆的账号会挤掉原来登陆的账号 allow-concurrent-login:false #在多人登陆相同账号的时候,是否使用相同的Token is-share:false token-style:uuid #是否读取Cookie中的令牌 isReadCookie:false #同端互斥 isConcurrent:false #SaToken缓存令牌用其他的逻辑库,避免业务数据和令牌数据共用相同的Redis逻辑库 alone-redis:  database:1  host:localhost  port:6379  password:abc123456  timeout:10s  lettuce:   pool:    #连接池最大连接数    max-active:200    #连接池最大阻塞等待时间(使用负值表示没有限制)    max-wait:10s    #连接池中的最大空闲连接    max-idle:16    #连接池中的最小空闲连接    min-idle:8Java语言允许我们自己封装异常类,我们可以自定义各种异常类,比如每种业务一个异常类,或者每个模块一个异常类。我这里不想做的那么复杂,不如我们创建一个通用的异常类,用来封装与业务有关的异常信息。在com.example.his.api.exception包中,创建HisException.java类。packagecom.example.his.api.exception;importlombok.Data;@DatapublicclassHisExceptionextendsRuntimeException{  privateStringmsg;  privateintcode=500;  publicHisException(Exceptione){    super(e);    this.msg="执行异常";  }  publicHisException(Stringmsg){    super(msg);    this.msg=msg;  }  publicHisException(Stringmsg,Throwablee){    super(msg,e);    this.msg=msg;  }  publicHisException(Stringmsg,intcode){    super(msg);    this.msg=msg;    this.code=code;  }  publicHisException(Stringmsg,intcode,Throwablee){    super(msg,e);    this.msg=msg;    this.code=code;  }}SpringBoot提供了全局处理异常的技术,只要我们给某个Java类用上@RestControllerAdvice注解,这个类就能捕获SpringBoot项目中所有的异常,然后统一处理(精简异常信息)再返回给前端项目。在com.example.his.api.config包中,创建ExceptionAdvice.java类。packagecom.example.his.api.config;importcn.dev33.satoken.exception.NotLoginException;importcn.felord.payment.PayException;importcn.hutool.json.JSONObject;importcom.example.his.api.exception.HisException;importlombok.extern.slf4j.Slf4j;importorg.springframework.validation.BindException;importorg.springframework.http.HttpStatus;importorg.springframework.http.converter.HttpMessageNotReadableException;importorg.springframework.web.HttpRequestMethodNotSupportedException;importorg.springframework.web.bind.MethodArgumentNotValidException;importorg.springframework.web.bind.annotation.ExceptionHandler;importorg.springframework.web.bind.annotation.ResponseBody;importorg.springframework.web.bind.annotation.ResponseStatus;importorg.springframework.web.bind.annotation.RestControllerAdvice;importorg.springframework.web.multipart.support.MissingServletRequestPartException;@Slf4j@RestControllerAdvicepublicclassExceptionAdvice{  /*   *捕获异常,并且返回500状态码   */  @ResponseBody  @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)  @ExceptionHandler(Exception.class)  publicStringexceptionHandler(Exceptione){    JSONObjectjson=newJSONObject();    if(einstanceofHttpMessageNotReadableException){      HttpMessageNotReadableExceptionexception=(HttpMessageNotReadableException)e;      log.error("error",exception);      json.set("error","请求未提交数据或者数据有误");    }     elseif(einstanceofMissingServletRequestPartException){      MissingServletRequestPartExceptionexception=(MissingServletRequestPartException)e;      log.error("error",exception);      json.set("error","请求提交数据错误");    }     elseif(einstanceofHttpRequestMethodNotSupportedException){      HttpRequestMethodNotSupportedExceptionexception=(HttpRequestMethodNotSupportedException)e;      log.error("error",exception);      json.set("error","HTTP请求方法类型错误");    }     //Web方法参数数据类型转换异常,比如String[]数组类型的参数,你上传的数据却是String类型    elseif(einstanceofBindException){      BindExceptionexception=(BindException)e;      StringdefaultMessage=exception.getFieldError().getDefaultMessage();      log.error(defaultMessage,exception);      json.set("error",defaultMessage);    }    //没有通过后端验证产生的异常    elseif(einstanceofMethodArgumentNotValidException){      MethodArgumentNotValidExceptionexception=(MethodArgumentNotValidException)e;      json.set("error",exception.getBindingResult().getFieldError().getDefaultMessage());    }    //处理业务异常    elseif(einstanceofHisException){      log.error("执行异常",e);      HisExceptionexception=(HisException)e;      json.set("error",exception.getMsg());    }     //微信支付异常    elseif(einstanceofPayException){      PayExceptionexception=(PayException)e;      log.error("微信支付异常",exception);      json.set("error","微信支付异常");    }    //处理其余的异常    else{      log.error("执行异常",e);      json.set("error","执行异常");    }    returnjson.toString();  }  /*   *捕获异常,并且返回401状态码   */  @ResponseBody  @ResponseStatus(HttpStatus.UNAUTHORIZED)  @ExceptionHandler(NotLoginException.class)  publicStringunLoginHandler(Exceptione){    JSONObjectjson=newJSONObject();    json.set("error",e.getMessage());    returnjson.toString();  }}因为Controller类用上@RestController注解之后,Web方法返回的对象会被自动转换成JSON对象,所以我们只需要声明一个封装类,让所有Web方法返回这个封装类的对象即可。除了公共属性之外,不同的Web方法要返回的业务数据也不尽相同,所以选择动态的结构才是最佳的方案,恰好HashMap允许我们随便添加数据,那就选择HashMap作为父类吧。在com.example.his.api.common包中,创建R.java类。packagecom.example.his.api.common;importorg.apache.http.HttpStatus;importjava.util.HashMap;importjava.util.Map;publicclassRextendsHashMap<String,Object>{  publicR(){    //默认创建的R对象中包含了公共的属性    put("code",HttpStatus.SC_OK);    put("msg","success");  }  /*   *覆盖继承的put函数,添加Key-Value数据   */  publicRput(Stringkey,Objectvalue){    super.put(key,value);    //把自己返回,用于链式调用    returnthis;  }  publicstaticRok(){    returnnewR();  }  publicstaticRok(Stringmsg){    Rr=newR();    r.put("msg",msg);    returnr;  }  publicstaticRok(Map<String,Object>map){    Rr=newR();    r.putAll(map);    returnr;  }  publicstaticRerror(intcode,Stringmsg){    Rr=newR();    r.put("code",code);    r.put("msg",msg);    returnr;  }  publicstaticRerror(Stringmsg){    returnerror(HttpStatus.SC_INTERNAL_SERVER_ERROR,msg);  }  publicstaticRerror(){    returnerror(HttpStatus.SC_INTERNAL_SERVER_ERROR,"未知异常,请联系管理员");  }}
  • 所需E币: 5
    时间: 2023-11-14 17:51
    大小: 21.11MB
    上传者: 浩瀚星蓝
    这是一本学习linux的经典书籍,详细介绍了shell的使用方法
  • 所需E币: 2
    时间: 2023-11-14 18:00
    大小: 85.65MB
    上传者: 浩瀚星蓝
    这本是是Linux典藏大系的其中一本,详细讲述了ARM嵌入式相关知识
  • 所需E币: 0
    时间: 2023-11-10 15:46
    大小: 1021KB
    上传者: Argent
    第11讲Linux内核顶层Makefile分析
  • 所需E币: 0
    时间: 2023-11-10 15:46
    大小: 1005.5KB
    上传者: Argent
    第10讲Linux内核源码目录分析
  • 所需E币: 0
    时间: 2023-11-10 15:46
    大小: 1006KB
    上传者: Argent
    第9讲正点原子官方Linux内核编译与体验
  • 所需E币: 0
    时间: 2023-11-10 15:47
    大小: 1014KB
    上传者: Argent
    第13讲Linux内核移植.ppt
  • 所需E币: 0
    时间: 2023-11-10 15:48
    大小: 1011KB
    上传者: Argent
    第17讲Linux内核移植(NAND版本)-补录
  • 所需E币: 0
    时间: 2023-11-10 15:47
    大小: 1MB
    上传者: Argent
    第12讲Linux内核启动流程简介
  • 所需E币: 0
    时间: 2023-11-10 16:41
    大小: 1011KB
    上传者: Argent
    第13讲Linux连接文件
  • 所需E币: 0
    时间: 2023-11-10 16:41
    大小: 1018.5KB
    上传者: Argent
    第15讲LinuxC编程
  • 所需E币: 1
    时间: 2023-11-10 11:50
    大小: 30.02KB
    上传者: Argent
    第34讲LinuxPWM驱动实验
  • 所需E币: 1
    时间: 2023-11-10 11:47
    大小: 28.75KB
    上传者: Argent
    第22讲LinuxIIC驱动实验_笔记
  • 所需E币: 1
    时间: 2023-11-10 11:48
    大小: 36.78KB
    上传者: Argent
    第23讲LinuxSPI驱动实验_笔记
  • 所需E币: 1
    时间: 2023-11-10 11:51
    大小: 27.83KB
    上传者: Argent
    第37讲LinuxADC驱动实验
  • 所需E币: 1
    时间: 2023-11-10 11:48
    大小: 29.9KB
    上传者: Argent
    第25讲Linux多点电容触摸屏实验_笔记