Android Audio音频系统

news2025/1/24 14:36:59

一、Audio音频架构简介

二、Android Audio系统框架

三、Audio架构以及各层的代码分布图

四、音频框架在Android系统中的进一步细化

五、创建声卡和注册声卡

六、Android Audio系统的结构

七、Audio音频原理介绍

八、Audio音频策略制定与策略执行的调用流程

九、Android AudioPolicyService服务启动过程

十、Android系统中所有的音频接口设备保存到AudioFlinger的成员变量mAudioHwDevs中

十一、audio_policy.conf同时定义了多个audio接口

十二、通过AudioFlinger的loadHwModule加载各audio接口对应的库文件实现调用PlaybackThread播放线程或RecordThread录音线程

十三、AudioFlinger的openInput()方法调用流程分析

十四、AudioFlinger的openOutput()方法的调用流程分析

十五、Audio系统为了能正常播放音频数据,需要创建抽象的音频输出接口对象,打开音频输出过程

十六、打开音频输入的流程

十七、打开音频输出后,在AudioFlinger与AudioPolicyService中的表现形式

十八、打开音频输入后,在AudioFlinger与AudioPolicyService中的表现形式

十九、AudioPolicyService加载完系统定义的所有音频接口,并生成相应的数据对象

二十、AudioPolicyService与AudioTrack和AudioFlinger的关系

二十一、AudioPolicyService注册名为服务的流程

二十二、AudioTrack构造过程

二十三、AudioTrack和AudioFlinger的关系

二十四、audio_policy与AudioPolicyService、AudioPolicyCompatClient之间的关系

图片

一、Audio音频架构简介

APP

整个音频体系的最上层

Framework

MediaPlayer和MediaRecorder、AudioTrack和AudioRecorder,Android系统为控制音频系统提供了AudioManager、AudioService及AudioSystem类,这些都是framework为便利上层应用开发所设计的

Libraries

系统服务AudioFlinger和AudioPolicyService(比如:ServiceManager、LocationManagerService、ActivityManagerService等等),音频体系中另一个重要的系统服务是MediaPlayerService

HAL

硬件抽象层是AudioFlinger直接访问的对象,这说明了两个问题,一方面AudioFlinger并不直接调用底层的驱动程序;另一方面,AudioFlinger上层(包括和它同一层的MediaPlayerService)的模块只需要与它进行交互就可以实现音频相关的功能了。因而我们可以认为AudioFlinger是Android音频系统中真正的“隔离板”,无论下面如何变化,上层的实现都可以保持兼容。音频方面的硬件抽象层主要分为两部分,即AudioFlinger和AudioPolicyService。实际上后者并不是一个真实的设备,只是采用虚拟设备的方式来让厂商可以方便地定制出自己的策略,抽象层的任务是将AudioFlinger/AudioPolicyService真正地与硬件设备关联起来

以前Android系统中的Audio系统依赖于ALSA-lib,但后期就变为了tinyalsa,这样的转变不应该对上层造成破坏。因而Audio HAL提供了统一的接口来定义它与AudioFlinger/AudioPolicyService之间的通信方式,这就是audio_hw_device、audio_stream_in及audio_stream_out等等存在的目的,这些Struct数据类型内部大多只是函数指针的定义,是一些“壳”。当AudioFlinger/AudioPolicyService初始化时,它们会去寻找系统中最匹配的实现(这些实现驻留在以audio.primary.*,audio.a2dp.*为名的各种库中)来填充这些“壳”

理解Android音频系统的时候分为两条线索

以库为线索,比如:AudioPolicyService和AudioFlinger都是在libaudioflinger库中,而AudioTrack、AudioRecorder等一系列实现则在libmedia库中

以进程为线索,库并不代表一个进程,进程则依赖于库来运行。虽然有的类是在同一个库中实现的,但并不代表它们会在同一个进程中被调用。比如AudioFlinger和AudioPolicyService都驻留于名为mediaserver的系统进程中,而AudioTrack/AudioRecorder和MediaPlayer/MediaRecorder一样实际上只是应用进程的一部分,它们通过binder服务来与其它系统进程通信

二、Android Audio系统框架

图片

三、Audio架构以及各层的代码分布图

图片

四、音频框架在Android系统中的进一步细化

图片

五、创建声卡和注册声卡

图片

六、Android Audio系统的结构

图片

七、Audio音频原理介绍

