0%

简介

  • strace命令相关笔记

linux strace命令是什么

strace 是 Linux 下的一个用于诊断和调试程序的命令,它可以跟踪系统调用和接收的信号。strace 工具允许你查看一个进程执行期间所有的系统调用和它们的返回值,这对理解程序的行为和调试问题非常有帮助。

以下是 strace 的一些常见用法:

  1. 跟踪一个程序的系统调用

    1
    strace -o output.txt program_name

    这会启动 program_name 并将所有的系统调用记录到 output.txt 文件中。

  2. 跟踪一个已经在运行的进程

    1
    strace -p pid

    其中 pid 是进程的 ID。这会附加到已经运行的进程并开始跟踪它的系统调用。

  3. 只跟踪特定类型的系统调用

    1
    strace -e trace=open,read,write program_name

    这将只跟踪 openreadwrite 系统调用。

  4. 打印每个系统调用所花费的时间

    1
    strace -T program_name
  5. 跟踪系统调用和信号

    1
    strace -c program_name

    这会总结系统调用的次数、出错次数和执行时间。

示例:使用 strace 跟踪 ls 命令的系统调用:

1
strace ls

这个命令会在终端中打印出 ls 命令执行期间所有的系统调用和它们的返回值。

strace 是一个非常强大的工具,特别是在调试程序、分析系统性能和排查系统问题时非常有用。

linux strace命令 详解

strace 是一个强大的 Linux 工具,用于诊断和调试程序。它能够跟踪系统调用(syscalls)和信号(signals),从而帮助用户了解程序的行为、查找问题根源、调试错误等。以下是 strace 的详细使用说明和示例。

基本用法

跟踪一个程序的系统调用

1
strace program_name

这会启动 program_name 并在终端上显示其所有系统调用。

将输出重定向到文件

1
strace -o output.txt program_name

将系统调用输出记录到 output.txt 文件中。

跟踪已经运行的进程

1
strace -p pid

其中 pid 是进程的 ID。这会附加到已经运行的进程并开始跟踪它的系统调用。

过滤系统调用

只跟踪特定类型的系统调用

1
strace -e trace=open,read,write program_name

这将只跟踪 openreadwrite 系统调用。

跟踪所有文件相关的系统调用

1
strace -e trace=file program_name

这会跟踪所有与文件操作相关的系统调用,例如 openreadwrite 等。

跟踪所有网络相关的系统调用

1
strace -e trace=network program_name

这会跟踪所有与网络操作相关的系统调用,例如 socketconnectsendrecv 等。

输出格式

打印每个系统调用所花费的时间

1
strace -T program_name

在输出中包含每个系统调用所花费的时间。

打印时间戳

1
strace -t program_name

在每个系统调用前面加上时间戳。

显示相对时间戳

1
strace -r program_name

在每个系统调用前面显示相对于前一个系统调用的时间间隔。

统计信息

汇总系统调用的次数、出错次数和执行时间

1
strace -c program_name

程序执行完毕后,输出汇总统计信息。

示例

跟踪 ls 命令的系统调用

1
strace ls

输出示例:

1
2
3
4
5
execve("/bin/ls", ["ls"], 0x7ffccf5a8c38 /* 20 vars */) = 0
brk(NULL) = 0x56325f1ff000
access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fbb6141f000
...

跟踪一个已经运行的进程

假设进程 ID 为 1234:

1
strace -p 1234

只跟踪 openread 系统调用

1
strace -e trace=open,read ls

常见系统调用解释

  • execve: 执行程序。
  • open: 打开文件。
  • read: 读取文件。
  • write: 写入文件。
  • close: 关闭文件描述符。
  • brk: 调整数据段末尾位置,用于内存分配。
  • mmap: 内存映射,用于文件或设备的内存访问。

strace 是一个非常强大的工具,特别是在调试程序、分析系统性能和排查系统问题时非常有用。它的输出可以帮助开发者理解程序的行为,找出问题的根源并进行优化。

简介

  • zabbix工具相关笔记

Zabbix是什么

Zabbix 是一个开源的企业级监控解决方案,旨在监控和跟踪 IT 基础设施的健康和性能。它提供了多种功能来帮助管理员监控网络、服务器、应用程序、云服务等。以下是 Zabbix 的一些主要特点:

  1. 全面的监控:Zabbix 能够监控各种类型的网络设备、服务器、虚拟机、云服务和应用程序。它支持多种协议,如 SNMP、IPMI、JMX、HTTP、SSH、Telnet 等。

  2. 数据收集和分析:Zabbix 可以收集大量的性能和可用性数据,并对这些数据进行存储和分析。它提供了强大的数据可视化工具,如图表、仪表盘和报告。

  3. 告警和通知:Zabbix 支持复杂的告警机制,可以根据预定义的条件触发告警,并通过多种渠道(如邮件、短信、Slack、Telegram 等)发送通知。

  4. 自动化和自愈:Zabbix 提供了自动化功能,可以在检测到问题时自动执行预定义的操作,如重启服务或执行脚本,以实现自愈。

  5. 高扩展性:Zabbix 设计为高扩展性,可以支持从小型环境到大型企业环境的监控需求。它支持分布式监控和代理模式,能够轻松扩展。

  6. 开源和社区支持:Zabbix 是开源软件,拥有活跃的社区和广泛的用户基础。社区提供了大量的插件、模板和文档,帮助用户快速上手和扩展功能。

Zabbix 的灵活性和强大的功能使其成为许多企业和组织选择的监控解决方案。无论是用于监控网络设备、服务器性能,还是应用程序健康,Zabbix 都能提供可靠的支持。

zabbix 详解

什么是 Zabbix?

Zabbix 是一个开源的监控软件工具,旨在监控和跟踪 IT 基础设施、网络、服务器、虚拟机、应用程序和云服务的健康状况和性能。它提供了一整套功能,从数据收集、存储、分析到告警和自动化操作。由于其高扩展性和灵活性,Zabbix 可以满足从小型组织到大型企业的不同需求。

主要功能和特点

1. 数据收集和监控

  • 广泛的支持:支持多种协议和技术,如 SNMP、IPMI、JMX、HTTP、SSH、Telnet 等,可以监控各种设备和应用程序。
  • 主动监控:通过代理(Agent)和代理代理(Proxy)模式,支持分布式监控,能够在网络的不同部分进行监控。
  • 被动监控:支持从设备和应用程序中获取数据,适用于需要从设备中获取状态和性能信息的场景。

2. 数据存储和处理

  • 历史数据存储:Zabbix 可以存储大量的历史数据,支持长期的数据保留和分析。
  • 灵活的数据处理:通过自定义的触发器和阈值,能够对监控数据进行实时处理和分析。

3. 告警和通知

  • 复杂的告警机制:支持根据预定义的条件和阈值触发告警,告警可以基于多个条件进行组合和过滤。
  • 多渠道通知:支持通过邮件、短信、IM(如 Slack、Telegram 等)和脚本发送通知,确保管理员能够及时收到告警信息。
  • 告警升级:支持告警的升级和降级,确保重要问题能够及时得到关注和处理。

4. 数据可视化

  • 图表和仪表盘:提供强大的图表和仪表盘功能,能够直观地展示监控数据和趋势。
  • 报告和分析:支持生成各种报告,帮助管理员了解系统的健康状况和性能。

5. 自动化和自愈

  • 自动操作:支持在检测到问题时自动执行预定义的操作,如重启服务、执行脚本等。
  • 自愈功能:通过自动化操作实现系统的自愈,减少人为干预,提高系统的可靠性。

6. 高扩展性

  • 分布式监控:支持代理和代理代理模式,能够扩展到大规模的分布式环境中进行监控。
  • 高可用性:通过集群和冗余设计,确保监控系统的高可用性和可靠性。

7. 开源和社区支持

  • 开源许可:Zabbix 是开源软件,拥有 GPL 许可,用户可以自由使用、修改和分发。
  • 活跃的社区:拥有庞大且活跃的用户和开发者社区,提供丰富的插件、模板和文档资源,用户可以从社区中获取支持和帮助。

典型应用场景

  • 网络监控:监控路由器、交换机、防火墙等网络设备的性能和状态。
  • 服务器监控:监控物理服务器和虚拟机的 CPU、内存、磁盘等资源的使用情况。
  • 应用程序监控:监控数据库、Web 服务器、邮件服务器等应用程序的性能和健康状况。
  • 云服务监控:监控 AWS、Azure 等云服务的资源使用情况和性能。

安装与配置

1. 系统要求

  • 操作系统:支持多种操作系统,如 Linux、Windows 等。
  • 数据库:需要一个数据库来存储监控数据,支持 MySQL、PostgreSQL、Oracle 等。
  • Web 服务器:需要一个 Web 服务器来运行 Zabbix 的前端界面,支持 Apache、Nginx 等。

2. 安装步骤

  • 数据库安装和配置:安装并配置所需的数据库。
  • Zabbix 服务器安装:安装 Zabbix 服务器组件。
  • Zabbix 前端安装:安装并配置 Web 服务器和 Zabbix 前端。
  • Zabbix 代理安装:在需要监控的设备上安装 Zabbix 代理。

3. 配置

  • 添加主机和模板:在 Zabbix 前端界面中添加需要监控的主机,并应用相应的监控模板。
  • 设置触发器和告警:配置触发器和告警规则,定义告警条件和通知方式。
  • 创建图表和仪表盘:根据监控需求创建图表和仪表盘,直观展示监控数据。

结论

Zabbix 是一个功能强大且灵活的监控解决方案,适用于各种 IT 环境。从网络设备和服务器的监控,到应用程序和云服务的监控,Zabbix 都能提供可靠的支持。通过其全面的数据收集、分析、告警和自动化功能,Zabbix 帮助企业提高 IT 基础设施的可见性和可靠性,从而更好地保障业务的连续性和性能。

zabbix 使用 示例

以下是一个简单的 Zabbix 使用示例,展示如何安装、配置和使用 Zabbix 来监控一台服务器的基本性能指标。

示例环境

  • Zabbix Server:Ubuntu 20.04
  • 监控对象:一台运行 Ubuntu 20.04 的服务器

步骤 1:安装 Zabbix Server

  1. 更新系统包

    1
    2
    sudo apt update
    sudo apt upgrade -y
  2. 安装 Zabbix 存储库

    1
    2
    3
    wget https://repo.zabbix.com/zabbix/6.0/ubuntu/pool/main/z/zabbix-release/zabbix-release_6.0-2+ubuntu20.04_all.deb
    sudo dpkg -i zabbix-release_6.0-2+ubuntu20.04_all.deb
    sudo apt update
  3. 安装 Zabbix Server、前端和数据库

    1
    sudo apt install zabbix-server-mysql zabbix-frontend-php zabbix-apache-conf zabbix-agent
  4. 安装并配置 MySQL 数据库

    1
    2
    sudo apt install mysql-server
    sudo mysql_secure_installation

    进入 MySQL 命令行,创建数据库和用户:

    1
    2
    3
    4
    CREATE DATABASE zabbix CHARACTER SET utf8 COLLATE utf8_bin;
    CREATE USER 'zabbix'@'localhost' IDENTIFIED BY 'password';
    GRANT ALL PRIVILEGES ON zabbix.* TO 'zabbix'@'localhost';
    FLUSH PRIVILEGES;
  5. 导入初始架构和数据

    1
    zcat /usr/share/doc/zabbix-server-mysql*/create.sql.gz | mysql -uzabbix -p zabbix
  6. 配置 Zabbix Server 连接到数据库

    编辑配置文件 /etc/zabbix/zabbix_server.conf

    1
    DBPassword=password
  7. 启动 Zabbix Server 和代理

    1
    2
    sudo systemctl restart zabbix-server zabbix-agent apache2
    sudo systemctl enable zabbix-server zabbix-agent apache2

步骤 2:安装和配置 Zabbix Agent 在被监控服务器上

  1. 在被监控服务器上安装 Zabbix Agent

    1
    2
    3
    4
    wget https://repo.zabbix.com/zabbix/6.0/ubuntu/pool/main/z/zabbix-release/zabbix-release_6.0-2+ubuntu20.04_all.deb
    sudo dpkg -i zabbix-release_6.0-2+ubuntu20.04_all.deb
    sudo apt update
    sudo apt install zabbix-agent
  2. 配置 Zabbix Agent

    编辑配置文件 /etc/zabbix/zabbix_agentd.conf

    1
    Server=<Zabbix Server IP>
  3. 启动 Zabbix Agent

    1
    2
    sudo systemctl restart zabbix-agent
    sudo systemctl enable zabbix-agent

步骤 3:在 Zabbix 前端配置监控

  1. 访问 Zabbix 前端

    在浏览器中访问 http://<Zabbix Server IP>/zabbix,按照提示完成初始设置。

  2. 登录 Zabbix 前端

    使用默认用户名 Admin 和密码 zabbix 登录。

  3. 添加主机

    • 导航到 Configuration -> Hosts -> Create host
    • 填写主机名、组和 IP 地址。
    • Templates 标签中,选择 Template OS Linux 模板。
  4. 检查监控数据

    • 导航到 Monitoring -> Latest data
    • 选择刚刚添加的主机,查看收集到的监控数据。

总结

通过以上步骤,你已经成功安装并配置了 Zabbix 服务器和代理,并添加了一台被监控的服务器。你可以通过 Zabbix 前端界面查看监控数据,设置告警规则,以及创建图表和仪表盘来直观展示数据。

Zabbix 提供了强大的功能和灵活性,可以监控各种类型的 IT 资源和服务。根据实际需求,你可以进一步定制 Zabbix 的配置和功能,以满足特定的监控需求。

简介

  • 流媒体相关理论基础笔记

推流是什么

推流(Streaming)是指将音视频内容通过网络实时传输到远程服务器或客户端,使得用户可以在不下载完整文件的情况下进行实时播放。推流技术广泛应用于直播、视频会议、在线教育、远程监控等领域。

推流的基本原理

  1. 采集

    • 通过摄像头、麦克风或其他输入设备采集音视频数据。
  2. 编码

    • 将采集到的音视频数据压缩编码,以减少数据量。常用的视频编码格式有 H.264、H.265,音频编码格式有 AAC、MP3 等。
  3. 封装

    • 将编码后的音视频数据封装成适合传输的流媒体格式,如 FLV、HLS 等。
  4. 传输

    • 将封装后的流媒体数据通过网络传输到流媒体服务器。常用的传输协议有 RTMP、HTTP、WebRTC 等。
  5. 分发和播放

    • 流媒体服务器接收推流数据后,进行处理和分发。用户端可以通过播放器软件或网页实时接收并播放流媒体内容。

常用的推流协议和工具

推流协议

  1. RTMP(Real-Time Messaging Protocol)

    • 广泛应用于直播推流,尤其是与 Adobe Flash 配合使用。尽管 Flash 技术逐渐被淘汰,但 RTMP 仍然被很多直播平台所支持。
  2. HLS(HTTP Live Streaming)

    • 由苹果公司开发,广泛应用于点播和直播流媒体服务。使用 HTTP 协议进行传输,兼容性好,特别适合在不同设备和网络条件下使用。
  3. WebRTC(Web Real-Time Communication)

    • 支持实时音视频通信,适用于视频通话、视频会议等场景。WebRTC 具有低延迟、点对点通信等优点。

推流工具

  1. OBS(Open Broadcaster Software)

    • 免费开源的推流和录屏软件,支持多种推流协议和平台。适合游戏直播、网络直播等场景。
  2. FFmpeg

    • 强大的多媒体处理工具,支持音视频采集、编码、封装和推流。适合高级用户和开发者。
  3. Wirecast

    • 专业的直播推流软件,提供丰富的功能和插件,适用于高质量直播需求。
  4. Streamlabs OBS

    • 基于 OBS 开发的推流工具,集成了更多直播相关的功能和插件,适合游戏直播和网络主播。

示例:使用 FFmpeg 推流

假设你想使用 FFmpeg 将本地视频文件推流到 RTMP 服务器,命令如下:

1
ffmpeg -re -i input.mp4 -c:v libx264 -c:a aac -strict experimental -f flv rtmp://example.com/live/stream_key