AudioFlinger、AudioPolicyService和AudioTrack/AudioRecorder抛开MediaPlayer、MediaRecorder这些与应用开发直接关联的部分,整个音频系统的核心就是由这三者构建而成的。其中前两个都是System Service,驻留在mediaserver进程中,不断地处理AudioTrack/AudioRecorder的请求。音频的回放和录制从大的流程上看都是相似的,所以我们侧重于对AudioTrack的分析

把所有媒体相关的native层服务(包括AudioFlinger,MediaPlayerService,CameraService和AudioPolicyService)启动起来,编译生成的mediaserver将被烧录到设备的/system/bin/mediaserver路径,然后由系统启动时的init进程启动

Audio系统的结构

libmedia.so提供Audio接口,这些Audio接口既向上层开放,也向本地代码开放

libaudiofilnger.so提供Audio接口实现

Audio硬件抽象层提供到硬件的接口,供AudioFlinger调用

Audio使用JNI和JAVA对上层提供接口

media库中的Audio框架部分

Android 的Audio的核心框架在media库中提供,其中对上面主要实现AudioSystem、AudioTrack和AudioRecorder三个类。提供了IAudioFlinger类接口,在这个类中,可以获得IAudioTrack和IAudioRecorder两个接口,分别用于声音的播放和录制。AudioTrack和AudioRecorder分别通过调用IAudioTrack和IAudioRecorder来实现

Audio系统的头文件

路径为:frameworks/base/include/media/

AudioSystem.h

IAudioFlinger.h

AudioTrack.h

IAudioTrack.h

AudioRecorder.h

IAudioRecorder.h

Ixxx的接口通过AudioFlinger来实现,其他接口通过JNI向上层提供接口

Audio系统的头文件在frameworks/base/include/media/目录中,主要的头文件如下

AudioSystem.h:media库的Audio部分对上层的总管接口

IAudioFlinger.h:需要下层实现的总管接口

AudioTrack.h:放音部分对上接口

IAudioTrack.h:放音部分需要下层实现的接口

AudioRecorder.h:录音部分对上接口

IAudioRecorder.h:录音部分需要下层实现的接口

IAudioFlinger.h、IAudioTrack.h和IAudioRecorder.h这三个接口通过下层的继承来实现(即:AudioFlinger)

AudioFlinger.h,AudioTrack.h和AudioRecorder.h是对上层提供的接口,它们既供本地程序调用(例如:声音的播放器、录制器等),也可以通过JNI向Java层提供接口

AudioTrack和AudioRecorder都具有start,stop和pause等接口。前者具有write接口,用于声音的播放,后者具有read接口,用于声音的录制

AudioSystem用于Audio系统的控制工作,主要包含一些set和get接口,是一个对上层的类

AudioFlinger是Audio系统的核心,来自AudioTrack的数据,最终在这里得到处理并被写入Audio HAL层

MediaPlayer在framework层还是会创建AudioTrack,把解码后的PCM数流传递给AudioTrack,AudioTrack再传递给AudioFlinger进行混音,然后才传递给硬件播放,所以是MediaPlayer包含了AudioTrack。使用AudioTrack播放音乐

MediaPlayer提供了更完整的封装和状态控制,相比MediaPlayer,AudioTrack更为精练、高效,实际上MediaPlayerService的内部实现就是使用了AudioTrack把所有媒体相关的native层服务(包括AudioFlinger,MediaPlayerService,CameraService和AudioPolicyService)启动起来,编译生成的mediaserver将被烧录到设备的/system/bin/mediaserver路径,然后由系统启动时的init进程启动

两种Audio Hardware HAL接口定义

legacy:hardware/libhardware_legacy/include/hardware_legacy/AudioHardwareInterface.h

非legacy:hardware/libhardware/include/hardware/audio.h

前者是2.3及之前的音频设备接口定义,后者是4.0的接口定义,为了兼容以前的设计,4.0实现一个中间层:hardware/libhardware_legacy/audio/audio_hw_hal.cpp,结构与其他的audio_hw.c大同小异,差别在于open方法事实上legacy也要封装成非legacy中的audio.h,确切的说需要一个联系legacy interface和not legacy interface的中间层,这里的audio_hw_hal.cpp就充当这样的一个角色了

hardware/libhardware/modules/audio/

createAudioHardware()函数

external/tinyalsa/

mixer.c      类alsa-lib的control,作用音频部件开关、音量调节等

pcm.c        类alsa-lib的pcm,作用音频pcm数据回放录制

上面的hardware/libhardware_legacy/audio/、hardware/libhardware/modules/audio/、device/samsung/tuna/audio/是同层的。之一是legacy audio,用于兼容2.2时代的alsa_sound;之二是stub audio接口;之三是Samsung Tuna的音频抽象层实现。调用层次:AudioFlinger -> audio_hw -> tinyalsa

Audio硬件抽象层的实现在各个系统中可能是不同的,需要使用代码去继承相应的类并实现它们,作为Android系统本地框架层和驱动程序接口AudioFlinger继承了libmedia.so(Audio本地框架类)里面的接口,上层调用的只是libmedia.so部分的接口,但实际上调用的内容是libaudioflinger.so,使用JNI和Java对上层提供接口,JNI部分通过调用libmedia.so库提供的接口来实现

Audio硬件抽象层提供到硬件的接口,供AudioFlinger调用,Audio的硬件抽象层实际上是各个平台开发过程中需要主要关注和独立完成的部分,因为Android中的Audio系统不涉及编解码环节,只负责上层系统和底层Audio硬件的交互,所以通常以PCM作为输入/输出格式

IAudioFlinger类接口通过该类可以获得IAudioTrack和IAudioRecorder两个接口,分别用于声音的播放和录制,AudioTrack和AudioRecorder分别通过调用IAudioTrack和IAudioRecorder来实现

 硬件抽象层主要实现了AudioStreamInALSA和AudioStreamOutALSA两个类,这两个类又会调用该文件下的 ALSAStreamOps类的方法。AudioStreamInALSA是录音部分调用的路径。在AudioStreamInALSA的构造函数中会对alsa进行一些初始化参数设置,

AudioStreamInALSA的read方法是最主要的方法,audioflinger层的read调用就是对 AudioStreamInALSA的read的调用。由于

录音部分出现单声道和双声道数据传输的问题,修改read方法如下,即可实现了录音功能正常, 避免了在编码的时候修改数据时其他编码仍不能工作的弊端

八、Audio音频策略制定与策略执行的调用流程

图片

AudioPolicyService是策略的制定者,AudioFlinger则是策略的执行者

AudioTrack是AudioFlinger的客户端,AudioFlinger是Android系统中Audio管理的中枢

在Android中AudioPolicyService最终都会调用到AudioFlinger中去,因为AudioFlinger实际创建并管理了硬件设备

AudioFlinger类是代表整个AudioFlinger服务的类,其余所有的工作类都是通过内部类的方式在其中定义的

九、Android AudioPolicyService服务启动过程

AudioPolicyService完成的工作

加载audio_policy.default.so库得到audio_policy_module模块

通过audio_policy_module模块打开audio_policy_device设备

通过audio_policy_device设备创建audio_policy

hw_get_module函数加载硬件抽象层模块的过程

audio_policy实现在audio_policy_hal.cpp中,audio_policy_service_ops实现在AudioPolicyService.cpp中。create_audio_policy()函数就是创建并初始化一个legacy_audio_policy对象,AudioPolicyCompatClient是对audio_policy_service_ops的封装类,对外提供audio_policy_service_ops数据结构中定义的接口

Android AudioPolicyService服务启动过程

引用AudioPolicyCompatClient对象,这样音频管理器AudioPolicyManager就可以使用audio_policy_service_ops中的接口

优先加载/vendor/etc/audio_policy.conf配置文件,如果该配置文件不存在,则加载/system/etc/audio_policy.conf配置文件,如果该文件还是不存在,则通过函数defaultAudioPolicyConfig()来设置默认音频接口

设置各种音频流对应的音量调节点

通过名称打开对应的音频接口硬件抽象库

打开mAttachedOutputDevices对应的输出

将输出IOProfile封装为AudioOutputDescriptor对象

设置当前音频接口的默认输出设备

打开输出,在AudioFlinger中创建PlaybackThread线程,并返回该线程的id

设置可以使用的输出设备为mAttachedOutputDevices

将输出描述符对象AudioOutputDescriptor及创建的PlaybackThread线程id以键值对形式保存

设置默认输出设备

AudioPolicyManagerBase对象构造过程中主要完成以下几个步骤

loadAudioPolicyConfig(AUDIO_POLICY_CONFIG_FILE)加载audio_policy.conf配置文件

initializeVolumeCurves()初始化各种音频流对应的音量调节点

加载audio policy硬件抽象库:mpClientInterface->loadHwModule(mHwModules[i]->mName)

打开attached_output_devices输出mpClientInterface->openOutput();

保存输出设备描述符对象addOutput(output, outputDesc);