解释:

  • -re:按实时速率读取输入文件。
  • -i input.mp4:指定输入文件。
  • -c:v libx264:使用 H.264 编码器。
  • -c:a aac:使用 AAC 音频编码器。
  • -strict experimental:允许使用实验性 AAC 编码器。
  • -f flv:指定输出格式为 FLV。
  • rtmp://example.com/live/stream_key:RTMP 服务器地址和流密钥。

推流应用场景

  • 游戏直播:通过推流软件将游戏画面和声音实时传输到直播平台,观众可以实时观看游戏过程。
  • 在线教育:教师通过摄像头和麦克风实时讲解课程内容,学生可以通过网络实时观看和互动。
  • 视频会议:通过推流技术,多个用户可以实时进行音视频交流,适用于远程办公和在线会议。
  • 远程监控:通过摄像头实时推流监控画面,用户可以通过网络随时查看监控区域的情况。

推流技术为各种实时音视频应用提供了基础支持,使得用户能够在不同设备和网络条件下享受到流畅的实时音视频体验。

拉流/取流是什么

拉流(Pull Streaming)或取流(Fetching Stream)是指从服务器端获取实时音视频流并在客户端进行播放的过程。与推流(Push Streaming)相对应,拉流是接收端的操作,常用于观看直播、视频会议、监控等场景。

拉流的基本原理

  1. 连接服务器

    • 客户端通过指定的协议和地址连接到流媒体服务器。
  2. 请求流

    • 客户端向服务器发送请求,要求获取特定的流媒体内容。
  3. 接收流

    • 服务器将实时的音视频数据发送给客户端。
  4. 解码和播放

    • 客户端接收数据后,进行解码并播放音视频内容。

常用的拉流协议和工具

拉流协议

  1. RTMP(Real-Time Messaging Protocol)

    • 常用于直播流的传输,尤其是与 Adobe Flash 配合使用。许多直播平台仍然支持 RTMP。
  2. HLS(HTTP Live Streaming)

    • 由苹果公司开发,广泛应用于点播和直播流媒体服务。使用 HTTP 协议进行传输,兼容性好,特别适合在不同设备和网络条件下使用。
  3. DASH(Dynamic Adaptive Streaming over HTTP)

    • 一种基于 HTTP 的自适应流媒体传输协议,可以根据网络条件动态调整视频质量。
  4. WebRTC(Web Real-Time Communication)

    • 支持实时音视频通信,适用于视频通话、视频会议等场景。具有低延迟、点对点通信等优点。

拉流工具

  1. VLC Media Player

    • 开源的多媒体播放器,支持多种流媒体协议,常用于播放 RTMP、HLS 等流媒体内容。
  2. FFmpeg

    • 强大的多媒体处理工具,支持音视频采集、编码、封装和拉流。适合高级用户和开发者。
  3. OBS(Open Broadcaster Software)

    • 虽然主要用于推流,也可以配置为接收和播放拉流内容。
  4. Web 浏览器

    • 现代浏览器支持 HLS、DASH 和 WebRTC,可以直接在网页中播放流媒体内容。

示例:使用 FFmpeg 拉流

假设你想使用 FFmpeg 从 RTMP 服务器拉取流媒体并保存到本地文件,命令如下:

1
ffmpeg -i rtmp://example.com/live/stream_key -c copy output.mp4

解释:

  • -i rtmp://example.com/live/stream_key:指定输入流地址。
  • -c copy:直接复制流媒体数据,不进行重新编码。
  • output.mp4:指定输出文件名和格式。

示例:使用 VLC 拉流

假设你想使用 VLC 播放 HLS 流媒体,可以按以下步骤操作:

  1. 打开 VLC Media Player。
  2. 选择“媒体”菜单,然后选择“打开网络串流”。
  3. 在网络 URL 输入框中输入 HLS 流地址,例如:
    1
    http://example.com/live/stream.m3u8
  4. 点击“播放”按钮,VLC 将开始播放流媒体内容。

拉流应用场景

  • 观看直播:用户通过拉流技术从直播服务器获取实时音视频流,观看直播节目、体育赛事、游戏直播等。
  • 视频会议:会议参与者通过拉流技术获取其他参与者的音视频流,实现实时交流。
  • 远程监控:监控系统通过拉流技术从摄像头获取实时视频流,监控人员可以随时查看监控区域的情况。
  • 在线教育:学生通过拉流技术观看教师的实时讲解和课程内容,进行在线学习。

拉流技术是流媒体应用的重要组成部分,使用户能够在不同设备和网络条件下实时获取和播放音视频内容,为各种实时互动和直播应用提供了支持。

流媒体服务器是什么

流媒体服务器是一种专门用于传输和分发音视频内容的服务器。它可以处理实时音视频流的接收、转码、存储和分发,使得用户能够通过网络实时观看或收听音视频内容。流媒体服务器在直播、点播、视频会议和远程教育等场景中发挥着关键作用。

流媒体服务器的主要功能

  1. 接收推流

    • 接收来自客户端或设备的实时音视频流(推流)。这些流可以来自摄像头、麦克风、编码器或其他推流设备。
  2. 转码

    • 将音视频流转码为不同的格式或码率,以适应不同的网络条件和设备。例如,将高分辨率视频转码为多种分辨率和码率,以便用户根据自己的网络状况选择最佳的播放质量。
  3. 存储

    • 将流媒体内容存储在服务器上,以便后续点播。存储的内容可以是完整的视频文件,也可以是分片的流媒体文件(如 HLS 的 TS 分片)。
  4. 分发拉流

    • 将流媒体内容分发给客户端(拉流)。客户端可以是播放器、浏览器、移动应用等。
  5. 负载均衡

    • 管理多个流媒体服务器,以处理大量的并发请求,确保系统的稳定性和高可用性。
  6. 安全性

    • 提供访问控制、加密和认证等安全措施,确保流媒体内容的安全传输和访问。

常见的流媒体服务器软件

  1. Nginx + RTMP 模块

    • Nginx 是一个高性能的 HTTP 服务器和反向代理服务器,通过 RTMP 模块扩展,可以实现 RTMP 推流和拉流功能。
    • Nginx RTMP Module
  2. Wowza Streaming Engine

    • 商用的流媒体服务器,支持多种流媒体协议和功能,适用于企业级应用。
    • Wowza
  3. Red5

    • 开源的流媒体服务器,支持 RTMP 和 WebRTC 等协议,适用于实时音视频通信。
    • Red5
  4. Flussonic

    • 商用的流媒体服务器,支持多种协议和高级功能,如转码、存储和分发。
    • Flussonic
  5. Kurento

    • 开源的 WebRTC 流媒体服务器,适用于视频会议和实时通信应用。
    • Kurento
  6. Adobe Media Server

    • 商用的流媒体服务器,支持 RTMP 和 HLS 等协议,适用于专业的流媒体应用。
    • Adobe Media Server

示例:使用 Nginx + RTMP 模块搭建流媒体服务器

  1. 安装 Nginx 和 RTMP 模块

    • 在 Ubuntu 系统上,首先安装必要的依赖:
      1
      2
      sudo apt-get update
      sudo apt-get install build-essential libpcre3 libpcre3-dev libssl-dev
    • 下载并编译 Nginx 和 RTMP 模块:
      1
      2
      3
      4
      5
      6
      7
      8
      wget http://nginx.org/download/nginx-1.19.6.tar.gz
      wget https://github.com/arut/nginx-rtmp-module/archive/master.zip
      tar -zxvf nginx-1.19.6.tar.gz
      unzip master.zip
      cd nginx-1.19.6
      ./configure --add-module=../nginx-rtmp-module-master --with-http_ssl_module
      make
      sudo make install
  2. 配置 Nginx

    • 编辑 Nginx 配置文件(通常位于 /usr/local/nginx/conf/nginx.conf):
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      worker_processes  1;

      events {
      worker_connections 1024;
      }

      http {
      include mime.types;
      default_type application/octet-stream;

      sendfile on;
      keepalive_timeout 65;

      server {
      listen 80;
      server_name localhost;

      location / {
      root html;
      index index.html index.htm;
      }
      }
      }

      rtmp {
      server {
      listen 1935;
      chunk_size 4096;

      application live {
      live on;
      record off;
      }
      }
      }
  3. 启动 Nginx

    • 启动 Nginx 流媒体服务器:
      1
      sudo /usr/local/nginx/sbin/nginx
  4. 推流和拉流

    • 使用推流工具(如 OBS 或 FFmpeg)将流媒体推送到服务器:
      1
      ffmpeg -re -i input.mp4 -c:v libx264 -c:a aac -strict experimental -f flv rtmp://your_server_ip/live/stream_key
    • 使用拉流工具(如 VLC)从服务器获取流媒体:
      1
      rtmp://your_server_ip/live/stream_key

流媒体服务器在现代网络音视频应用中起到了至关重要的作用,通过合理的部署和配置,可以实现高效、稳定和安全的实时音视频传输。

linux 循环推流 详解

在 Linux 上进行循环推流意味着将一个视频文件不断地推送到流媒体服务器,以便进行持续播放。这通常用于 24/7 直播、广告循环播放等场景。使用 FFmpeg 是实现这一功能的常见方法,因为它非常灵活且支持多种音视频处理任务。

步骤详解

安装 FFmpeg

如果尚未安装 FFmpeg,请先进行安装:

对于 Debian/Ubuntu 系统:

1
2
sudo apt update
sudo apt install ffmpeg

对于 CentOS/RHEL 系统:

1
2
sudo yum install epel-release
sudo yum install ffmpeg

编写循环推流脚本

以下是一个简单的 Bash 脚本示例,用于实现视频文件的循环推流:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#!/bin/bash

# 推流地址
RTMP_URL="rtmp://example.com/live/stream_key"

# 视频文件路径
VIDEO_FILE="input.mp4"

# 循环推流
while true; do
ffmpeg -re -stream_loop -1 -i "$VIDEO_FILE" -c:v libx264 -c:a aac -f flv "$RTMP_URL"
if [ $? -ne 0 ]; then
echo "推流失败,等待 5 秒后重试..."
sleep 5
fi
done

解释:

  • RTMP_URL:指定 RTMP 服务器的推流地址。
  • VIDEO_FILE:指定要推流的视频文件。
  • while true; do ... done:无限循环执行推流命令。
  • ffmpeg -re -stream_loop -1 -i "$VIDEO_FILE" -c:v libx264 -c:a aac -f flv "$RTMP_URL"
    • -re:按实时速率读取输入文件。
    • -stream_loop -1:无限循环输入文件。
    • -i "$VIDEO_FILE":指定输入文件。
    • -c:v libx264:使用 H.264 视频编码器。
    • -c:a aac:使用 AAC 音频编码器。
    • -f flv "$RTMP_URL":指定输出格式为 FLV,并推送到 RTMP 服务器。
  • if [ $? -ne 0 ]; then ...:如果 FFmpeg 命令失败,等待 5 秒后重试。

运行脚本

保存上述脚本为 loop_stream.sh,然后赋予其执行权限并运行:

1
2
chmod +x loop_stream.sh
./loop_stream.sh

处理特殊情况

有时你可能需要在每次循环结束后对推流进行一些清理工作,或者在推流过程中处理网络中断等情况。可以通过捕捉信号或检查推流状态来实现更复杂的逻辑。例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#!/bin/bash

RTMP_URL="rtmp://example.com/live/stream_key"
VIDEO_FILE="input.mp4"

# 捕捉退出信号以进行清理
trap 'echo "脚本终止"; exit 0' SIGINT SIGTERM

while true; do
ffmpeg -re -stream_loop -1 -i "$VIDEO_FILE" -c:v libx264 -c:a aac -f flv "$RTMP_URL"
if [ $? -ne 0 ]; then
echo "推流失败,等待 5 秒后重试..."
sleep 5
fi
done

此脚本在接收到中断信号(如 Ctrl+C)时会优雅地退出。

其他考虑

  • 性能优化:根据实际需求调整 FFmpeg 编码参数,以优化推流性能。
  • 监控和日志:可以将推流日志重定向到文件,并使用监控工具(如 supervisordsystemd)来确保推流脚本的运行和自动重启。
  • 多文件循环播放:如果需要循环播放多个视频文件,可以修改脚本以支持多文件循环。例如,使用 ffmpegconcat 过滤器。

多文件循环播放示例

以下是一个多文件循环播放的脚本示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#!/bin/bash

RTMP_URL="rtmp://example.com/live/stream_key"
VIDEO_FILES=("video1.mp4" "video2.mp4" "video3.mp4")

# 捕捉退出信号以进行清理
trap 'echo "脚本终止"; exit 0' SIGINT SIGTERM

while true; do
for VIDEO_FILE in "${VIDEO_FILES[@]}"; do
ffmpeg -re -i "$VIDEO_FILE" -c:v libx264 -c:a aac -f flv "$RTMP_URL"
if [ $? -ne 0 ]; then
echo "推流失败,等待 5 秒后重试..."
sleep 5
fi
done
done

此脚本会按顺序循环播放 VIDEO_FILES 列表中的每个视频文件,并在所有文件播放完毕后重新开始。

简介

  • LZ4库相关笔记

lz4.h 是什么

lz4.h 是 LZ4 压缩库的头文件。LZ4 是一种非常快速的无损压缩算法,专为高性能和高压缩率设计。LZ4 库主要用于数据压缩和解压缩操作,适用于需要快速处理大量数据的应用程序。

LZ4 库的头文件 lz4.h 定义了所有必要的函数和数据结构,使得开发者可以在他们的 C 或 C++ 项目中轻松地使用 LZ4 压缩和解压缩功能。这个头文件通常包含以下内容:

  1. 数据类型和结构:定义了用于压缩和解压缩过程的数据类型和结构体。
  2. 宏和常量:定义了库中使用的一些常量和宏。
  3. 函数声明:声明了库中提供的所有函数,包括压缩和解压缩函数。

常用的函数包括:

  • LZ4_compress_default()
  • LZ4_decompress_safe()
  • LZ4_compressBound()

这些函数分别用于默认压缩、解压缩和计算压缩缓冲区的最大大小。

以下是一个使用 lz4.h 的简单示例,演示如何进行压缩和解压缩:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "lz4.h"

int main() {
// 原始数据
const char* input = "Hello, LZ4 compression!";
int inputSize = strlen(input) + 1;

// 压缩缓冲区
int maxCompressedSize = LZ4_compressBound(inputSize);
char* compressedData = (char*)malloc(maxCompressedSize);

// 压缩数据
int compressedDataSize = LZ4_compress_default(input, compressedData, inputSize, maxCompressedSize);
if (compressedDataSize <= 0) {
fprintf(stderr, "Compression failed!\n");
free(compressedData);
return 1;
}

// 解压缩缓冲区
char* decompressedData = (char*)malloc(inputSize);

// 解压缩数据
int decompressedSize = LZ4_decompress_safe(compressedData, decompressedData, compressedDataSize, inputSize);
if (decompressedSize < 0) {
fprintf(stderr, "Decompression failed!\n");
free(compressedData);
free(decompressedData);
return 1;
}

// 输出解压缩后的数据
printf("Decompressed data: %s\n", decompressedData);

// 清理内存
free(compressedData);
free(decompressedData);

return 0;
}

这个示例展示了如何使用 LZ4 库进行数据的压缩和解压缩操作。

LZ4 库是什么

LZ4 库是一个开源的压缩算法库,专注于提供高效、快速的无损压缩和解压缩功能。LZ4 是一种非常快的压缩算法,能够在保持高压缩速度的同时提供良好的压缩率。它特别适用于需要快速处理大量数据的应用程序,例如日志压缩、传输数据的实时压缩、存储系统等。

LZ4 库具有以下几个主要特点:

  1. 高速压缩和解压缩:LZ4 以其极快的压缩和解压缩速度而闻名,通常在几百MB/s到几GB/s的范围内。
  2. 无损压缩:LZ4 是无损压缩算法,这意味着压缩和解压缩过程中数据不会丢失。
  3. 简单易用:LZ4 库提供了简单易用的 API,可以很容易地集成到 C、C++ 项目中。它还提供了其他语言的绑定,例如 Python、Java 等。
  4. 小内存占用:LZ4 的压缩和解压缩过程对内存的占用很小,适合在内存受限的环境中使用。

LZ4 库的使用

LZ4 库主要提供了两个核心函数:LZ4_compress_defaultLZ4_decompress_safe

压缩数据

LZ4_compress_default 函数用于压缩数据:

1
int LZ4_compress_default(const char* source, char* dest, int sourceSize, int maxDestSize);
  • source:指向要压缩的源数据。
  • dest:指向压缩后存储数据的目标缓冲区。
  • sourceSize:源数据的大小。
  • maxDestSize:目标缓冲区的最大大小。

返回值是压缩后的数据大小,如果返回值为 0 或负值,则表示压缩失败。

解压缩数据

LZ4_decompress_safe 函数用于解压缩数据:

1
int LZ4_decompress_safe(const char* source, char* dest, int compressedSize, int maxDecompressedSize);
  • source:指向要解压缩的压缩数据。
  • dest:指向解压缩后存储数据的目标缓冲区。
  • compressedSize:压缩数据的大小。
  • maxDecompressedSize:目标缓冲区的最大大小。

返回值是解压缩后的数据大小,如果返回值为负值,则表示解压缩失败。

示例代码

下面是一个简单的示例,展示了如何使用 LZ4 库进行数据的压缩和解压缩:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "lz4.h"

int main() {
// 原始数据
const char* input = "Hello, LZ4 compression!";
int inputSize = strlen(input) + 1;

// 压缩缓冲区
int maxCompressedSize = LZ4_compressBound(inputSize);
char* compressedData = (char*)malloc(maxCompressedSize);

// 压缩数据
int compressedDataSize = LZ4_compress_default(input, compressedData, inputSize, maxCompressedSize);
if (compressedDataSize <= 0) {
fprintf(stderr, "Compression failed!\n");
free(compressedData);
return 1;
}

// 解压缩缓冲区
char* decompressedData = (char*)malloc(inputSize);

// 解压缩数据
int decompressedSize = LZ4_decompress_safe(compressedData, decompressedData, compressedDataSize, inputSize);
if (decompressedSize < 0) {
fprintf(stderr, "Decompression failed!\n");
free(compressedData);
free(decompressedData);
return 1;
}

// 输出解压缩后的数据
printf("Decompressed data: %s\n", decompressedData);

// 清理内存
free(compressedData);
free(decompressedData);

return 0;
}

这个示例展示了如何使用 LZ4 库的压缩和解压缩函数来处理字符串数据。

LZ4库 编译安装

要编译和安装 LZ4 库,你可以按照以下步骤进行。这里假设你使用的是 Unix 系统,例如 Linux 或 macOS。如果你使用的是 Windows 系统,可以参考对应的指南。

1. 下载 LZ4 源代码

首先,你需要从 LZ4 的 GitHub 仓库下载源代码。你可以使用 git 命令来克隆仓库,或者直接从 GitHub 网站下载压缩包。

1
2
git clone https://github.com/lz4/lz4.git
cd lz4

2. 编译 LZ4 库

进入 LZ4 源代码目录后,使用以下命令来编译库。LZ4 使用 make 工具来管理编译过程。

1
make

这个命令会编译所有的源文件并生成可执行文件和库文件。

3. 安装 LZ4 库

编译完成后,你可以使用以下命令来安装 LZ4 库。默认情况下,库会安装到系统的标准位置(例如 /usr/local)。

1
sudo make install

如果你希望将库安装到自定义位置,可以使用 PREFIX 选项来指定目标路径:

1
sudo make install PREFIX=/path/to/your/installation/directory

4. 验证安装

你可以使用以下命令来验证 LZ4 是否安装成功:

1
lz4 -V

这个命令会输出 LZ4 的版本信息,表明 LZ4 已成功安装。

5. 在项目中使用 LZ4 库

在你的 C/C++ 项目中,你需要包含 LZ4 的头文件并链接 LZ4 库。假设你已经安装 LZ4,并且头文件和库文件位于系统的标准位置,你可以使用以下示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "lz4.h"

int main() {
// 原始数据
const char* input = "Hello, LZ4 compression!";
int inputSize = strlen(input) + 1;

// 压缩缓冲区
int maxCompressedSize = LZ4_compressBound(inputSize);
char* compressedData = (char*)malloc(maxCompressedSize);

// 压缩数据
int compressedDataSize = LZ4_compress_default(input, compressedData, inputSize, maxCompressedSize);
if (compressedDataSize <= 0) {
fprintf(stderr, "Compression failed!\n");
free(compressedData);
return 1;
}

// 解压缩缓冲区
char* decompressedData = (char*)malloc(inputSize);

// 解压缩数据
int decompressedSize = LZ4_decompress_safe(compressedData, decompressedData, compressedDataSize, inputSize);
if (decompressedSize < 0) {
fprintf(stderr, "Decompression failed!\n");
free(compressedData);
free(decompressedData);
return 1;
}

// 输出解压缩后的数据
printf("Decompressed data: %s\n", decompressedData);

// 清理内存
free(compressedData);
free(decompressedData);

return 0;
}

编译这个示例代码时,记得链接 LZ4 库:

1
gcc -o example example.c -llz4

通过以上步骤,你应该能够成功地编译、安装和使用 LZ4 库。

简介

  • live555开源工具的相关笔记

live555是什么

LIVE555 是一个开源的多媒体流媒体库,主要用于实现 RTP/RTCP、RTSP 和 SIP 协议。它提供了一系列的 C++ 类,用于创建多媒体流媒体应用程序,例如流媒体服务器和客户端。LIVE555 可以用于流式传输各种多媒体格式,包括 H.264 视频、AAC 音频等。

LIVE555 的主要特点包括:

  1. 跨平台支持:支持多种操作系统,包括 Windows、macOS、Linux 等。
  2. 高效性:使用 C++ 编写,具有高效的性能。
  3. 灵活性:提供了丰富的 API,开发者可以根据需要进行定制。
  4. 支持多种协议:支持 RTP/RTCP、RTSP 和 SIP 协议,使其可以与许多不同类型的流媒体应用程序兼容。
  5. 开源:LIVE555 是开源软件,可以免费使用和修改。

通常,LIVE555 被用作开发实时流媒体应用程序的基础组件,例如视频监控系统、实时视频会议系统等。

live555 详解

LIVE555 是一个开源的多媒体流媒体库,广泛应用于实时流媒体传输和接收。它由 Ross Finlayson 维护,主要用来处理 RTP/RTCP、RTSP 和 SIP 协议,并支持多种多媒体格式的流式传输。以下是 LIVE555 的详细介绍:

1. 主要组件

a. BasicUsageEnvironment

这是 LIVE555 提供的基础环境,处理事件循环和日志记录。它为应用程序提供了一个使用基础 API 的上下文。

b. Groupsock

该模块处理网络套接字的组播和单播通信,封装了 IP 地址和端口的处理逻辑。

c. RTSPClient

该模块用于实现 RTSP 客户端功能,能够发送 RTSP 请求(如 DESCRIBE、SETUP、PLAY、PAUSE 和 TEARDOWN)并处理 RTSP 响应。

d. RTSPServer

该模块用于实现 RTSP 服务器功能,能够接收和处理来自客户端的 RTSP 请求,并管理媒体流会话。

e. MediaSession

该模块表示一个媒体会话,包含一个或多个媒体子会话(MediaSubsession),每个子会话表示一个媒体流(例如视频流或音频流)。

f. MediaSink

该模块处理媒体数据的接收和处理,通常用于播放或保存媒体流。

g. MediaSource

该模块用于生成媒体数据,可以从文件、设备或其他来源读取数据。

2. 主要协议支持

a. RTP/RTCP

RTP(实时传输协议)用于实时传输多媒体数据,而 RTCP(RTP 控制协议)用于监控数据传输的质量和提供会话控制。

b. RTSP

RTSP(实时流协议)是一种网络控制协议,用于建立和控制媒体流会话。RTSP 类似于 HTTP,但专门设计用于流媒体控制。

c. SIP

SIP(会话初始协议)用于建立、修改和终止多媒体会话(如 IP 电话呼叫)。

3. 支持的多媒体格式

LIVE555 支持多种多媒体格式的流式传输,包括但不限于:

  • H.264 视频
  • MPEG 视频
  • AAC 音频
  • AMR 音频
  • JPEG 图像

4. 使用场景

  • 实时视频监控系统:例如 IP 摄像机和网络视频录像机(NVR)。
  • 实时视频会议系统:例如视频会议软件和硬件设备。
  • 流媒体服务器:例如媒体流服务器,能够实时传输多媒体内容给客户端。
  • 多媒体播放器:能够接收和播放来自流媒体服务器的实时媒体流。

5. 示例代码

以下是一个简单的 RTSP 客户端示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <BasicUsageEnvironment.hh>
#include <liveMedia.hh>

void afterPlaying(void* clientData) {
// Placeholder for post-playback actions
}

int main(int argc, char** argv) {
// Create the usage environment
TaskScheduler* scheduler = BasicTaskScheduler::createNew();
UsageEnvironment* env = BasicUsageEnvironment::createNew(*scheduler);

// Create the RTSP client
RTSPClient* rtspClient = RTSPClient::createNew(*env, "rtsp://example.com/stream");

// Start the event loop
env->taskScheduler().doEventLoop();

// Cleanup
delete rtspClient;
env->reclaim();
delete scheduler;

return 0;
}

这个示例展示了如何创建一个简单的 RTSP 客户端,连接到 RTSP 流媒体服务器并进入事件循环。

6. 文档和资源

通过这些资源,您可以获得更多关于 LIVE555 的详细信息和使用指南。

简介

  • ascend相关笔记

ascend是什么

  • 2023年5月6日,在昇腾AI开发者峰会上,华为正式发布了面向算子开发场景的昇腾Ascend C编程语言。Ascend C原生支持C/C++编程规范,通过多层接口抽象、并行编程范式、孪生调试等技术,极大提高了算子的开发效率,帮助AI开发者低成本完成算子开发和模型调优部署。
  • 和CUDA开发的算子运行在GPU上一样,基于Ascend C开发的算子,可以通过异构计算架构CANN(Compute Architecture for Neural Networks)运行在昇腾AI处理器(可简称NPU)上。CANN是使能昇腾AI处理器的一个软件栈,通过软硬件协同优化,能够充分发挥昇腾AI处理器的强大算力。从下面的架构图可以清楚的看到,使用Ascend C编程语言开发的算子通过编译器编译和运行时调度,最终运行在昇腾AI处理器上。
    架构图
  • 我们知道,通用计算就是我们常写的一些在CPU上运行的计算,它擅长逻辑控制和串行计算,而AI计算相对通用计算来说,更擅长并行计算,可支持大规模的计算密集型任务。
  • 最小计算代码能同时计算多个数据的乘加,更近一步,如果使用Cube计算单元,只需要一条语句就能完成一个矩阵乘的计算,这就是我们所说的SIMD(单指令多数据)。因此,我们通常使用AI处理器来进行大量的并行计算。
  • NPU不能独立运行,需要与CPU协同工作,可以看成是CPU的协处理器,CPU负责整个操作系统运行,管理各类资源并进行复杂的逻辑控制,而NPU主要负责并行计算任务。在基于CPU+NPU的异构计算架构中,NPU与CPU通过PCIe总线连接在一起来协同工作,CPU所在位置称为主机端(host),而NPU所在位置称为设备端(device)

ascend C编程范式

  • Ascend C 编程范式是一种流水线式的编程范式,把算子核内的处理程序,分成多个流水任务,通过队列(Queue)完成任务间通信和同步,并通过统一的内存管理模块(Pipe)管理任务间通信内存。流水编程范式应用了流水线并行计算方法。

简介

  • 从上一份工作结束之后,面试了几家公司,其中格灵深瞳公司的招聘岗位与我期望的职业方向非常符合,因此我想了解公司的发展理念和主营产品,为未来发展做谋划。

格灵深瞳简介

  • 格灵深瞳成立于2013年,由创始人赵勇在北京创立,是一家人工智能公司。公司专注于将计算机视觉技术和大数据分析技术与应用场景深度融合,提供面向城市管理、智慧金融、商业零售、体育健康、轨交运维、元宇宙等领域的人工智能产品及解决方案。

  • 2022年3月17日,格灵深瞳正式在上海证券交易所科创板挂牌上市。股票代码:688207。

  • 格灵深瞳是一家行业领先的人工智能A股上市公司(股票代码:688207)。公司以 让AI造福人类,让世界更安全更宜居更健康 为愿景,专注于将先进的计算机视觉,大数据分析,人机交互和机器人技术与应用场景深度融合,提供面向智慧金融,城市管理,智慧商业,轨交运维,体育健康,元宇宙等领域的人工智能产品及解决方案。

  • 公司成立于2013年,2022年3月在上海证券交易所科创板挂牌上市,成为A股第一家AI计算机视觉上市公司。

  • 北京卫视采访

    • 我想了解一下,这个国家发生了什么。我能不能够参与到这个进程中去
    • 我的野心和梦想就是通过20年,30年的时间,把格灵深瞳能够塑造成一家有梦想,有能力,对社会有贡献的公司。从行业角度来讲,我希望基于人工智能的大数据,基于人工智能的机器人和基于人工智能的人机交互,能够变成我们现实生活中每一天服务老百姓,服务企业,服务政府的优秀的产品
    • 无论是深瞳视觉体能训练系统,还是高铁检修机器人,赵勇始终把公司未来的发展和国家的发展紧紧结合在一起

个人思考

  • 我的定位是工程师,AI模型工程化,是面向产品研发的。产品分为多种,格灵深瞳将产品的定位放在生命健康主题之上,相对于娱乐类,这个方向有意义,也有很好的前景。对于我自身的经历和前辈的经验分享来说,只有做人民生活的基石工作,工作才有长远的意义,因为人民在日常生活中不会注意到它,但又离不开它。一个简单的例子,人民都会用应用软件,会注意到哪一类应用软件,但操作系统不会在意,因为这是基石,人民习惯了它的存在,所以忽视了它。
  • 格灵深瞳的产品定位是与国家发展绑定在一起的,我认为这种思想是很值得我个人学习的,只有紧跟国家的脚步,顺着时代的洪流,才能比较容易的生存下来。要做一些国家鼓励做的事情,倡导我们做的事情,对人民有利的事情。

问题解惑

A股是什么

A股是指在中国大陆证券交易所(如上海证券交易所和深圳证券交易所)上市交易的股票。以下是一些关键点:

  1. 货币单位:A股股票以人民币计价。
  2. 投资者:主要面向中国大陆的个人和机构投资者。此前,A股市场对外国投资者有所限制,但近年来随着中国金融市场的开放,外国投资者也可以通过合格境外机构投资者(QFII)和人民币合格境外机构投资者(RQFII)等渠道投资A股。
  3. 市场特点:A股市场的上市公司多为中国本土企业,涉及行业广泛,涵盖制造、科技、金融、医疗等多个领域。

A股市场与H股(在香港证券交易所上市的中国公司股票)和B股(以外币计价、面向外国投资者的股票)有所不同。

如有进一步的问题或需要了解更多具体细节,请告诉我。

简介

  • 记录docker部署下遇到的问题及解决方法

docker容器日志导致主机磁盘空间满了

什么是Docker日志

  • 在Docker中,日志是容器生成的所有输出,该输出保存在主机计算机上的日志文件中。这些日志文件可以帮助了解Docker容器的健康问题,调试问题,并监视应用程序行为。Docker在linux上,容器日志一般存放在 /var/lib/docker/containers/container_id/ 下面

为什么需要清理Docker日志

  • Docker日志文件可以变得相当大,因为他们持续记录容器生成的所有输出。如果不定期的清理这些日志文件,他们将占用大量的磁盘空间。在服务器上,磁盘空间非常重要。如果磁盘空间不足,将导致系统崩溃或者性能下降。此外,当Docker容器日志过大时,可能会导致调试问题过于复杂或深度,使用Docker日志分析工具成为不可避免的任务。

如何清理Docker日志

  • 清理Docker日志的最佳方法是通过Docker提供的内置支持在容器运行时管理日志级别并限制日志大小。但是,如果需要清理已经存在的日志文件,则可以用以下方法

  • 使用Docker命令清理

    • 清空所有容器的日志文件
      1
      docker container prune --filter "until=24h"
    • 这将会删除已经停止容器的日志,最后使用时间超过24h的容器的日志将会被保留,也可以将24h这个参数按需更改。
  • 手动清理Docker日志

    • 需要找出要删除的容器ID
      1
      docker container ls -a
    • 该命令将返回运行的,停止的和删除的容器列表,以及他们的container id, image 和 names
    • 在主机上找到日志文件,Docker日志文件通常在 /var/lib/docker/containers/容器ID/容器ID-json.log路径下,其中container_id为需要清理的容器ID
    • 清空日志文件
      1
      cat /dev/null > /var/lib/docker/containers/容器ID/容器ID-json.log