十、Android系统中所有的音频接口设备保存到AudioFlinger的成员变量mAudioHwDevs中

图片

audio_policy.conf文件

从audio_policy.conf文件中可以发现,系统包含了primary、a2dp、usb等音频接口,对应着系统中的audio.<primary/a2dp/usb>.<device>.so。每个音频接口中又包含了若干个outputs & inputs,并且每个output or input又包含了若干个devices,且还有采样频率,声道数等信息。这些devices信息、采样频率信息 & 声道信息等都会保存在各自module的IOProfile中。按上文中audio_policy.conf配置文件所描述,系统最后会生成6个modules(eg.primary,a2dp,hdmi,r_submix,hs_usb & usb)以及7个outputs


十一、audio_policy.conf同时定义了多个audio接口

图片

不同的Android产品在音频的设计上通常是存在差异的,而这些差异可以通过Audio的配置文件audio_policy.conf来获得。在Android系统中音频配置文件存放路径有两处,存放地址可以从AudioPolicyManagerBase.cpp文件中获取

在AudioPolicyManager.cpp文件中可以知道系统会首先加载vendor/etc目录下的configure文件,再加载system/etc目录下的configure文件。若这两者加载都发生错误的话,系统会加载default配置文件,并命名为primary module

通过loadGlobalConfig(root)函数来读取这些全局配置信息,通过loadHwModules()函数来加载系统配置的所有audio接口(加载音频接口),由于audio_policy.conf可以定义多个音频接口,因此该函数循环调用loadHwModule()来解析每个音频接口参数信息。Android定义HwModule类来描述每一个audio接口参数,定义IOProfile类来描述输入输出模式配置

/system/etc/audio_policy.conf

/vendor/etc/audio_policy.conf

加载audio_module模块

AudioPolicyManager通过读取audio_policy.conf配置文件,可以知道系统当前支持哪些音频接口以及attached的输入输出设备、默认输出设备。接下来就需要加载这些音频接口的硬件抽象库

AudioPolicyClientInterface提供了加载音频接口硬件抽象库的接口函数,AudioPolicyCompatClient通过代理audio_policy_service_ops实现AudioPolicyClientInterface接口

AudioPolicyCompatClient将音频模块加载工作交给audio_policy_service_ops,AudioPolicyService又将其转交给AudioFlinger 

当AudioPolicyManagerBase构造时,它会根据用户提供的audio_policy.conf来分析系统中有哪些audio接口(primary,a2dp以及usb),然后通过AudioFlinger::loadHwModule加载各audio接口对应的库文件,并依次打开其中的output(openOutput)和input(openInput)

打开音频输出时创建一个audio_stream_out通道,并创建AudioStreamOut对象以及新建PlaybackThread播放线程

打开音频输入时创建一个audio_stream_in通道,并创建AudioStreamIn对象以及创建RecordThread录音线程 

audio_policy.conf文件定义了两种音频配置信息 

当前系统支持的音频输入输出设备及默认输入输出设备,这些信息是通过global_configuration配置项来设置,在global_configuration中定义了三种音频设备信息

attached_output_devices:已连接的输出设备

default_output_device:默认输出设备

attached_input_devices:已连接的输入设备

系统支持的音频接口信息

audio_policy.conf定义了系统支持的所有音频接口参数信息,比如primary、a2dp、usb等

每种音频接口包含输入输出,每种输入输出又包含多种输入输出配置,每种输入输出配置又支持多种音频设备。AudioPolicyManagerBase首先加载/vendor/etc/audio_policy.conf,如果该文件不存在,则加载/system/etc/audio_policy.conf

audio_policy.conf同时定义了多个audio 接口,每一个audio接口包含若干output和input,而每个output和input又同时支持多种输入输出模式,每种输入输出模式又支持若干种设备

十二、通过AudioFlinger的loadHwModule加载各audio接口对应的库文件实现调用PlaybackThread播放线程或RecordThread录音线程

图片

十三、AudioFlinger的openInput()方法调用流程分析

图片

十四、AudioFlinger的openOutput()方法的调用流程分析

图片

十五、Audio系统为了能正常播放音频数据,需要创建抽象的音频输出接口对象,打开音频输出过程

图片

十六、打开音频输入的流程

图片

十七、打开音频输出后,在AudioFlinger与AudioPolicyService中的表现形式

图片

十八、打开音频输入后,在AudioFlinger与AudioPolicyService中的表现形式

图片

十九、AudioPolicyService加载完系统定义的所有音频接口,并生成相应的数据对象

图片

二十、AudioPolicyService与AudioTrack和AudioFlinger的关系

图片

二十一、AudioPolicyService注册名为服务的流程

图片

二十二、AudioTrack构造过程

图片

图片

图片

二十三、AudioTrack和AudioFlinger的关系

图片

二十四、audio_policy与AudioPolicyService、AudioPolicyCompatClient之间的关系

图片

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

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

相关文章

配电自动化系统“三区四层”数字化架构

目录 一、“三区四层”数字化架构的特点和优势 二、“三区四层”数字化架构面临的挑战 三、针对“三区四层” 数字化架构在实际应用中面临挑战的措施 配电自动化系统 “三区四层” 数字化架构是一种用于规范和指导配电自动化系统建设与运行的体系结构&#xff0c;对保障电力…

编程界“华山论剑”:PHP与Go,谁主沉浮?

在编程的广阔天地里&#xff0c;选择一门合适的编程语言就如同为一场冒险挑选趁手的武器&#xff0c;至关重要却又常常令人纠结。当我们面对 PHP 与 Go 这两种备受瞩目的编程语言时&#xff0c;这种纠结愈发明显&#xff1a;PHP&#xff0c;作为 Web 开发领域的老牌劲旅&#x…

element tbas增加下拉框

使用Tabs 标签页的label插槽&#xff0c;嵌入Dropdown 下拉菜单&#xff0c;实现Tabs 标签页增加下拉切换功能 Tabs 标签页 tab-click"事件"&#xff08;这个事件当中到拥有下拉框的tab里时&#xff0c;可以存一下Dropdown 第一个菜单的id&#xff0c;实现点击到拥有…

AIGC视频生成模型:ByteDance的PixelDance模型

大家好&#xff0c;这里是好评笔记&#xff0c;公主号&#xff1a;Goodnote&#xff0c;专栏文章私信限时Free。本文详细介绍ByteDance的视频生成模型PixelDance&#xff0c;论文于2023年11月发布&#xff0c;模型上线于2024年9月&#xff0c;同时期上线的模型还有Seaweed&…

mysql 学习3 SQL语句--整体概述。SQL通用语法;DDL创建数据库,查看数据库,删除数据库,使用数据库;

SQL通用语法 SQL语句分类 DDL data definition language : 用来创建数据库&#xff0c;创建表&#xff0c;创建表中的字段&#xff0c;创建索引。因此成为 数据定义语言 DML data manipulation language 有了数据库和表以及字段后&#xff0c;那么我们就需要给这个表中 添加数…

Pyside6(PyQT5)中的QTableView与QSqlQueryModel、QSqlTableModel的联合使用

QTableView 是QT的一个强大的表视图部件&#xff0c;可以与模型结合使用以显示和编辑数据。QSqlQueryModel、QSqlTableModel 都是用于与 SQL 数据库交互的模型,将二者与QTableView结合使用可以轻松地展示和编辑数据库的数据。 QSqlQueryModel的简单应用 import sys from PySid…

C语言文件操作:标准库与系统调用实践

目录 1、C语言标准库文件操作 1.1.题目要求&#xff1a; 1.2.函数讲解&#xff1a; fopen 函数原型 参数 常用的打开模式 返回值 fwrite函数 函数原型 参数 返回值 注意事项 fseek函数 函数原型 参数 返回值 fread函数 函数原型 参数 返回值 fclose 函数…

【2024年华为OD机试】 (C卷,100分)- 多段线数据压缩(JavaScriptJava PythonC/C++)

一、问题描述 问题描述 给定一个多线段的坐标列表&#xff0c;每个坐标由行号和列号表示。多线段的走向只能是水平、竖直或斜向45度。输入数据包含冗余的坐标点&#xff0c;要求输出最简化的坐标列表&#xff0c;仅保留起点、拐点和终点。 输入描述 输入数据为多线段的坐标…

状态模式——C++实现

目录 1. 状态模式简介 2. 代码示例 3. 单例状态对象 4. 状态模式与策略模式的辨析 1. 状态模式简介 状态模式是一种行为型模式。 状态模式的定义&#xff1a;状态模式允许对象在内部状态改变时改变它的行为&#xff0c;对象看起来好像修改了它的类。 通俗的说就是一个对象…

数据库SQLite和SCADA DIAView应用教程