启动时设置日志文件大小

  • 参考 docker 官方的文档,重启docker启动时增加参数
    1
    docker run -it --log-opt max-size=10m --log-opt max-file=3 alpine ash

简介

  • 音视频开发相关笔记

C++语言在音视频相关业务中的优势与劣势

优势

  • 性能卓越:

    • C++作为一种静态类型、编译型语言,直接生成机器码,执行效率极高。在音视频编解码、实时处理、特效渲染等对性能要求严苛的场景中,C++可以直接操纵硬件资源,实现接近底层的优化,保证低延迟、高帧率和高质量的音视频输出。
  • 丰富的库与工具链:

    • C++在音视频处理领域有着悠久的历史和深厚的积淀,拥有大量的成熟库和工具链,如FFmpeg、OpenCV、OpenSSL、GStreamer、VLC等,涵盖编解码、滤镜、流媒体传输、加密、图形处理等全方位功能。这些库经过长期打磨,性能稳定,功能强大,能够满足各种复杂的音视频业务需求。
  • 面向对象与模板编程:

    • C++支持面向对象编程,允许开发者通过封装、继承、多态等机制设计清晰、可扩展的系统架构。同时,模板和STL(Standard Template Library)提供了泛型编程能力,有助于编写通用、高效的代码。这对于构建复杂的音视频处理框架和组件化系统非常有利。
  • 对硬件资源的精细控制:

    • C++可以直接访问硬件资源,如CPU缓存、内存、GPU等,进行精细的资源管理和优化。在音视频处理中,这有助于实现内存池、零拷贝技术、异步I/O、多线程/多进程调度等高级特性,最大限度地提高系统性能和效率。
  • 跨平台支持:

    • C++编写的代码通过适当的条件编译和第三方库支持,可以跨多个操作系统(如Windows、Linux、macOS)和硬件平台(如x86、ARM)部署,实现广泛的平台兼容性。

劣势

  • 学习曲线陡峭:

    • C++语言特性丰富且复杂,包括指针、内存管理、模板元编程等高级概念,学习成本相对较高。对于初学者或非专业的音视频开发人员,掌握C++并正确使用其高级特性的难度较大。
  • 代码复杂性与错误排查:

    • C++允许程序员进行低级别的操作,虽然带来了灵活性,但也可能导致代码复杂,容易出现内存泄漏、空指针引用、未初始化变量等错误。调试和排查这些问题往往需要深厚的C++知识和经验,以及熟练使用调试工具。
  • 开发效率:

    • 相比于Python、Go等现代语言,C++的开发效率较低。编译时间较长,缺少自动垃圾回收机制,需要手动管理内存,编写和维护代码的工作量较大。尤其是在快速迭代的项目中,C++的开发速度可能成为瓶颈。
  • 安全性:

    • C++没有内置的安全性检查机制,如数组越界检查、空指针检测等。开发者需要自行确保代码的安全性,否则可能会引发运行时错误甚至安全漏洞。在编写音视频处理软件时,需格外注意边界条件和异常处理,以防止程序崩溃或数据损坏。
  • 标准库支持有限:

    • C++标准库在音视频处理相关的功能上较为基础,许多高级功能需要借助第三方库。这增加了项目的依赖管理和版本控制的复杂性,有时需要处理不同库之间的兼容性和接口差异。

常见开源框架

Live555

  • 项目主页:https://www.live555.com/liveMedia/
  • 简介
    • Live555是一个高度成熟的、跨平台的C++开源库,专注于实时流媒体协议的实现。它提供了对多种流媒体传输协议的支持,包括但不限于:
      • RTP (Real-time Transport Protocol)
      • RTCP (Real-time Transport Control Protocol)
      • RTSP (Real-time Streaming Protocol)
      • SIP (Session Initiation Protocol)
    • Live555还支持多种媒体格式的封装和解封装,如MPEG Transport Stream (TS)、H.264/AAC编码的FLV、MP4等。这个库广泛用于构建流媒体客户端(如播放器)和服务器端应用程序,适用于视频会议、IPTV、监控系统等多种场景。
  • 特点
    • 跨平台:支持Windows、Linux、macOS以及嵌入式系统。
    • 全面的协议支持:方便实现基于RTSP、RTP等协议的流媒体服务。
    • 可扩展性:用户可以基于其基础架构开发定制化的流媒体组件。
    • 社区活跃:有丰富的文档和示例代码供开发者参考。

SRS(Simple Realtime Streaming Server)

  • 项目主页:https://github.com/ossrs/srs
  • 简介:
    • SRS是一款高性能、易用且功能丰富的C++开源流媒体服务器,特别针对实时流媒体场景进行了优化。它支持多种流媒体协议:
      • RTMP (Real-Time Messaging Protocol)
      • HLS (HTTP Live Streaming)
      • DASH (Dynamic Adaptive Streaming over HTTP)
      • WebRTC
  • 特点
    • 高并发:通过多进程模型和高效内存管理实现大规模并发直播和点播服务。
    • 扩展性强:可通过插件系统添加新功能或与其他系统集成。
    • 云原生支持:能够无缝部署在Kubernetes等容器环境中,并与CDN、云存储服务良好对接。
    • 实时转码、录制、截图等功能:内置FFmpeg支持多种媒体处理任务。

GStreamer

  • 项目主页:https://gstreamer.freedesktop.org/
  • 简介
    • :GStreamer是一个模块化、跨平台的多媒体框架,尽管主要以C语言编写,但其API对C++友好,且拥有丰富的C++绑定。GStreamer不仅适用于流媒体,还能处理音频、视频、图像等各种媒体类型。它采用流水线(pipeline)概念来构建复杂的处理链路,支持以下特性:
      • 多种输入/输出协议:RTSP、RTP、HTTP、HLS、DASH、UDP、TCP等。
      • 广泛的编解码器支持:通过插件系统集成各种音频、视频编码器和解码器。
      • 媒体处理能力:过滤、转换、混音、特效等。
      • 适应多种平台:桌面、移动、嵌入式设备等。

FFmpeg

  • 项目主页:https://ffmpeg.org/
  • 简介
    • FFmpeg是一个著名的多媒体处理工具集合和库,包含了大量的音频/视频编解码器、格式转换工具以及流处理功能。尽管其命令行工具更广为人知,但FFmpeg的库部分(libavformat、libavcodec等)可以被C++项目直接调用,用于实现流媒体相关的功能,如:
      • 多种流媒体协议解析与封装:RTSP、RTMP、HTTP、HLS等。
      • 音视频编解码:支持几乎所有的主流编码格式。
      • 转码、过滤、流复用与拆分等高级处理。

ZLMediaKit

  • 项目主页:https://github.com/ZLMediaKit/ZLMediaKit
  • 简介
    • ZLMediaKit是一个高性能、轻量级的C++流媒体服务器框架,专为互联网音视频直播、点播业务设计。它支持多种流媒体协议和功能,适用于构建大规模的流媒体服务平台。
  • 主要特性
    • 协议支持:
      • RTMP:支持Flash、移动端(如H5)的直播推流与拉流。
      • HLS:兼容iOS、Android、桌面浏览器的直播与点播。
      • RTSP:用于传统IP摄像头、NVR等设备的接入与分发。
      • HTTP-FLV:提供低延迟的直播观看体验。
      • WebSocket-FLV:基于WebSocket的低延迟直播传输方式。
      • GB28181:符合中国国标标准的视频监控系统协议。
    • 功能丰富:
      • 简单易用的API,便于快速集成与二次开发。
      • 支持多路同时推流、多路同时拉流。
      • 流媒体录制、时移回放(VOD)功能。
      • 视频转码、水印、截图等媒体处理能力。
      • 支持鉴权、防盗链、IP黑名单等安全措施。
      • 提供HTTP API接口,方便远程管理和监控。
    • 高性能与稳定性:
      • 采用事件驱动、无锁设计,保证高并发下的性能表现。
      • 内存池管理、线程池优化,减少资源消耗。
      • 自动恢复断线重连,保证服务连续性。
      • 支持负载均衡,可集群部署以应对大规模流量。

Nginx RTMP Module

  • 项目主页:https://github.com/arut/nginx-rtmp-module
  • 简介
    • Nginx RTMP Module是为Nginx服务器添加RTMP(Real-Time Messaging Protocol)支持的一个模块。通过将该模块与Nginx集成,可以将Nginx打造成一个高效的RTMP流媒体服务器,支持直播推流、拉流以及录制等功能。Nginx RTMP Module具有以下特点:
      • 轻量级:基于成熟的Nginx服务器,占用资源少,易于部署和维护。
      • RTMP支持:接收和分发RTMP直播流,兼容常见的推流软件(如OBS)和播放器。
      • HLS支持:自动将RTMP流转化为HLS格式,供移动设备和桌面浏览器播放。
      • 录制与回放:支持按需录制直播流,并通过HTTP提供录像文件的点播服务。
      • 简单配置:通过修改Nginx配置文件即可设置服务器参数、流权限等。

NGINX-VOD-Module

  • 项目主页:https://github.com/kaltura/nginx-vod-module
  • 简介
    • NGINX-VOD-Module是为Nginx添加视频点播(VOD)功能的模块,主要用于处理HTTP/HTTPS协议下的视频流。与Nginx RTMP Module配合使用,可以构建一套完整的流媒体服务体系。主要特性包括:
      • HTTP/HTTPS VOD:支持通过HTTP/HTTPS协议播放MP4、FLV等格式的视频文件。
      • 切片缓存:对视频文件进行切片缓存,实现平滑的流式播放和快速的随机访问。
      • 带宽节省:通过自适应码率流控和HTTP Range请求,减少网络带宽消耗。
      • DRM支持:集成Widevine、PlayReady等数字版权管理(DRM)系统,保护内容安全。
      • 灵活配置:通过Nginx配置文件定义VOD服务的各项参数和规则。

MediaServer

  • 项目主页:https://github.com/ossrs/srs/wiki/Product
  • 简介
    • MediaServer是由SRS(Simple Realtime Streaming Server)团队开发的一款全功能、高性能的流媒体服务器,基于C++实现。MediaServer除了继承SRS的优秀特性外,还扩展了更多的功能和协议支持:
      • 全面协议:支持RTMP、HLS、FLV、HTTP-FLV、WebRTC等多种流媒体协议。
      • 超低延迟:优化传输协议和算法,实现亚秒级的直播延迟。
      • 转码与处理:内置FFmpeg,支持实时转码、混流、截图、水印等媒体处理。
      • 云原生:支持Kubernetes、Docker等容器化部署,易于扩展和管理。
      • API管理:提供丰富的HTTP API接口,便于远程控制和统计分析。

简介

  • 记录2024年求职面试遇到的C++面试问题

STL分为哪几类

STL (Standard Template Library) 主要由以下几类组件构成:

  1. 容器(Containers)

    • 序列式容器(Sequence Containers):vector, deque, list, array
    • 关联式容器(Associative Containers):set, map, multiset, multimap
    • 无序容器(Unordered Containers):unordered_set, unordered_map, unordered_multiset, unordered_multimap
  2. 迭代器(Iterators)

    • 输入迭代器(Input Iterator)
    • 输出迭代器(Output Iterator)
    • 前向迭代器(Forward Iterator)
    • 双向迭代器(Bidirectional Iterator)
    • 随机访问迭代器(Random Access Iterator)
  3. 算法(Algorithms)

    • 非修改算法(Non-modifying algorithms):find, count, for_each, 等
    • 修改算法(Modifying algorithms):copy, swap, transform, 等
    • 排序算法(Sorting algorithms):sort, partial_sort, nth_element, 等
    • 其他算法:search, merge, includes, 等
  4. 函数对象(Function Objects):如 less, greater, plus, minus, 等

  5. 适配器(Adapters)

    • 容器适配器(Container Adapters):stack, queue, priority_queue
    • 迭代器适配器(Iterator Adapters):reverse_iterator, back_insert_iterator, front_insert_iterator, 等
    • 函数适配器(Function Adapters):bind, function, mem_fn, 等

这些组件共同构成了STL,使其能够提供高效且灵活的操作来处理各种数据结构和算法。

STL常用的几个容器有哪些?用时间复杂度,空间复杂度对比一下

STL中常用的容器主要包括:vector, deque, list, set, map, unordered_set, unordered_map。下面是这些容器的时间复杂度和空间复杂度对比:

1. vector

  • 时间复杂度
    • 访问元素:O(1)
    • 插入/删除(末尾):O(1) 均摊
    • 插入/删除(中间或开头):O(n)
  • 空间复杂度
    • 动态数组,空间效率较高,但可能有一些额外的空间用于容器增长。

2. deque

  • 时间复杂度
    • 访问元素:O(1)
    • 插入/删除(两端):O(1)
    • 插入/删除(中间):O(n)
  • 空间复杂度
    • 双端队列,通常比 vector 多一些空间开销,但仍然是较为紧凑的。

3. list

  • 时间复杂度
    • 访问元素:O(n)
    • 插入/删除(任意位置):O(1)
  • 空间复杂度
    • 链表,每个元素需要额外的指针空间,相较于 vectordeque 更高。

4. set

  • 时间复杂度
    • 插入/删除/查找:O(log n)
  • 空间复杂度
    • 平衡二叉搜索树,空间开销较高,需要额外的指针。

5. map

  • 时间复杂度
    • 插入/删除/查找:O(log n)
  • 空间复杂度
    • 平衡二叉搜索树,与 set 类似,空间开销较高。

6. unordered_set

  • 时间复杂度
    • 插入/删除/查找:O(1) 均摊
  • 空间复杂度
    • 哈希表,需要额外的空间用于哈希桶。

7. unordered_map

  • 时间复杂度
    • 插入/删除/查找:O(1) 均摊
  • 空间复杂度
    • 哈希表,与 unordered_set 类似,空间开销较高。

对比总结

  • 访问元素vectordeque 的随机访问效率最高,list 最低。
  • 插入/删除list 在任意位置插入/删除效率最高,vectordeque 末尾插入/删除效率高,但在中间位置操作效率低。
  • 查找:有序容器(如 setmap)和无序容器(如 unordered_setunordered_map)均提供高效的查找,但有序容器的查找是O(log n),无序容器为O(1)均摊。
  • 空间效率vectordeque 空间效率较高,list 和基于树结构的 set/map 空间开销较高,无序容器(哈希表)则需要额外空间用于哈希桶。

单例模式怎么实现

单例模式(Singleton Pattern)是一种设计模式,确保一个类只有一个实例,并提供一个全局访问点。实现单例模式的方式有很多种,以下是几种常见的实现方法:

1. 懒汉式(线程不安全)

懒汉式单例模式在第一次使用时创建实例。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Singleton {
private:
static Singleton* instance;
Singleton() {} // 私有构造函数

public:
static Singleton* getInstance() {
if (instance == nullptr) {
instance = new Singleton();
}
return instance;
}
};

// 初始化静态成员
Singleton* Singleton::instance = nullptr;

2. 懒汉式(线程安全,使用互斥锁)

使用互斥锁确保线程安全,但会影响性能。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <mutex>

class Singleton {
private:
static Singleton* instance;
static std::mutex mtx;
Singleton() {} // 私有构造函数

public:
static Singleton* getInstance() {
std::lock_guard<std::mutex> lock(mtx);
if (instance == nullptr) {
instance = new Singleton();
}
return instance;
}
};

// 初始化静态成员
Singleton* Singleton::instance = nullptr;
std::mutex Singleton::mtx;

3. 饿汉式

饿汉式单例模式在类加载时就创建实例。

1
2
3
4
5
6
7
8
9
10
11
12
13
class Singleton {
private:
static Singleton* instance;
Singleton() {} // 私有构造函数

public:
static Singleton* getInstance() {
return instance;
}
};

// 初始化静态成员
Singleton* Singleton::instance = new Singleton();

4. 双重检查锁(Double-Checked Locking)

双重检查锁在提高性能的同时确保线程安全。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <mutex>

class Singleton {
private:
static Singleton* instance;
static std::mutex mtx;
Singleton() {} // 私有构造函数

public:
static Singleton* getInstance() {
if (instance == nullptr) {
std::lock_guard<std::mutex> lock(mtx);
if (instance == nullptr) {
instance = new Singleton();
}
}
return instance;
}
};

// 初始化静态成员
Singleton* Singleton::instance = nullptr;
std::mutex Singleton::mtx;

5. 使用局部静态变量(C++11及以上)

C++11标准保证了静态局部变量的线程安全性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Singleton {
private:
Singleton() {} // 私有构造函数

public:
static Singleton& getInstance() {
static Singleton instance;
return instance;
}

// 禁用拷贝构造函数和赋值运算符
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
};

6. 使用 call_once(C++11及以上)

std::call_once 保证了一次性初始化。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <mutex>

class Singleton {
private:
Singleton() {} // 私有构造函数
static Singleton* instance;
static std::once_flag initFlag;

public:
static Singleton* getInstance() {
std::call_once(initFlag, []() {
instance = new Singleton();
});
return instance;
}

// 禁用拷贝构造函数和赋值运算符
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
};

// 初始化静态成员
Singleton* Singleton::instance = nullptr;
std::once_flag Singleton::initFlag;

这几种方法各有优缺点,选择哪种实现方式取决于具体的需求和场景。

手动实现一个栈或队列

下面是手动实现一个栈(Stack)和一个队列(Queue)的示例代码,分别使用C++语言:

栈的实现

一个简单的栈可以使用数组或链表来实现。这里我们使用链表来实现一个栈。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
#include <iostream>

// 节点结构体
struct Node {
int data;
Node* next;
Node(int value) : data(value), next(nullptr) {}
};

class Stack {
private:
Node* top;

public:
Stack() : top(nullptr) {}

// 入栈
void push(int value) {
Node* newNode = new Node(value);
newNode->next = top;
top = newNode;
}

// 出栈
void pop() {
if (top == nullptr) {
std::cerr << "Stack is empty." << std::endl;
return;
}
Node* temp = top;
top = top->next;
delete temp;
}

// 获取栈顶元素
int peek() {
if (top == nullptr) {
std::cerr << "Stack is empty." << std::endl;
return -1;
}
return top->data;
}

// 检查栈是否为空
bool isEmpty() {
return top == nullptr;
}

~Stack() {
while (top != nullptr) {
pop();
}
}
};

int main() {
Stack stack;
stack.push(10);
stack.push(20);
stack.push(30);

std::cout << "Top element is " << stack.peek() << std::endl;

stack.pop();
std::cout << "Top element is " << stack.peek() << std::endl;

return 0;
}

队列的实现

一个简单的队列可以使用数组或链表来实现。这里我们使用链表来实现一个队列。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
#include <iostream>

// 节点结构体
struct Node {
int data;
Node* next;
Node(int value) : data(value), next(nullptr) {}
};

class Queue {
private:
Node* front;
Node* rear;

public:
Queue() : front(nullptr), rear(nullptr) {}

// 入队
void enqueue(int value) {
Node* newNode = new Node(value);
if (rear == nullptr) {
front = rear = newNode;
return;
}
rear->next = newNode;
rear = newNode;
}

// 出队
void dequeue() {
if (front == nullptr) {
std::cerr << "Queue is empty." << std::endl;
return;
}
Node* temp = front;
front = front->next;
if (front == nullptr) {
rear = nullptr;
}
delete temp;
}

// 获取队首元素
int peek() {
if (front == nullptr) {
std::cerr << "Queue is empty." << std::endl;
return -1;
}
return front->data;
}

// 检查队列是否为空
bool isEmpty() {
return front == nullptr;
}

~Queue() {
while (front != nullptr) {
dequeue();
}
}
};

int main() {
Queue queue;
queue.enqueue(10);
queue.enqueue(20);
queue.enqueue(30);

std::cout << "Front element is " << queue.peek() << std::endl;

queue.dequeue();
std::cout << "Front element is " << queue.peek() << std::endl;

return 0;
}

这两个示例展示了如何使用链表来实现栈和队列的基本操作,包括入栈/入队、出栈/出队、获取栈顶/队首元素,以及检查栈/队列是否为空。

rtsp视频流是什么

RTSP(Real-Time Streaming Protocol,即实时流传输协议)是一种网络协议,用于控制音频或视频流的传输。RTSP允许客户端从服务器请求媒体流并进行播放、暂停、停止等操作。它通常用于IP摄像头、直播流媒体服务器和其他需要实时流传输的应用场景。

RTSP协议的主要特点包括:

  1. 实时性:RTSP适用于需要实时传输音视频的应用,如直播和视频会议。
  2. 控制性:RTSP允许客户端对流媒体进行控制,例如开始、暂停、停止和快进。
  3. 独立于传输:RTSP可以与各种传输协议(如RTP、UDP、TCP等)结合使用,以适应不同的网络环境。
  4. 灵活性:RTSP可以支持不同的媒体格式和传输机制,使其适应各种应用需求。

常见的RTSP使用场景包括:

  • 网络摄像头(IP摄像头)的实时视频监控
  • 流媒体服务器的直播传输
  • 视频点播(VoD)服务

RTSP流的典型URL格式如下:

1
rtsp://[用户名]:[密码]@[IP地址]:[端口]/[路径]

例如:

1
rtsp://user:password@192.168.1.100:554/stream1

这表示客户端可以使用RTSP协议连接到IP地址为192.168.1.100的服务器,端口为554,并请求路径为/stream1的媒体流。

实时传输协议RTP与RTCP

  • RTP(Real-time Transport Protocol)是用于Internet上针对多媒体数据流的一种传输协议。

  • RTP协议是建立在UDP协议上的。RTP协议详细说明了在互联网上传递音频和视频的标准数据包格式。RTP协议常用于流媒体系统(配合RTCP协议),视频会议和视频电话系统(配合H.263或SIP)

  • RTP本身并没有提供按时发送机制或其他服务质量(Qos)保证,它依赖于底层服务去实现这一过程。RTP并不保证传送或防止无序传送,也不确定底层网络的可靠性。

  • RTP中的序列号允许接收方重组发送方的包序列,同时序列号也能用于决定适当的包位置,例如:在视频解码中,就不需要顺序解码。

  • 实时传输控制协议(Real-time Transport Control Protocol, RTCP)是实时传输协议(RTP)的一个姐妹协议。RTCP为RTP媒体流提供信道外控制。RTCP定期在流媒体会话参加者之间传输控制数据。

  • RTCP的主要功能是为RTP所提供的服务质量提供反馈。RTCP收集相关媒体连接的统计信息,例如:传输字节数,传输分组数,丢失分组数,时延抖动,单向和双向网络延迟等等。

  • 网络应用程序可以利用RTCP所提供的信息试图提高服务质量,比如限制信息流量或改用压缩比较小的编解码器。RTCP本身不提供数据加密或身份认证,其伴生协议STRCP(安全实时传输控制协议)则可以用于此类用途。

RTP协议是什么

RTP(Real-time Transport Protocol,即实时传输协议)是一种网络协议,专门用于通过IP网络传输实时音视频数据。它是由IETF(Internet Engineering Task Force)定义的标准协议,用于在互联网上传输实时媒体流。

RTP协议的主要特点和功能包括:

  1. 实时传输:RTP适用于需要低延迟的实时应用,如视频会议、IP电话和流媒体播放。
  2. 数据包封装:RTP将音视频数据封装成数据包,以便在网络上传输。每个RTP包包含一个序列号和时间戳,用于保证数据包的顺序和同步。
  3. 传输质量监控:RTP通常与RTCP(Real-time Transport Control Protocol,即实时传输控制协议)一起使用。RTCP提供了传输质量的反馈信息,如丢包率、延迟和抖动等,帮助优化传输质量。
  4. 灵活性:RTP支持各种音视频编解码器和媒体格式,使其适用于不同类型的媒体流。

RTP通常与其他协议一起使用,如:

  • RTSP(实时流传输协议):用于控制RTP流的传输。
  • SIP(会话发起协议):用于建立、修改和终止多媒体会话,如IP电话。
  • SDP(会话描述协议):用于描述多媒体会话的参数,包括媒体类型、格式、传输地址和端口等。

一个典型的RTP数据包结构如下:

  • 版本号:指示RTP版本。
  • 序列号:用于标识数据包的顺序。
  • 时间戳:用于同步音视频流。
  • 有效负载类型:指示数据包中承载的媒体类型和编码方式。
  • 源标识符(SSRC):唯一标识发送RTP流的源。

通过这些特性,RTP能够确保实时音视频数据的有效传输和同步,从而在互联网上提供高质量的实时多媒体通信。

RTCP协议是什么

RTCP(Real-time Transport Control Protocol,即实时传输控制协议)是一种网络协议,用于与RTP(Real-time Transport Protocol,即实时传输协议)一起工作,提供对实时数据流传输质量的监控和控制。RTCP并不传输媒体数据本身,而是传输控制信息,帮助管理和优化媒体传输。

RTCP的主要功能包括:

  1. 传输质量反馈:RTCP提供有关传输质量的反馈信息,如数据包丢失率、延迟和抖动。这些信息可以帮助发送方和接收方调整传输参数,以提高传输质量。

  2. 参与者标识:RTCP允许每个参与者报告其标识信息,如名称和邮箱地址。这有助于在多方通信中识别各个参与者。

  3. 带宽管理:RTCP可以帮助控制会话的带宽使用。通过反馈信息,发送方可以调整传输速率,以避免网络拥塞。

  4. 同步多媒体流:RTCP可以协助同步来自不同源的多个RTP流(如音频和视频),以确保它们在接收端正确地同步播放。

RTCP报文类型主要有以下几种:

  1. SR(Sender Report,发送报告):由RTP流的发送者定期发送,包含发送的RTP包数和字节数,以及与接收者同步的时间信息。

  2. RR(Receiver Report,接收报告):由RTP流的接收者定期发送,包含接收到的RTP包数、丢包率、往返时间等统计信息。

  3. SDES(Source Description Items,源描述项):包含源标识符和相关的文本信息(如参与者的名字)。

  4. BYE:通知结束会话的消息,表示发送者将退出会话。

  5. APP(Application Specific Message,应用特定消息):用于传输特定应用定义的控制信息。

通过与RTP一起使用,RTCP能够提供有效的传输控制和质量管理,确保实时媒体流的传输质量和可靠性。这使得RTCP成为多媒体通信中不可或缺的协议之一。

实时消息传输协议RTMP

RTMP(Real-Time Messaging Protocol,即实时消息传输协议)是一种由Adobe Systems开发的协议,主要用于在互联网或局域网上进行音频、视频和数据的实时传输。RTMP最初用于支持Adobe Flash播放器,但现已被广泛用于各种流媒体传输应用,尤其是在直播流媒体中。

RTMP的主要特点和功能包括:

  1. 低延迟传输:RTMP能够实现低延迟的音视频数据传输,这使其非常适用于直播和实时互动场景。
  2. 持续连接:RTMP通过TCP连接传输数据,保持与服务器的持久连接,以确保数据的可靠传输和最小化延迟。
  3. 多路复用:RTMP支持将音频、视频和控制信息多路复用在一个TCP连接上,从而简化了传输过程。
  4. 灵活性:RTMP支持多种音频和视频编解码器,包括H.264和AAC,这使其能够适应不同的媒体格式和质量要求。
  5. 控制消息:RTMP支持多种控制消息,如暂停、播放、停止等,以便对流媒体进行实时控制。

RTMP的传输过程通常分为三个阶段:

  1. 握手阶段:客户端和服务器通过交换握手消息建立连接,并协商通信参数。
  2. 连接阶段:客户端向服务器发送连接请求,并建立会话。
  3. 数据传输阶段:客户端和服务器之间开始传输音视频数据和控制消息。

RTMP有几个变种,包括:

  • RTMPT(RTMP Tunneled):通过HTTP进行封装,以绕过防火墙。
  • RTMPS(RTMP Secure):使用SSL/TLS加密的RTMP,提供安全的数据传输。
  • RTMPE(RTMP Encrypted):使用Adobe的加密机制进行加密,提供安全的数据传输。

一个典型的RTMP URL格式如下:

1
rtmp://[服务器IP或域名]/[应用名称]/[流名称]

例如:

1
rtmp://live.example.com/app/stream

这表示客户端可以使用RTMP协议连接到live.example.com服务器,请求app应用中的stream流。

尽管RTMP已经不再是主流,但它仍然在许多直播平台和视频传输系统中使用。现代替代方案如HLS(HTTP Live Streaming)和DASH(Dynamic Adaptive Streaming over HTTP)在许多新应用中更为常见。

  • RTMP是Adobe Systems公司为Flash播放器和服务器之间音频,视频和数据传输开发的开放协议。它有三种变种

    • 工作在TCP之上的明文协议,使用端口1935
    • RTMPT封装在HTTP请求之中,可穿越防火墙
    • RTMPS类似RTMPT,但使用的是HTTPS连接
  • RTMP视频播放的特点是

    • RTMP协议是采用实时的流式传输,所以不会缓存文件到客户端,这种特性说明用户想下载RTMP协议下的视频是比较难的。
    • 视频流可以随意拖动,既可以从任意时间点向服务器发送请求进行播放,并不需要视频有关键帧。相比而言,HTTP协议下视频需要有关键帧才可以随意拖动
    • RTMP协议支持点播/回放(通俗点讲,就是支持把flv, f4v, mp4文件放在RTMP服务器,客户端可以直接播放),直播(边录制视频边播放)

HLS

HLS(HTTP Live Streaming,即HTTP实时流传输协议)是一种基于HTTP的流媒体传输协议,由苹果公司开发。HLS最早在2009年与iOS 3.0一起发布,现已成为一种广泛使用的流媒体传输标准,适用于直播和点播视频。

HLS的主要特点和功能包括:

  1. 基于HTTP:HLS使用标准的HTTP协议传输数据,因此能够通过现有的HTTP基础设施(如CDN和HTTP缓存服务器)进行流媒体分发。
  2. 分片传输:HLS将媒体内容分割成小的时间片段(通常是几秒钟的长度),每个片段以单独的HTTP请求进行传输。这种分片传输方式可以更好地适应网络波动,提供更流畅的观看体验。
  3. 自适应比特率:HLS支持自适应比特率传输,客户端可以根据当前的网络状况动态选择不同质量的媒体片段,从而在网络带宽变化时提供最佳的观看体验。
  4. 跨平台支持:HLS不仅支持苹果设备(如iPhone、iPad和Apple TV),还可以在其他操作系统和设备上使用,包括Android设备、智能电视和Web浏览器。
  5. 简单实现:由于HLS基于HTTP协议,实现相对简单,并且容易与现有的Web技术集成。

HLS工作流程如下:

  1. 媒体切片:服务器将媒体内容分割成一系列小的、连续的TS(MPEG-2 Transport Stream)文件,这些文件通常是几秒钟的长度。
  2. 索引文件(M3U8):服务器生成一个索引文件(通常是M3U8格式),列出所有媒体片段的URL。这个文件称为播放列表,包含了媒体片段的顺序和位置。
  3. 客户端请求:客户端请求播放列表(M3U8文件),并根据播放列表中的URL逐个请求媒体片段。
  4. 播放和缓存:客户端下载并播放媒体片段,同时可以缓存一定数量的片段以应对网络波动。

一个典型的HLS播放列表文件(M3U8)的示例如下:

1
2
3
4
5
6
7
8
9
10
#EXTM3U
#EXT-X-VERSION:3
#EXT-X-TARGETDURATION:10
#EXT-X-MEDIA-SEQUENCE:0
#EXTINF:10.0,
http://example.com/segment0.ts
#EXTINF:10.0,
http://example.com/segment1.ts
#EXTINF:10.0,
http://example.com/segment2.ts

这个播放列表文件指示客户端依次请求和播放segment0.tssegment1.tssegment2.ts,每个片段长度为10秒。