课程简介 此系列课程大纲主要包含七个课时。主要使用到的开发工具有&#xff1a;SQLite studio 和 SCADA DIAView。详细的可成内容大概如下&#xff1a; 1、SQLite 可视化管理工具SQLite Studio &#xff1a;打开数据库和查询数据&#xff1b;查看视频 2、创建6个变量&#x…

java开发,IDEA转战VSCODE配置(mac)

一、基本java开发环境配置 前提&#xff1a;已经安装了jdk、maven、vscode&#xff0c;且配置了环境变量 1、安装java相关的插件 2、安装spring相关的插件 3、vscode配置maven环境 打开 VsCode -> 首选项 -> 设置&#xff0c;也可以在setting.json文件中直接编辑&…

python爬虫的学习流程(1-前提准备)

这里主要记录一下我的python爬虫初级的学习的流程 1.python爬虫入门实战课 什么是爬虫&#xff1f;众说纷纭&#xff0c;我们引用维基百科上对网络爬虫的介绍&#xff1a; 网络爬虫&#xff08;英语&#xff1a;Web crawler&#xff09;&#xff0c;也叫网络蜘蛛&#xff08;…

数据结构初阶之双向链表的介绍与双向链表的实现

一、概念与结构 带头双向循环链表 next &#xff1a;指向下一个结点&#xff08;后继结点&#xff09; prev &#xff1a;指向前一个结点&#xff08;前驱结点&#xff09; 二、实现双向链表 项目创建的时候&#xff0c;要创建一个头文件&#xff08;.h&#xff09;List.h …

CICD集合(六):Jenkins配置Editable Email Notification

CICD集合(六):Jenkins配置Editable Email Notification 1、系统配置 1.1、Jenkins Location 1.2、邮件通知 1、SMTP服务器:imap.exmail.qq.com (因qq企业邮箱) 2、邮箱后缀:@xxx.com 3、勾选:使用SMTP认证 用户名:

Golang 中除了加锁还有哪些安全读写共享变量的方式?

Golang 中除了加锁还有哪些安全读写共享变量的方式&#xff1f; 在 Golang 中&#xff0c;除了使用 Mutex 锁来保护共享变量外&#xff0c;还可以通过 Channel 和 原子性操作 来实现安全读写共享变量。 1. 使用 Channel 原理 Channel 是 Golang 中用于 Goroutine 之间通信的…

应用层协议 HTTP 讲解实战:从0实现HTTP 服务器

&#x1f308; 个人主页&#xff1a;Zfox_ &#x1f525; 系列专栏&#xff1a;Linux 目录 一&#xff1a;&#x1f525; HTTP 协议 &#x1f98b; 认识 URL&#x1f98b; urlencode 和 urldecode 二&#xff1a;&#x1f525; HTTP 协议请求与响应格式 &#x1f98b; HTTP 请求…

F/V/F/I频率脉冲信号转换器

F/V/F/I频率脉冲信号转换器 概述&#xff1a;捷晟达科技的JSD TFA-1001系列是一进一出频率脉冲信号转换器(F/V转换器),该频率转换器是将频率脉冲信号(方波、正弦波、锯齿波)转换成国际标准的模拟量电压(电流)信号,并远距离无失真传送到控制室(如:PLC,DCS,AD,PC采集系统)产品的输…

Windows的docker中安装gitlab

一.Windows的docker中安装gitlab 1.通过阿里云拉取镜像 docker pull registry.cn-hangzhou.aliyuncs.com/lab99/gitlab-ce-zh 2.在本地创建备份数据的目录 mkdir -p D:home/software/gitlab/etc mkdir -p D:home/software/gitlab/logs mkdir -p D:home/software/gitlab/dat…

【Linux】理解Linux中一切皆文件、缓冲区、ext2文件系统、软硬链接

⭐️个人主页&#xff1a;小羊 ⭐️所属专栏&#xff1a;Linux 很荣幸您能阅读我的文章&#xff0c;诚请评论指点&#xff0c;欢迎欢迎 ~ 目录 1、如何理解在Linux中一切皆文件&#xff1f;1.1 概述1.2 文件类型1.3 优势 2、缓冲区2.1 为什么要引入缓冲区&#xff1f;2.2 缓…

在Docker 容器中安装 Oracle 19c

在 Docker 容器中安装 Oracle 19c 是可行的&#xff0c;但它相较于其他数据库&#xff08;如 MySQL、PostgreSQL 等&#xff09;会复杂一些&#xff0c;因为 Oracle 数据库有一些特定的要求&#xff0c;如操作系统和库的依赖&#xff0c;以及许可证问题。 不过&#xff0c;Ora…