HLS在流媒体行业中得到广泛应用,特别是在直播和视频点播服务中,成为了许多视频传输解决方案的基础。

  • HTTP Live Streaming(HLS)是苹果公司实现的基于HTTP的流媒体传输协议,可实现流媒体的直播和点播,主要应用于IOS系统。HLS点播是分段HTTP点播,不同在于它的分段非常小。要实现HLS点播,重点在于对媒体文件分段,目前有不少开源工具可以使用。
  • 相对于常见的流媒体直播协议,HLS直播最大的不同在于,直播客户端获得的并不是一个完整的数据流,HLS协议再服务器端将直播数据流存储为连续的,很短时长的媒体文件(MPEG-TS格式)。而客户端则不断的下载并播放这些小文件,因为服务器总是会将最新的直播数据生成新的小文件,这样客户端只要不停的按顺序播放从服务器获取到的文件,就实现了直播。由此可见,基本上可以认为,HLS是以点播的技术方式实现直播。由于数据通过HTTP协议传输,所以完全不用考虑防火墙或者代理的问题,而且分段文件的时长很短,客户端可以很快的选择和切换码率,以适应不同带宽条件下的播放。不过HLS的这种技术特点,决定了它的延迟一般总是会高于普通的流媒体直播协议。

中间件的定义

  • 中间件这个术语第一次出现是1968年在德国加尔米施帕滕基兴举办的 NATO 软件工程大会 结束后发表的一份报告中。

  • 这届大会正式确定了软件工程(Software Engineering)的概念,同时还探讨了软件设计、生产和分发等主题。

  • 中间件(Middleware),又译中介层,是一类提供系统软件和应用软件之间连接,便于软件各部件之间的沟通的软件,应用软件可以借助中间件在不同的技术架构之间共享信息和资源。中间件位于客户机服务器的操作系统之上,管理者计算资源和网络通信。

什么不是中间件

  • 我们按照类别来看一些经常会遇到的一些不是中间件的概念
    • 业务平台不是中间件,业务平台是从服务的视角抽象的能同时支撑多个业务,业务之间的信息能形成交互和增强的平台。
    • 营销工具不是中间件,营销工具是直接作用于最终消费者用户的软件或者插件服务。
    • 二方/三方工具包不是中间件,二方/三方工具包是在各种场景的程序开发过程中沉淀的一些常用工具类(功能)的集合,包含于软件代码本身。
    • SaaS 不是中间件,SaaS(Software as a Service) 更多的是一种软件交付模式,无需用户安装,通过网络在线访问的一种服务模式。
    • PaaS 不是中间件,PaaS(Platform as a Service) 将软件研发的平台做为一种服务,提供软件部署平台(强调的是屏蔽系统和软件细节的runtime平台)。

评判关键

  • 从定义可以总结出评判的几个地方
    • 性质:中间件是软件。
    • 作用层级:系统软件和应用软件之间、软件各部件之间;管理客户机与系统软件之间的计算资源和网络通信。
    • 服务对象:中间件为应用软件服务,应用软件为最终用户服务,最终用户并不直接使用中间件。

中间件的好处

  • 中间件能给客户带来什么?
    • 为上层应用软件的开发提供便捷的、开箱即用的服务交互和计算的能力,缩短开发周期;屏蔽底层runtime的差异;节省应用本身的系统资源,减少运行成本。

中间件分类,什么时候使用中间件

  • 基于中间件的定义我们知道中间件是连接软件与系统之间的服务,那么我们什么时候使用了中间件,在哪些地方用到了中间件了。我们不妨假设一个http请求过程来窥视一番
    • 当你在浏览器中输入一个网址时,它会通过 DNS 解析到目标服务注册的公网IP地址
    • 请求到达目标服务的 web 反向代理服务器 Tengine 之后,经过一定的过滤转发到目标服务A上
    • 服务A通过 RPC框架 Dubbo 请求服务B的结果做中间计算,并且从 Tair 缓存中读取计算因子,计算结果
    • 服务A接着使用 Druid 通过 TDDL 写入计算结果到 MySQL Master 节点然后返回结果
    • 异步过程中 Canal 通过模拟 Binlog 主从复制的原理,迅速将这条 Binlog 消费并下发到消息队列 RocketMQ
    • 服务C通过 RocketMQ 消费到事件之后,通过配置中心 ConfigServer 拉取到的策略进行对应策略的事件处理。
  • 这个过程中我们使用了一系列的中间件来协同各个微服务完成整个流程,如web反向代理服务器 Tengine、RPC框架 Dubbo、缓存 Tair、连接池 Driud、数据库代理层 TDDL、Binlog 同步工具 Canal、消息队列 RocketMQ、配置中心 ConfigServer。

常用基础中间件

  • 路由与web服务器:处理和转发其他服务器通信数据的服务器。 如被业界广泛使用的阿里基于 Nginx 研发的 Tengine、阿里内部的集中式路由服务 VipServer
  • RPC框架:微服务时代的远程服务调用框架。如grpc, Thrift, 阿里的 HSF, Dubbo, SOFA-RPC
  • 消息中间件:支持在分布式系统之间发送和接收消息的软件。 如 Apache kafka, Apache RabbitMQ, NSQ, 阿里孵化开源的 Apache RocketMQ
  • 缓存服务: 分布式的高速数据存储层,一般是内存存储。如 阿里 Tair,业界的 Redis, Memcached, Ehcache
  • 配置中心:用来统一管理各个项目中所有配置的系统。如 阿里 Nacos、携程 Apollo、百度 Disconf
  • 分布式事务:事务的参与者、支持事务的服务器、资源服务器以及事务管理器分别位于不同的分布式系统的不同节点之上。 如 阿里 seata、腾讯 DTF
  • 任务调度:分布式环境下提供定时、任务编排、分布式跑批等功能的系统。如 阿里 SchedulerX、业界 xxl-job、当当 elastic-job、有赞 TSP
  • 数据库层 用于支持弹性扩容和分库分表的 TDDL,数据库连接池 Driud, Binlog 同步的 Canal 等

中间件云产品

  • 随着云时代的到来,大量公司的业务向云上迁移;为了云上客户能够便捷的使用稳定高效的中间件能力,云厂商开始将自身沉淀的基础中间件能力云化,用于支撑各个云上客户和自身业务的快速生长

什么是中间件开发

  • 随着国内软件行业的发展,国内互联网公司规模越来越大,业务越来越复杂,随之使用大量的中间件来提升后台服务性能。由此产生了中间件开发和维护人员。
  • 诚然,在小公司,中间件,例如缓存,MQ,RPC 等服务,极大可能是由业务开发人员自己维护,或者委托第三方云平台运维(支付一些费用)。但,如果后台开发超过 200 人,基本就会组建自己的中间件或者基础架构团队,用于维护后台服务器基础架构和中间
  • 更大规模的公司,则由于各种各样的原因(性能,KPI),会自己开发中间件,简称自研。这要求中间件团队需要更多的人员。

中间件开发人员需要哪些素质

  • 既然需要中间件开发人员,那么中间件开发人员一般从哪里招聘呢?招聘的要求是什么

  • 通常,一个公司在刚开始组建中间件团队的时候,都会从公司内部挑选精英人才,或者挑选对中间件感兴趣的人才。这时候,可能你没有相关经验,但你仍然有机会参与到中间件开发中。反之,如果你没有中间件开发经验,想通过招聘的方式进入中间件行业,那么相对而言,会有些曲折

  • 那么,假设,你想从事中间件开发,但,你没有中间件开发经验,且,你的公司也没有组建中间件团队的打算。

  • 该怎么突破?

    • 跳槽。跳槽到别的公司的中间件团队。
  • 这里就涉及到了一个中间件团队需要哪些技能。因为跳槽肯定就要面试,如果你面试的是中间件岗位,那么自然,就需要准备中间件的相关知识。

  • 另外,还有一点,在这个分工明确的时代,即使是中间件,也有很多种类,我这里稍微分一下,可能不是很准确。

    • 服务治理中间件,例如 RPC 相关中间件,限流熔断,链路追踪,分布式配置中心等等。你可以从 SpringCloud 里找到相关的产品。当然国内也有很多优秀的产品。
    • 存储中间件,例如缓存,MQ等等,如果存储涉及到分布式(通常都会涉及),那么要求相对较高。
    • 各种 Proxy,不论是数据库,还是 Cache,还是各种存储,通常单机无法承载海量数据,比较简单的办法就是使用 Proxy 进行代理,让应用透明的使用集群。出于性能考虑,这里通常会使用性能较高的产品,例如 goLang,C++ 等。Java 的长处——开发效率,在这个地方权重不大。
    • 各种分布式中间件,例如 ZK 这种,这个我个人认为难度是较大的。分布式向来是软件开发中比较困难的一个点。特别是涉及到存储和一致性。
    • 容器相关,k8s,docker等,容器化已经是大势所趋
  • 回到之前的话题: 一个中间件开发者需要哪些素质?

    • 语言基础。从 Java 程序员的角度,基础通常就是:集合,并发,JVM,Netty,IO、NIO(mmap,sendfile)
    • 计算机基础,由于中间件开发人员经常和 OS 打交道,所以计算机基础也必不可少,例如文件系统(IO/磁盘),进程线程,内存管理。
    • 网络基础,搞后台的人员,肯定要对网络熟悉了,熟悉在 Linux 下排查网络问题,熟悉 Epoll 原理等。
    • 分布式相关知识,互联网海量数据背景下,分布式知识必不可少,CAP, Paxos,Raft,zab,2pc,3pc,base等等。最好能根据这些理论写出实现代码。
    • 熟悉开源实现,即使你是业务开发人员,你也 100% 会接触开源项目,例如 Spring,那么,通常你需要对这种常用的开源代码有深刻的理解,不仅知晓其原理,也领会其设计。从大的角度看,你得看清整个框架的背景,设计和取舍,从小的角度看,你得看清框架的内部实现细节,有哪些有趣的地方(通常这种框架都会进行性能优化)。
    • 了解行业风向标,中间件行业和业务开发稍有不同,每个中间件的版本升级都会让该领域的开发者们侧目(类似 iPhone 发布会),了解其特性,进而了解行业趋势,最后成为行业引领

简介

  • AI工程师侧重于将算法应用到实际系统中,开发相应的软件或产品。
  • AI工程师的专业知识北京更侧重于软件工程,系统架构和编程。需要将AI算法有效的集成到产品和服务中,这不仅包括算法的实现,还包括优化系统性能,确保算法稳定运行在不同的平台和环境中。因此,AI工程师需要具备强大的编程能力,熟悉软件开发生命周期,以及掌握一些关于系统设计和架构的知识。

boss直聘上相关岗位职责

  • 负责推理场景模型适配优化,含ONNX模型转换,精度对齐,性能分析,优化方案设计和开发实现(含Pass/融合算子),部署上线等

  • 负责建设维护自动驾驶AI基础设施,为分布式训练系统,大数据平台进行性能优化迭代,保障系统底座稳定性

  • 设计并实现AI Infra的计算,存储,网络架构及AI应用的通信,I/O效率优化方案,并系统化落地

  • 确保系统的高效,稳定,简历可观测体系

  • 负责自动驾驶数据平台的后端开发设计,平台主要功能如下

    • 面向自动驾驶场景的海量数据管理及检索
    • 数据自动挖掘及应用,数据标注及采集
    • 平台后端接口开发,服务部署和优化
    • 任务调度框架开发和维护,数据生产flow日常运维
    • 平台监控系统开发和维护
  • 负责寒武纪平台基于TensorFlow/Pytorch/MXNet/Caffe等框架的模型移植,部署,并对最终模型精度及性能进行调优

  • 结合寒武纪产品,参与目标检测跟踪,超分辨,图像切割,图像融合,图像增强等深度学习方案的设计与实现。

  • 图形处理目标检测跟踪等算法模型的部署,移植,精度与性能调优

  • 结合客户需求,为客户提供深度学习解决方案,并负责方案的落地

  • 了解AI,算法,数据应用等行业趋势,根据行业客户的战略,业务,产品及技术需求及公司内部产品布局,制定AI算法解决方案

  • 根据解决方案,负责人工智能相关算法实现以及在嵌入式平台上的开发应用

  • 负责公司AI方向的研究并提供解决方案,为研发中心所有项目组服务

  • 不断学习并运用新的知识提升产品竞争力

  • C++实现不同平台下AI算法的推理,根据平台特性以及任务特性对算法进行针对性优化,包括推理性能,计算精度等

  • 协同AI算法工程师完成模型转换,算子支持,模型量化,推理效果对齐等工作

  • 开发及维护底层AI推理的C++算法库

  • 负责进迭时空AI部署工具OnnxRuntime基于riscv平台的开发

  • 负责相关量化工具,模型转换工具的开发

  • 参与AI项目的研发,负责业务算法的C++实现和优化

  • 与算法工程师合作,将AI模型落地到产品端

  • 进行性能调优,确保AI应用的实时性和准确性

  • 编写和维护技术文档,确保代码的可读性和可维护性

boss直聘上相关任职要求

  • 熟悉一个或多个推理框架(例如TVM/TensorRT等)

  • 了解深度学习常用模型,有推理部署优化经验。

  • 熟练使用C++,2年以上开发经验

  • 熟悉CUDA等并行程序开发和性能调优的优先

  • 具备良好的C++/Python编程能力,熟悉Linux/Unix系统

  • 熟悉Docker及Kubernetes,有Kubernetes operator/crds开发经验者优先

  • 熟悉后端(Java/Golang/Python)开发语言,微服务框架等

  • 了解并行计算,网络通信,系统优化,集群硬件架构等HPC相关的知识

  • 了解argo,airflow,prefect等任务编排框架

  • 了解Kafka,prometheus,grafana等各种工具的使用

  • 有自动驾驶数据闭环,AI分布式系统研发,AI编译优化相关经验者优先

  • 熟练掌握C++/Python/Shell等编程语言,有扎实的编程基础和良好的编程风格,掌握Linux操作系统下CMake,GDB,Git等常用开发和调试工具

  • 掌握深度学习基础知识例如CNN,RNN,LSTM等,有深度学习相关工程经验,有目标检测跟踪,图像融合增强等相关工程经验优先,熟悉常见算法如EDSR,pixellink,MaskRCNN,Deeplab等

  • 熟悉TensorFlow/Pytorch/Mxnet/Caffe等深度学习框架一种或多种,具有计算框架任意核心模块开发,移植和优化经验优先

  • 了解异构计算机软硬件协同编程模型,熟悉OpenCV,Cuda编程优先

  • 具有两年以上深度学习算法或嵌入式应用开发工作经验

  • 熟练使用至少一种主流深度学习框架(例如pytorch,caffe,tensorflow等)

  • 有良好的编程风格,熟练掌握C/C++或Python编程语言,熟悉Linux操作系统

  • 熟悉ARM,DSP或者其他CPU,有嵌入式深度学习模型部署经验优先

  • 具有极好的协作能力和沟通能力,乐于主动沟通,具有敏锐的判断力,善于表达,适应短期出差

  • 2年以上人工智能软件开发经验,熟悉一种常用深度学习框架,对训练,精度/性能调优,推理部署有完整的实践经验

  • 具备扎实的Python编码基本功,熟悉windows/Linux开发环境,熟悉C++开发者尤佳

  • 至少对图片分类,OCR识别有过效果良好的岗位实践,对NLP方向(比如语义分析,问答系统)有深入研究者尤佳

  • 精通C/C++编程,有产品级开发经验

  • 熟悉深度学习相关基础算子,熟练掌握模型转换,量化,推理等基本流程

  • 有车机端,移动端AI部署经验者优先

  • 熟悉C/C++

  • 了解常见计算机体系结构不限于CPU,GPU等

  • AI领域工作经验2年以上,熟悉神经网络,机器视觉算法原理

  • 熟悉神经网络推理库,不限于NCNN,MNN,OnnxRuntime等

  • 具有模型量化,模型转换,图优化经验

  • 精通C++编程,熟悉STL标准库中常用数据结构,算法及容器类,熟悉C++11/14/17标准及其特性

  • 3年以上使用C++进行软件开发的经验,有AI或机器学习项目经验者优先

  • 熟悉多线程,并行计算和性能优化技术

  • 熟悉Linux开发环境和常用开发工具

  • 良好的算法和数据结构基础,具备优秀的问题解决能力

  • 有流媒体开发经验(ffmpeg, live555)

  • 有AI编程经验,熟悉CUDA或Ascend

概述

  • 程序开发过程要求我们做到两点:
    • 高效的数据描述
    • 步骤合理,可用程序实现的算法设计
  • 要做到第一点,必须具备数据结构领域的专门知识
  • 要做到第二点,必须具备算法设计领域的专门知识

第一张 C++回顾

第二章 程序性能分析

概述

  • 程序最重要的属性是正确性。一个程序,如果不能正确的实现算法,他就没有什么用处。
  • 我们用程序性能(program performance)来指一个程序对内存和时间的需求。要对数据结构和算法设计方法给予应用的评价,就必须能够计算程序性能。
  • 我们用操作数和执行步数来估计程序的运行时间。用符号法来分别描述程序在最好,最坏和平均情况下的运行时间。
  • 本章开发了很多应用程序,这些程序在以后的章节中是很有用处的,他们是
    • 数组元素的查找
    • 数组元素的排序:排列排序,选择排序,冒泡排序和插入排序
  • 基于霍纳法则的多项式计算
  • 矩阵加法,转置和乘法

什么是程序性能

  • 所谓程序性能(performance of a program)是指运行这个程序所需要的内存和时间的多少。我们用两种方法来确定一个程序的性能,一个是分析方法,另一个是实验方法。在性能分析(performance analysis)时,采用分析方法,而在性能测量(performance measurement)时,使用实验方法。

  • 所谓一个程序的空间复杂度(space complexity)是指该程序的运行所需内存的大小。我们对程序的空间复杂度感兴趣的主要原因如下

    • 如果一个程序要运行在一个多用户计算机系统中,那么我们需要指明该程序所需要内存的大小
    • 在任何一个计算机系统上运行程序,都需要知道是否有足够的内存可以用来运行该程序
    • 一个问题可能有若干个解决方案,他们对内存的需求各不相同
    • 利用空间复杂度,我们可以估算一个程序所能解决的问题最大可以是什么规模。
  • 所谓程序的时间复杂度(time complexity)是指运行程序所需要的时间。我们对程序的时间复杂度感兴趣的主要原因如下

    • 有些计算机需要用户提供程序运行时间的上限,一旦达到了这个上限,程序将被强制结束。
    • 正在开发的程序可能需要一个令人满意的实时响应。
    • 如果一个问题有多种解决方案,那么具体采用哪一种方案,主要根据这些方案的性能差异。对于各种方案的时间和空间性能,我们将采用加权测量方式进行评价。

空间复杂度

  • 程序所需要的空间主要由以下部分构成

    • 指令空间(instruction space), 是指编译之后的程序所需要的存储空间
    • 数据空间(data space), 是指所有常量和变量所需要的存储空间。它有两个部分构成
      • 常量和简单变量所需要的存储空间
      • 动态数组和动态类实例等动态对象所需要的空间
    • 环境栈空间(environment stack space),是用来保存暂停的函数和方法在恢复运行时所需要的信息。
  • 指令空间的数量取决于如下因素

    • 把程序转换成机器代码的编译器
    • 在编译时的编译器选项
    • 目标计算机
  • 在决定最终代码需要多少空间的时候,编译器是一个最重要的因素。

  • 即使采用相同的编译器,编译后的程序代码也可能不同。因为一个编译器可能具备优化选项。

  • 编译器的覆盖选项也可以显著的减少程序空间。在覆盖模式下,空间仅分配给当前正在执行的程序模块。调用一个新模块需要从磁盘或者其他设备中读取,新模块代码将覆盖原模块的代码。因此,程序所需要的空间便是最大模块所需要的空间,而不是所有模块所需要的空间之和。

  • 对各种数据类型,C++语言并没有指定他们的空间大小,只是大多数C++编译器有相应的空间分配。

  • 一个结构变量的空间大小是每个结构成员所需要的空间大小之和。类似的,一个数组的空间大小是数组的长度乘以一个数组元素的空间大小。

  • 当计算分配给一个数组的空间时,我们只关心分配给数组元素的空间。数组a的空间是100个double类型元素所占用的空间。若每个元素空间是8字节,则数组a的空间是800字节。数组maze有rows*cols个int类型的元素,占用的空间是4 * rows * cols字节。

  • 在开始性能分析时,人们通常会忽略环境栈所需要的空间,因为他们不理解函数(特别是递归函数)是如何被调用的以及在函数调用结束时会发生什么。每当一个函数被调用时,下面的数据将被保存在环境栈中

    • 返回地址
    • 正在调用的函数的所有局部变量的值以及形式参数的值(仅对递归函数而言)
  • 值得注意的是,有些编译器,不论对递归函数还是非递归函数,在函数调用时,都会保留局部变量和形参的值,而有些编译器仅对递归函数才会如此。因此实际使用的编译器将影响环境栈所需空间的大小。

  • 程序要处理的问题实例都有一些特征,这些特征都包含着可以决定程序空间大小的因素(例如,输入和输出的数量或相关数的大小)。例如,对n个元素排序的程序,它所需要的空间大小是n的函数,n为其实例特征;将两个n x n矩阵累加的程序,n为其实例特征;把两个m x n矩阵相加的程序,m和n为其实例特征。

  • 相对来说,指令空间的大小受实例特征的影响不大。

  • 可以把一个程序所需要的空间分为两部分

    • 固定部分。它独立于实例特征。这一部分通常包括指令空间(即代码空间),简单变量空间和常量空间等
    • 可变部分。它由动态分配空间构成和递归栈空间构成。前者在某种程度上依赖实例特征,而后者主要依赖实例特征。
  • 任意程序P所需要的空间可以表示为: c + Sp(实例特征)

    • 其中c是一个常量,表示空间需要的固定部分,Sp表示空间需求的可变部分。
  • 在分析一个程序的空间复杂度时,我们将集中计算Sp对任意给定的问题,我们首先要确定哪些实例特征可以用来估算空间需求。选择实例特征是一个很具体的问题,我们需要求助于实际的例子来说明各种可能的情况。一般来说,我们的选择仅限于程序输入和输出的规模。有时我们也会对数据项之间的关系进行复杂的估算。

时间复杂度

  • 用分析方法确定一个程序的运行时间是复杂的,因此只能估算运行时间。而且有两个比较容易控制的方法

    • 找出一个或多个关键操作,确定他们的执行时间
    • 确定程序总的步数
  • 估算一个程序或函数的时间复杂度,一种方法是选择一种或者多种关键操作,例如加,乘,比较等,然后 确定每一种操作的执行次数。使用这种方法成功与否取决于是否能够找到耗时最大的操作。

第三章 渐近记法

第四章 性能测量

引言

  • 性能测量(performance measurement),关注的是一个程序实际需要的空间和时间。
  • C++函数 clock() 来测量时间,它用 滴答 数来计时。在头文件 time.h 中定义了常数 CLOCK_PER_SEC,它记录每秒流逝的 滴答 数,并转换成秒。CLOCK_PER_SEC=1000,滴答一次等于一毫秒。

第五章 线性表-数组描述

  • 从本章开始研究数据结构,一直到第16章为止。第5章和第6章集中研究线性表,但主要是介绍数据的描述方法,即数据在计算机内存和磁盘上的存储方式。在随后的章节中,我们研究其他常用的数据结构描述方法。这些数据结构有矩阵,栈,队列,字典,优先级队列,竞赛树,搜索树和图。

  • C++程序常用的数据描述方法是数组描述和链式描述。线性表可以用来说明这两种方法。

  • STL容器(vector和list)大致相当于线性表描述方法和链式描述方法。STL的类还有很多其他的方法。在建立线性表的数组描述和链式描述中,我们使用的函数名和签名与STL代码所使用的相同。

  • 数组描述方法将元素存储在一个数组中,用一个数学公式来确定每个元素存储的位置,即在数组中的索引。这是最简单的一种存储方式,所有元素依次存储在一片连续的存储空间中,这就是通常所说的顺序表。

  • 本章引用的数据结构的概念如下

    • 抽象数据类型和相应的C++抽象类
    • 线性表
    • 变长数组和数组容量倍增
    • 数组描述
    • 数据结构迭代器
  • 本章新增的C++概念

    • 抽象类
    • 迭代器

数据对象和数据结构

  • 数据对象(data object)是一组实例或者值,例如

    1
    2
    boolean = {false, true};
    digit = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
  • boolean, digit都是数据对象,true和false是boolean的实例,而0,1—9是digit的实例。

  • 数据对象的一个实例,要么是一个不可再分的原子,要么是由另一个数据对象的实例作为成员复合而成。对后一种情形,用元素(element)来表示这些成员。

  • 数据对象的实例以及构成实例的元素通常都有某种相关性。例如,自然数0是最小的自然数,1是仅比0大的自然数。在串good中,g是第一个字母,o是第二个和第三个字母,而d是最后一个字母。

  • 数据结构(data structure)是一个数据对象,同时这个对象的实例以及构成实例的元素都存在着联系,而且这些联系由相关的函数来规定。

  • 研究数据结构,关心的是数据对象(实际上是实例)的描述以及相关函数的具体实现。数据对象描述的好,函数的实现就会高效。

线性表数据结构

  • 线性表(linear list)也称有序表(ordered list),它的每一个实例都是元素的一个有序集合。

  • 一个线性表可以用一个抽象数据类型(abstract data type, ADT)来说明,既说明它的实例,也说明对他的操作。抽象数据类型的说明独立于任何程序语言的描述。所有对抽象数据类型的语言描述必须满足抽象数据类型的说明,抽象数据类型的说明保证了程序语言描述的有效性。另外,所有满足抽象数据类型说明的语言描述,都可以在应用中替换使用。

数组描述

  • 在数组描述(array representation)中,用数组来存储线性表的元素。虽然可以用一个数组存储若干个线性表的实例,但是用不同数组存储每个实例更加容易一些。我们可以用一个数学公式来确定一个线性表的元素在数组中的位置。

  • 假定使用一个一维数组element来存储线性表的元素。数组element的位置有 element[0] — element[arrayLength - 1],其中arrayLength是数组长度或者容量。数组的每一个位置都可以存储线性表的一个元素。我们需要一个映射,使线性表的一个元素对应数组的一个位置。线性表的第0个元素在数组的什么位置?线性表的最后一个元素在数组的什么位置?这种映射可以用下面的一个公式来表示

    • location(i) = i
  • 一维数组a,线性表元素存储在a[0:n-1]中,要增加或者减少这个数组的长度,首先要建立一个具有新长度的数组,然后把数组a的元素复制到这个新数组,最后改变数组a的值,使它能够引用到新数组。

  • 当数组满而需要加大数组长度时,数组长度常常是要加倍的。这个过程称为数组倍增(array doubling)。数组倍增的时间,从渐近意义上考量,不会大于元素插入的总时间。

  • 为什么数组长度不是增加1或2,而是要加倍呢?数组长度每次增加1或2,虽然不影响插入操作的最坏时间复杂度,但是影响连续插入时的渐近时间复杂度。

  • 定理:如果我们总是按一个乘法因子来增加数组长度,那么实施一系列线性表的操作所需要的时间与不用改变数组长度时相比,至多增加一个常数因子。

C++ 迭代器

  • 一个迭代器(iterator)是一个指针,指向对象的一个元素(例如,一个指向数组元素的指针)。顾名思义,一个迭代器可以用来逐个访问对象的所有元素。

  • 迭代器是编写C++通用算法的基础概念。STL的copy函数便是用来复制任何具有迭代器的对象的元素。

  • 为了简化迭代器的开发和基于迭代器的通用算法的分类,C++的STL定义了5中迭代器:

    • 输入,输出,向前,双向和随机访问
  • 所有迭代器都具备操作符 == , != 和解引用操作符 *

  • 另外,输入迭代器还提供了对其指向元素的只读操作以及前++和后++操作符。输出迭代器提供了对其指向元素的写操作和++操作符。向前迭代器具有++操作符,而双向迭代器既具有++操作符也具有–操作符。随机访问迭代器是最一般的迭代器,它既可以随意的实现跳跃移动,也可以通过指针算术运算来实现跳跃移动。

vector的描述

  • STL提供了一个基于数组的类 vector。

第六章 链式描述

  • 用数组描述线性表是很自然的,因此你可能以为没有其他的描述方法了。
  • 在链式描述中,线性表的元素在内存中的存储位置是随即的。每个元素都有一个明确的指针或链(指针和链是一个意思)指向线性表的下一个元素的位置(即地址)
  • 在基于数组的描述中,元素的地质是由数学公式决定的。而在链式描述中,元素的地址是随即分布的。
  • STL的容器类list使用带有头节点的双向循环链表来描述实例。它的方法与vector的方法具有相同的签名和操作。因此,他的erase和insert的签名和抽象类型linearList的要求不同,然而和vector一样,它可以用来设计从抽象类linearList的方法具有相同的签名和操作。

单向链表

  • 描述

    • 在链式描述中,数据对象实例的每一个元素都用一个单元或节点来描述。节点不必是数组成员,因此不是用公式来确定元素的位置。取而代之的是,每一个节点都明确包含另一个相关节点的位置信息,这个信息称为链(link)或指针(pointer)
    • 在对这个线性表的一个可能的链式描述中,每个元素都在一个单独的节点中描述,每一个节点都有一个链域,它的值是线性表的下一个元素的位置,即地址。
    • 一般来说,为了找到索引为theIndex的元素,需要从firstNode开始,跟踪theIndex个指针才能找到。
  • 每一个节点只有一个链,这种结构称为单向链表(singly linked list)。链表从左到右,每一个节点(最后一个节点除外)都链接着下一个节点,最后一个节点的链阈值为NULL。这样的结构也称为链条(chain)

  • 结构chainNode

    • 为了用链表描述线性表,我们要定义一个结构chainNode和一个类chain。
  • 类chain

    • 类chain用单向链表实现了线性表,其中最后一个节点的指针域为NULL,即它用单向链接的一组节点实现线性表。

循环链表和头节点

  • 两条措施,可以使链表的应用代码简洁和高效
    • 把线性表描述成一个单向循环链表(singly linked circular list)(简称循环链表),而不是单向链表
    • 在链表的前面增加一个节点,称为头节点(header node)。只要将单向链表的尾节点与头节点链接起来,单向链表就成为循环链表。

双向链表

  • 对于线性表的大多数应用来说,采用链表和/或循环链表已经足够了。然而,对于有些应用,如果每个元素节点既有一个指向后继的指针,又有一个指向前驱的指针,就会更加方便。
  • 双向链表(doubly linked list)便是这样一个有序的节点序列,其中每个节点都有两个指针nest和previous。
    • next指针指向右边节点(如果存在)
    • previous指针指向左边节点(如果存在)

第七章 数组和矩阵

  • 在实际应用中,数据通常以表的形式出现。尽管用数组来描述表是最自然的方式,但为了减少程序所需的时间和空间,经常采用自定义的描述方式。
  • 本章首先检查了多维数组的行主描述方式和列主描述方式。这些描述方式把多维数组映射成一维数组。
  • 矩阵经常用二维数组来表示。然后,矩阵的索引通过从1开始,而C++的二维数组是从0开始。矩阵的操作有加法,减法和转置,但是C++的二维数组不支持这些操作。因此我们开发了类matrix,它与矩阵的关系密切
  • 我们还要考察具有特殊结构的矩阵,例如对角矩阵,三对角矩阵,三角矩阵和对陈矩阵。关于这些矩阵的描述方法,自定义数组与二维数组相比,不仅大大减少了存储空间安,也减少了大多数矩阵操作的运行时间。

数组

  • 抽象数据类型
  • 一个数组的每一个实例都是形如(索引,值)的数对集合,其中任意两个数对的索引(index)都不相同。有关数组的操作如下
    • 取值: 对一个给定的索引,取对应数对中的值
    • 存值: 把一个新数对加到数对集合中。如果已存在一个索引相同的数对,就用新数对覆盖
  • 这两个操作定义了抽象数据类型array

行主映射和列主映射

  • 数组的应用需要我们把数组元素序列化,即按一维顺序排列。例如,数组元素只能一次输出或输入一个。因此,我们必须确定一个输入或输出的顺序。
  • 从第一行开始,依次对每一行的索引从左至右连续编号。它把二维数组的索引映射为 [0, n-1]中的数,这种映射方式称为行主映射(row major mapping)。索引对应的数称为行主次序
  • 另一种映射方式,称为列主映射(column major mapping)。在列主映射中,对索引的编号从最左列开始,依次对每一列的索引从上到下连续编号。

矩阵

  • 一个m * n的矩阵(matrix)是一个m行,n列的表,m和n是矩阵的维数(dimension)

  • 矩阵通常用来组织数据。

  • 矩阵最常见的操作是矩阵转置,矩阵相加,矩阵相乘。

  • 两个矩阵仅当维数相同时(即它们的行数和列数都分别相等)才可以相加。两个m * n的矩阵A和B相加之后是一个m * n的矩阵C

  • 一个m * n的矩阵A和一个q * p的矩阵B,只有当A的列数等于B的行数(即n=q)时,才可以相乘AB。AB的结果是一个m * p的矩阵C

  • 特殊矩阵

    • 方阵(square matrix)是行数和列数相同的矩阵。一些常用的特殊方阵如下
    • 对角矩阵(diagonal)
    • 三对角矩阵(tridiagonal)
    • 下三角矩阵(lower triangular)
    • 上三角矩阵(upper triangular)
    • 对陈矩阵(symmetric)
  • 一个m * n的矩阵,如果大多数元素都是0,则称为稀疏矩阵(spare matrix)。一个矩阵如果不是稀疏的,就成为稠密矩阵(dense matrix)

第八章 栈

  • 栈和队列很可能是应用频率最高的数据结构。在第五章和第六章曾经广泛研究了线性表和有序表数据结构,而栈和队列是它们的限制版。

  • 栈和队列的应用广泛,以至于C++的标准类模板库STL都提供了用数组实现的栈和队列。

  • 把线性表的插入和删除操作限制在同一端进行,就得到栈数据结构。因此,栈是一个后进先出(last-in-first-out, FIFO)的数据结构。因为栈是一种特殊的线性表,所以从相应的线性表类派生出栈类是很自然的事情。

定义和应用

  • 栈(stack)是一种特殊的线性表,其插入(也称入栈或压栈)和删除(也称出栈或弹栈)操作都在表的同一端进行。这一端称为栈顶(top),另一端称为栈底(bottom)

  • 计算机是如何执行递归函数?

    • 使用递归工作栈(recursion stack)。当一个函数被调用时,一个返回地址(即被调函数一旦执行完,接下去要执行的程序指令的地址)和被调函数的局部变量和形参的值都要存储在递归工作栈中。当执行一次返回时,被调函数的局部变量和形参的值被恢复为调用之前的值(这些值存储在递归工作栈的顶部),而且程序从返回地址处继续执行,这个返回地址也存储在递归工作栈的顶部。
  • 抽象数据类型

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    template<class T>
    class stack
    {
    public:
    virtual ~stack();
    virtual bool empty() const = 0; // 返回true,当且仅当栈为空
    virtual int size() const = 0; // 返回栈中元素个数
    virtual T& top() = 0; // 返回栈顶元素的引用
    virtual void pop() = 0; // 删除栈顶元素
    virtual void push(const T& theElement) = 0; // 将元素theElement压入栈顶
    };

SD、HD、FHD、UHD、FUHD

  • SD(Standard Definition),标清

    • 480p
    • 576p
  • HD(High Definition),高清

    • 720p
  • FHD(Full High Definition),全高清

    • 1080p
  • UHD(Ultra High Definition),超高清,4k UHD

    • 4k
  • FUHD(Full Ultra High Definition),8k超高清,8k UHD

    • 8k

return

  • return,它的主要任务是在函数结束时将控制权交还给调用他的代码。

  • return不仅可以返回基本数据类型,还可以返回复杂的数据结构,例如对象和数组。

  • 合理使用return,可以使代码更加简洁明了。在进行条件判断时,我们可以在条件满足时立即返回,避免不必要的嵌套。

exit

  • exit,它不仅仅是结束当前的函数,而是终止整个程序的执行,立即返回给操作系统。

  • 应急处理和日志记录。在一些关键任务中,遇到不可恢复的错误时,我们可以使用exit来终止程序,并在退出前记录错误日志,以便后续分析。

  • 注册清理函数。使用atexit函数,我们可以在程序终止时执行一些清理工作,例如释放资源或者保存程序状态

return 与 exit的对比和选择

  • 作用范围

    • return 仅终止当前函数,返回到调用者
    • exit 终止整个程序的执行
  • 清理操作

    • return 执行当前函数的清理工作
    • exit 执行全局和静态对象的析沟函数,并调用通过atexit注册的函数
  • 使用场景

    • return 用于函数结束后返回结果
    • exit 用于遇到不可恢复错误或者需要立即终止程序的情况
  • 综合考虑

    • 在实际开发中,我们应该优先使用return进行正常的函数返回,以保持代码的可读性和可维护性。而exit应仅在特殊情况下使用,确保在调用前做好必要的清理工作和日志记录。

计算机基础到底是那些基础

  • 计算机基础主要包括
    • 硬件基础,
    • 软件基础,
    • 操作系统基础,
    • 网络基础,
    • 编程基础,
    • 算法和数据结构。

硬件基础

  • 硬件基础指的是计算机中各个物理组成部分的知识,包括CPU,内存,硬盘,主板等工作原理和相互关系。在这些构成中,CPU的作用尤为重要,因为它是整个计算机系统的大脑,负责执行指令和处理数据。
  • 计算机的硬件基础是整个计算机科学的基石。硬件包括中央处理器(CPU),内存,输入输出设备,存储设备和其他辅助设备。这些组件的配合使得计算机能够执行复杂的任务。例如CPU的架构,工作频率和核心数直接决定了计算机的处理能力。
  • 对于硬件,更深入的了解会涉及到微架构,指令集以及如何与外围设备通信。硬件的性能优化,故障排除和维护,也是计算机基础知识的重要部分。

软件基础

  • 软件基础涉及操作系统使用,办公软件的操作,以及基本的系统配置技能。熟悉常用的软件应用,例如文字处理,表格计算,演示制作和图像编辑,是提高工作效率的关键。
  • 软件基础的高级知识还包括理解不同软件背后的基本原理,例如数据库管理系统(DBMS)的工作模式,网络服务器软件的配置等等。

操作系统基础

  • 操作系统(OS)是管理计算机硬件与软件资源的软件,它是用户与计算机交互的媒介。熟悉至少一种操作系统(例如 Windows, macOS, Linux)的使用,能为个人使用计算机打下良好的基础
  • 进一步的操作系统知识包括了了解内部结构,各类操作系统的原理和设计,进程管理,内存管理以及文件系统等等。

网络基础

  • 网络基础包含对计算机网络的认识和理解。它涉及到网络的结构,工作原理,以及网络协议(例如TCP/IP)的运作方式。了解如何在网络上进行数据的传输和共享,以及网络安全的基础知识,是网络基础的环节。
  • 在网络方面,还需要掌握不同的网络拓扑,路由器,交换机的功能以及网络的构建和维护。

编程基础

  • 编程是计算机科学中不可或缺的技能之一。从理解变量,控制结构,数据类型,到能够用一种或者多种编程语言来解决问题,都属于编程基础。
  • 随着技能的提升,编程基础将扩展到软件开发的最佳实践,代码的结构化与模块化,以及版本控制的使用等。

算法和数据结构

  • 算法和数据结构是编程的核心。数据结构如数组,链表,栈,队列,树和图等,是组织和存储数据的方式。算法则涉及解决问题的具体步骤和方法。掌握基本的算法和数据结构,可以提高解决问题的效率和程序的性能。
  • 高级算法包括排序算法,搜索算法以及算法复杂度分析等

小结

  • 计算机基础涵盖的范围广泛,而且随着技术的基本,这些基础知识在不断更新。个人要根据自身的职业规划和兴趣点来选择重点学习的方向。
  • 总之,对于从事任何计算机相关工作的人来说,牢固的计算机基础知识是成功的必要条件。

算法简介

  • 在职业发展上,算法是衡量程序员能力的重要标准之一,对于求职特别是在大型科技公司中占有重要的位置。算法不仅仅是解决问题的方法,它还是程序员逻辑思维和编程能力的体现。

解决问题的能力

  • 算法作为解决问题的一种方法和工具,对提升程序员解决复杂问题的能力有着至关重要的作用。
  • 掌握算法可以帮助程序员更有效的分析问题,设计解决方案,并能在实现过程中选择最优的方法。
  • 没有算法基础的程序员,在面对数据处理,性能优化等问题时可能会感到无从下手,这不仅会降低开发效率,还可能导致项目难以达到预期的性能要求

算法的学习路径

  • 对于程序员来说,学习算法是一个连续且长期的过程。一个合理的学习路径是从基础的数据结构开始,例如数组,链表,树,图等,逐步深入到排序,搜索,图算法,动态规划等更高级的内容。
  • 实践是检验学习成果的唯一标准,通过参与实际项目,解决真实问题,参加算法竞赛等方式,可以不断提升自己的算法能力。

如何提高自己的代码/工程能力

  • 代码能力是计算机学生必须具备的能力,但是简单的说 代码能力 是不够的。项目需求,程序设计语言,开发框架多种多样,能力必须有针对性的培养。
  • 代码能力粗略的分为两类:
    • 写出(高效)解决单个问题的代码的能力,即算法能力,
    • 写出整体高性能可维护 优雅 的代码的能力,即工程能力
  • 这两者都可以继续分类。

基础工具和能力

算法题

  • 关于是否大量刷算法题历来颇有争议。有人说数据结构很实用,说算法能够锻炼思维和在压力环境下解决问题的能力。但是也有人说算法题需要大量重复训练单个知识点,学到的很多算法和实际应用不符。

  • 不管是申请实验室还是找工作,算法题是笔试中的常客,一定程度上是选拔人才的标准。

  • 一般需要掌握一下内容

    • 基础: 程序控制语句,数组,指针,函数
    • 基础算法:排序,穷举,贪心
    • 数据结构:链表,二叉树,图
    • 图论:搜索,最短路,连通性,连通分量
    • 询问:区间最值,单点修改,区间维护,区间修改,区间维护,
    • 动态规划和记忆化搜索
    • 数论(密码学)
    • 数值计算
    • 计算几何

学习开源代码

  • 优秀开源代码是很好的学习资源,在代码里可以学到从设计到每个部分的具体实现,如果能够读完一套相关领域优秀框架的源码,作一份笔记,将对自己的能力有很大的帮助。

培养读官方文档和源码的能力

  • 不论是什么项目,在深入学习一段时间之后,就会发现:百度一下能找到的东西太有限了。很多知识都存在于官方文档中,甚至为了明白到到底发生着什么需要直接进入所引用模块的源码。
  • 通过阅读文档/源码,可以学习如何严谨的设计程序的结构,以提高程序的各项性能。

简介

  • CPU(Central Processing Unit),即中央处理器
  • GPU(Graphics Processing Unit), 即图形处理器
  • TPU(Tensor Processing Unit), 即张量处理器(谷歌)
  • NPU(Neual network Processing Unit),即神经网络处理器

概括三者的区别

  • CPU虽然有多核,但一般也就几个,每个核都有足够大的缓存和足够多的数字和逻辑运算单元,需要很强的通用性来处理各种不同的数据类型,同时又要逻辑判断又会引入大量的分支跳转和中断的处理,并辅助有很多加速分支判断甚至更复杂的逻辑判断的硬件;
  • GPU的核数远超CPU,被称为众核(NVIDIA Fermi有512个核)。每个核拥有的缓存大小相对小,数字逻辑运算单元也少而简单(GPU初始时在浮点计算上一直弱于CPU),面对的则是类型高度统一的、相互无依赖的大规模数据和不需要被打断的纯净的计算环境
  • TPU是一款为机器学习而定制的芯片,经过了专门深度机器学习方面的训练,它有更高效能(每瓦计算能力)。大致上,相对于现在的处理器有7年的领先优势,宽容度更高,每秒在芯片中可以挤出更多的操作时间,使用更复杂和强大的机器学习模型,将之更快的部署,用户也会更加迅速地获得更智能的结果
  • 所谓NPU, 即神经网络处理器,用电路模拟人类的神经元和突触结构

CPU

  • CPU(CentralProcessing Unit)中央处理器,是一块超大规模的集成电路,主要逻辑架构包括控制单元Control,运算单元ALU和高速缓冲存储器(Cache)及实现它们之间联系的数据(Data)、控制及状态的总线(Bus)。简单说,就是计算单元、控制单元和存储单元。
  • CPU遵循的是冯·诺依曼架构,其核心是存储程序/数据、串行顺序执行。因此CPU的架构中需要大量的空间去放置存储单元(Cache)和控制单元(Control),相比之下计算单元(ALU)只占据了很小的一部分,所以CPU在进行大规模并行计算方面受到限制,相对而言更擅长于处理逻辑控制。
  • CPU无法做到大量数据并行计算的能力,但GPU可以。

GPU

  • GPU(GraphicsProcessing Unit),即图形处理器,是一种由大量运算单元组成的大规模并行计算架构,早先由CPU中分出来专门用于处理图像并行计算数据,专为同时处理多重并行计算任务而设计。
  • GPU中也包含基本的计算单元、控制单元和存储单元,但GPU的架构与CPU有很大不同。
  • 与CPU相比,CPU芯片空间的不到20%是ALU,而GPU芯片空间的80%以上是ALU。即GPU拥有更多的ALU用于数据并行处理。
  • GPU具有如下特点:
    • 多线程,提供了多核并行计算的基础结构,且核心数非常多,可以支撑大量数据的并行计算,处理神经网络数据远远高效于CPU。
    • 拥有更高的访存速度。
    • 更高的浮点运算能力。
  • 因此,GPU比CPU更适合深度学习中的大量训练数据、大量矩阵、卷积运算。
  • GPU虽然在并行计算能力上尽显优势,但并不能单独工作,需要CPU的协同处理,对于神经网络模型的构建和数据流的传递还是在CPU上进行。
  • 但是GPU也有天生缺陷,那就是功耗高,体积大,价格贵。
  • 性能越高的GPU体积越大,功耗越高,价格也昂贵,对于一些小型设备、移动设备来说将无法使用。
  • 因此,一种体积小、功耗低、计算性能高、计算效率高的ASIC专用芯片NPU诞生了。

NPU

  • NPU (NeuralNetworks Process Units)神经网络处理单元。其针对于矩阵运算进行了专门的优化设计,解决了传统芯片在神经网络运算时效率低下的问题。NPU工作原理是在电路层模拟人类神经元和突触,并且用深度学习指令集直接处理大规模的神经元和突触,一条指令完成一组神经元的处理。相比于CPU和GPU,NPU通过突出权重实现存储和计算一体化,从而提高运行效率。
  • 神经网络处理器(NPU)采用“数据驱动并行计算”的架构,特别擅长处理视频、图像类的海量多媒体数据。NPU处理器专门为物联网人工智能而设计,用于加速神经网络的运算,解决传统芯片在神经网络运算时效率低下的问题。
  • NPU是模仿生物神经网络而构建的,CPU、GPU处理器需要用数千条指令完成的神经元处理,NPU只要一条或几条就能完成,因此在深度学习的处理效率方面优势明显。
  • 神经网络中存储和处理是一体化的,都是通过突触权重来体现。 冯·诺伊曼结构中,存储和处理是分离的,分别由存储器和运算器来实现,二者之间存在巨大的差异。当用现有的基于冯·诺伊曼结构的经典计算机(如X86处理器和英伟达GPU)来跑神经网络应用时,就不可避免地受到存储和处理分离式结构的制约,因而影响效率。这也就是专门针对人工智能的专业芯片能够对传统芯片有一定先天优势的原因之一。

CPU 如何辅助NPU实现加速

  • NPU与GPU加速不同,主要体现为每层神经元计算结果不用输出到主内存,而是按照神经网络的连接传递到下层神经元继续计算,因此其在运算性能和功耗上都有很大的提升。
  • CPU将编译好的神经网络模型文件和权重文件交由专用芯片加载,完成硬件编程

关于RK1126平台项目的小思考

  • 在瑞芯微的1126芯片上有NPU,我接触的有四种检测模型:安全帽检测,区域入侵检测,反光衣检测,烟火检测。软件具体运行流程为:底层视频结构化引擎运行,后端服务运行,将模型配置文件发送给引擎,加载检测模型,将流媒体地址发送给引擎,引擎将视频流解码为一帧帧图片,将图片处理为检测模型需要的格式,这一步一般称为预处理,由检测模型返回检测结果,引擎处理成结构化数据。

  • 检测模型就是神经网络模型文件和权重文件。底层引擎通过平台提供的接口,将检测模型加载到NPU上。