Linux Gstreamer深度解析之gst_audio_ring_buffer_prepare_read调用流程与实战(七十四)

简介: CSDN博客专家、《Android系统多媒体进阶实战》作者

博主新书推荐:《Android系统多媒体进阶实战》🚀

Android Audio工程师专栏地址:Audio工程师进阶系列【原创干货持续更新中...... 】🚀

Android多媒体专栏地址:多媒体系统工程师系列【原创干货持续更新中...... 】🚀

专题一 二:AAOS车载系统+AOSP14系统攻城狮入门视频实战课 🚀

专题三:Android14 Binder之HIDL与AIDL通信实战课 🚀

专题四:Android15快速自定义与集成音效实战课 🚀

专题五:Android15音频策略实战课 🚀

专题六:Android15音频性能实战课(无声/杂音/断音/爆音实战案例) 🚀

人生格言: 人生从来没有捷径,只有行动才是治疗恐惧和懒惰的唯一良药.
更多原创,欢迎关注:Android系统攻城狮

🍉🍉🍉文章目录🍉🍉🍉

🌻1.前言

本篇目的:理解gst_audio_ring_buffer_prepare_read()如何根据RingBuffer当前位置返回分段索引、数据指针和长度,并通过实战观察回调填充与分段推进。

GstAudioRingBuffer把音频数据组织成多个固定大小的分段。设备子类需要取得当前分段的地址和长度,再把数据交给设备读写。gst_audio_ring_buffer_prepare_read()负责定位分段并返回这组信息;它本身不搬运音频数据,也不推进分段计数。
#mermaid-svg-l4aPiotowsXukbA1{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-l4aPiotowsXukbA1 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-l4aPiotowsXukbA1 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-l4aPiotowsXukbA1 .error-icon{fill:#552222;}#mermaid-svg-l4aPiotowsXukbA1 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-l4aPiotowsXukbA1 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-l4aPiotowsXukbA1 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-l4aPiotowsXukbA1 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-l4aPiotowsXukbA1 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-l4aPiotowsXukbA1 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-l4aPiotowsXukbA1 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-l4aPiotowsXukbA1 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-l4aPiotowsXukbA1 .marker.cross{stroke:#333333;}#mermaid-svg-l4aPiotowsXukbA1 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-l4aPiotowsXukbA1 p{margin:0;}#mermaid-svg-l4aPiotowsXukbA1 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-l4aPiotowsXukbA1 .cluster-label text{fill:#333;}#mermaid-svg-l4aPiotowsXukbA1 .cluster-label span{color:#333;}#mermaid-svg-l4aPiotowsXukbA1 .cluster-label span p{background-color:transparent;}#mermaid-svg-l4aPiotowsXukbA1 .label text,#mermaid-svg-l4aPiotowsXukbA1 span{fill:#333;color:#333;}#mermaid-svg-l4aPiotowsXukbA1 .node rect,#mermaid-svg-l4aPiotowsXukbA1 .node circle,#mermaid-svg-l4aPiotowsXukbA1 .node ellipse,#mermaid-svg-l4aPiotowsXukbA1 .node polygon,#mermaid-svg-l4aPiotowsXukbA1 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-l4aPiotowsXukbA1 .rough-node .label text,#mermaid-svg-l4aPiotowsXukbA1 .node .label text,#mermaid-svg-l4aPiotowsXukbA1 .image-shape .label,#mermaid-svg-l4aPiotowsXukbA1 .icon-shape .label{text-anchor:middle;}#mermaid-svg-l4aPiotowsXukbA1 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-l4aPiotowsXukbA1 .rough-node .label,#mermaid-svg-l4aPiotowsXukbA1 .node .label,#mermaid-svg-l4aPiotowsXukbA1 .image-shape .label,#mermaid-svg-l4aPiotowsXukbA1 .icon-shape .label{text-align:center;}#mermaid-svg-l4aPiotowsXukbA1 .node.clickable{cursor:pointer;}#mermaid-svg-l4aPiotowsXukbA1 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-l4aPiotowsXukbA1 .arrowheadPath{fill:#333333;}#mermaid-svg-l4aPiotowsXukbA1 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-l4aPiotowsXukbA1 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-l4aPiotowsXukbA1 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-l4aPiotowsXukbA1 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-l4aPiotowsXukbA1 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-l4aPiotowsXukbA1 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-l4aPiotowsXukbA1 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-l4aPiotowsXukbA1 .cluster text{fill:#333;}#mermaid-svg-l4aPiotowsXukbA1 .cluster span{color:#333;}#mermaid-svg-l4aPiotowsXukbA1 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-l4aPiotowsXukbA1 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-l4aPiotowsXukbA1 rect.text{fill:none;stroke-width:0;}#mermaid-svg-l4aPiotowsXukbA1 .icon-shape,#mermaid-svg-l4aPiotowsXukbA1 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-l4aPiotowsXukbA1 .icon-shape p,#mermaid-svg-l4aPiotowsXukbA1 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-l4aPiotowsXukbA1 .icon-shape .label rect,#mermaid-svg-l4aPiotowsXukbA1 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-l4aPiotowsXukbA1 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-l4aPiotowsXukbA1 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-l4aPiotowsXukbA1 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 已采集的RingBuffer
gst_audio_ring_buffer_prepare_read
定位当前分段
返回分段地址和长度
设备子类处理数据

🌻2.应用场景和用法

2.1音频设备如何取得当前分段

gst_audio_ring_buffer_prepare_read()主要供音频设备子类使用。播放端取得RingBuffer中的一段音频,再交给设备写入;录音端取得目标分段,再让设备把采集到的数据写入该区域。

调用者通常先确认RingBuffer已采集并拥有有效内存,再调用该接口取得当前分段。设备处理完一个分段后,调用gst_audio_ring_buffer_advance()推进完成计数。下一次调用会据此定位新的分段。

如果RingBuffer没有设置填充回调,函数要求状态已经是GST_AUDIO_RING_BUFFER_STATE_STARTED。设置了回调时,函数会调用回调填充本次返回的分段内存。

2.2参数、返回值与相邻接口

函数原型如下:

c 复制代码
gboolean
gst_audio_ring_buffer_prepare_read (GstAudioRingBuffer *buf,
                                    gint *segment,
                                    guint8 **readptr,
                                    gint *len);
参数 含义
buf 已采集并包含有效分段内存的RingBuffer
segment 输出参数,返回当前分段编号
readptr 输出参数,返回当前分段的内存地址
len 输出参数,返回当前分段的字节数

返回TRUE表示分段信息已写入输出参数。返回FALSE通常表示没有填充回调且RingBuffer尚未启动。无论是否设置回调,buf、segment、readptr和len都必须有效,RingBuffer内存也必须存在。

readptr指向RingBuffer内部内存,调用者不能释放它。函数返回的len是字节数,不是Frame数。相邻接口的分工如下:

接口 职责
gst_audio_ring_buffer_set_callback() 设置分段填充回调
gst_audio_ring_buffer_prepare_read() 定位当前分段并返回内存地址和长度
gst_audio_ring_buffer_advance() 记录设备已处理的分段数
gst_audio_ring_buffer_read() 从RingBuffer读取指定样本位置的数据

🌻3.调用流程剖析

3.1根据分段计数计算地址

函数先检查buf是否为有效RingBuffer。如果没有设置callback,它还会检查状态是否为GST_AUDIO_RING_BUFFER_STATE_STARTED;未启动时直接返回FALSE。

通过检查后,函数读取原子变量segdone,用它和spec.segtotal计算当前分段:

c 复制代码
*segment = segdone % buf->spec.segtotal;
*len = buf->spec.segsize;
*readptr = buf->memory + *segment * *len;

segdone表示已经完成处理的分段累计数。取模后得到环形缓冲区中的当前分段编号。分段地址则由RingBuffer起始地址、分段编号和每段字节数共同确定。
#mermaid-svg-Bj5roYXgfBwNWouG{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-Bj5roYXgfBwNWouG .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Bj5roYXgfBwNWouG .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Bj5roYXgfBwNWouG .error-icon{fill:#552222;}#mermaid-svg-Bj5roYXgfBwNWouG .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Bj5roYXgfBwNWouG .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Bj5roYXgfBwNWouG .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Bj5roYXgfBwNWouG .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Bj5roYXgfBwNWouG .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Bj5roYXgfBwNWouG .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Bj5roYXgfBwNWouG .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Bj5roYXgfBwNWouG .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Bj5roYXgfBwNWouG .marker.cross{stroke:#333333;}#mermaid-svg-Bj5roYXgfBwNWouG svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Bj5roYXgfBwNWouG p{margin:0;}#mermaid-svg-Bj5roYXgfBwNWouG .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-Bj5roYXgfBwNWouG .cluster-label text{fill:#333;}#mermaid-svg-Bj5roYXgfBwNWouG .cluster-label span{color:#333;}#mermaid-svg-Bj5roYXgfBwNWouG .cluster-label span p{background-color:transparent;}#mermaid-svg-Bj5roYXgfBwNWouG .label text,#mermaid-svg-Bj5roYXgfBwNWouG span{fill:#333;color:#333;}#mermaid-svg-Bj5roYXgfBwNWouG .node rect,#mermaid-svg-Bj5roYXgfBwNWouG .node circle,#mermaid-svg-Bj5roYXgfBwNWouG .node ellipse,#mermaid-svg-Bj5roYXgfBwNWouG .node polygon,#mermaid-svg-Bj5roYXgfBwNWouG .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Bj5roYXgfBwNWouG .rough-node .label text,#mermaid-svg-Bj5roYXgfBwNWouG .node .label text,#mermaid-svg-Bj5roYXgfBwNWouG .image-shape .label,#mermaid-svg-Bj5roYXgfBwNWouG .icon-shape .label{text-anchor:middle;}#mermaid-svg-Bj5roYXgfBwNWouG .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-Bj5roYXgfBwNWouG .rough-node .label,#mermaid-svg-Bj5roYXgfBwNWouG .node .label,#mermaid-svg-Bj5roYXgfBwNWouG .image-shape .label,#mermaid-svg-Bj5roYXgfBwNWouG .icon-shape .label{text-align:center;}#mermaid-svg-Bj5roYXgfBwNWouG .node.clickable{cursor:pointer;}#mermaid-svg-Bj5roYXgfBwNWouG .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-Bj5roYXgfBwNWouG .arrowheadPath{fill:#333333;}#mermaid-svg-Bj5roYXgfBwNWouG .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Bj5roYXgfBwNWouG .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Bj5roYXgfBwNWouG .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Bj5roYXgfBwNWouG .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-Bj5roYXgfBwNWouG .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Bj5roYXgfBwNWouG .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-Bj5roYXgfBwNWouG .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Bj5roYXgfBwNWouG .cluster text{fill:#333;}#mermaid-svg-Bj5roYXgfBwNWouG .cluster span{color:#333;}#mermaid-svg-Bj5roYXgfBwNWouG div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-Bj5roYXgfBwNWouG .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-Bj5roYXgfBwNWouG rect.text{fill:none;stroke-width:0;}#mermaid-svg-Bj5roYXgfBwNWouG .icon-shape,#mermaid-svg-Bj5roYXgfBwNWouG .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Bj5roYXgfBwNWouG .icon-shape p,#mermaid-svg-Bj5roYXgfBwNWouG .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-Bj5roYXgfBwNWouG .icon-shape .label rect,#mermaid-svg-Bj5roYXgfBwNWouG .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Bj5roYXgfBwNWouG .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-Bj5roYXgfBwNWouG .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-Bj5roYXgfBwNWouG :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 读取segdone
对segtotal取模
得到当前segment
计算readptr和len

3.2回调填充与分段推进

如果设置了callback,函数会在计算出readptr和len后,直接调用该回调:

c 复制代码
if (buf->callback)
  buf->callback (buf, *readptr, *len, buf->cb_data);

回调拿到的是当前分段地址和字节数,可以在这块内存中填入音频数据。该调用发生在gst_audio_ring_buffer_prepare_read()返回之前。

函数不会在内部递增segdone。设备子类完成一个分段的处理后,需调用gst_audio_ring_buffer_advance()更新计数。下一次调用prepare_read()再根据新的计数定位分段。

GstAudioSink和GstAudioSrc的处理循环都使用该接口取得分段内存。Sink从分段中读取数据并写入音频设备;Src把设备读出的数据写入分段。实际设备读写由各自的设备回调完成。

🌻4.实战案例

4.1准备测试用RingBuffer

下面创建一个内存后端RingBuffer子类。它不连接真实音频设备:打开操作直接成功,采集操作分配一块内存。RingBuffer包含4个分段,每段16字节,填充回调会把当前分段写成0x5A。

该示例重点观察prepare_read()返回的分段编号、地址、长度和回调效果。

4.2读取当前分段并推进位置

测试按以下步骤执行:

  1. 创建RingBuffer并设置4段、每段16字节的规格。
  2. 打开和采集RingBuffer资源,再设置分段填充回调。
  3. 调用prepare_read(),检查首段编号为0、长度为16字节,且首字节被回调写为0x5A。
  4. 调用gst_audio_ring_buffer_advance()推进一个分段,再次调用prepare_read(),检查分段编号变为1,地址移动16字节。

这组检查可以确认:prepare_read()根据segdone定位分段,并同步调用填充回调;分段计数由advance()单独推进。

4.3完整代码
c 复制代码
#include <gst/gst.h>
#include <gst/audio/audio.h>
#include <string.h>

typedef struct _TestRingBuffer
{
  GstAudioRingBuffer parent;
} TestRingBuffer;

typedef struct _TestRingBufferClass
{
  GstAudioRingBufferClass parent_class;
} TestRingBufferClass;

G_DEFINE_TYPE (TestRingBuffer, test_ring_buffer,
    GST_TYPE_AUDIO_RING_BUFFER);

/* 模拟设备打开,不连接真实硬件 */
static gboolean
test_open_device (GstAudioRingBuffer *buf)
{
  return TRUE;
}

/* 为测试RingBuffer分配分段内存 */
static gboolean
test_acquire (GstAudioRingBuffer *buf,
    GstAudioRingBufferSpec *spec)
{
  buf->size = spec->segsize * spec->segtotal;
  buf->memory = g_malloc0 (buf->size);

  return TRUE;
}

/* 释放测试后端申请的内存 */
static gboolean
test_release (GstAudioRingBuffer *buf)
{
  g_free (buf->memory);
  buf->memory = NULL;
  buf->size = 0;

  return TRUE;
}

/* 模拟设备关闭 */
static gboolean
test_close_device (GstAudioRingBuffer *buf)
{
  return TRUE;
}

/* 填充prepare_read返回的当前分段 */
static void
fill_segment (GstAudioRingBuffer *buf,
    guint8 *data,
    guint len,
    gpointer user_data)
{
  guint *callback_count = user_data;

  (*callback_count)++;
  memset (data, 0x5A, len);
}

static void
test_ring_buffer_class_init (TestRingBufferClass *klass)
{
  GstAudioRingBufferClass *ring_buffer_class =
      GST_AUDIO_RING_BUFFER_CLASS (klass);

  ring_buffer_class->open_device = test_open_device;
  ring_buffer_class->acquire = test_acquire;
  ring_buffer_class->release = test_release;
  ring_buffer_class->close_device = test_close_device;
}

static void
test_ring_buffer_init (TestRingBuffer *ring_buffer)
{
}

int
main (int argc, char *argv[])
{
  TestRingBuffer *test_buffer;
  GstAudioRingBuffer *ring_buffer;
  GstAudioRingBufferSpec *spec;
  gint segment = -1;
  gint len = 0;
  guint8 *readptr = NULL;
  guint callback_count = 0;
  gboolean opened = FALSE;
  gboolean acquired = FALSE;
  gboolean ok = FALSE;

  gst_init (&argc, &argv);

  test_buffer = g_object_new (
      test_ring_buffer_get_type (),
      NULL);
  ring_buffer = GST_AUDIO_RING_BUFFER (test_buffer);
  spec = &ring_buffer->spec;

  /* 设置测试规格,分段大小为16字节,总计4段 */
  memset (spec, 0, sizeof (*spec));
  gst_audio_info_init (&spec->info);
  if (!gst_audio_info_set_format (
          &spec->info,
          GST_AUDIO_FORMAT_S16,
          48000,
          2,
          NULL))
    goto done;

  spec->type = GST_AUDIO_RING_BUFFER_FORMAT_TYPE_RAW;
  spec->segsize = 16;
  spec->segtotal = 4;
  spec->seglatency = -1;

  /* prepare_read需要RingBuffer已经打开并采集资源 */
  if (!gst_audio_ring_buffer_open_device (ring_buffer))
    goto done;
  opened = TRUE;

  if (!gst_audio_ring_buffer_acquire (ring_buffer, spec))
    goto done;
  acquired = TRUE;

  /* 设置回调,验证每次prepare_read都会填充当前段 */
  gst_audio_ring_buffer_set_callback (
      ring_buffer,
      fill_segment,
      &callback_count);

  if (!gst_audio_ring_buffer_prepare_read (
          ring_buffer,
          &segment,
          &readptr,
          &len))
    goto done;

  /* 首次读取应定位第0段,回调写入0x5A */
  if (segment != 0
      || len != 16
      || readptr != ring_buffer->memory
      || readptr[0] != 0x5A
      || callback_count != 1)
    goto done;

  /* 模拟设备处理完一段,再检查环形位置向前移动 */
  gst_audio_ring_buffer_advance (ring_buffer, 1);

  if (!gst_audio_ring_buffer_prepare_read (
          ring_buffer,
          &segment,
          &readptr,
          &len))
    goto done;

  ok = segment == 1
      && len == 16
      && readptr == ring_buffer->memory + 16
      && readptr[0] == 0x5A
      && callback_count == 2;

done:
  /* 先移除回调,再释放其使用的栈上计数器 */
  if (ring_buffer)
    gst_audio_ring_buffer_set_callback (ring_buffer, NULL, NULL);

  if (acquired)
    gst_audio_ring_buffer_release (ring_buffer);
  if (opened)
    gst_audio_ring_buffer_close_device (ring_buffer);

  if (ring_buffer)
    gst_object_unref (ring_buffer);

  return ok ? 0 : 1;
}

🌻5.总结

gst_audio_ring_buffer_prepare_read()根据segdone返回当前分段的内存地址和长度,并在设置了填充回调时同步填充该分段,但不会自行推进分段计数。

相关推荐
杨云龙UP1 小时前
PostgreSQL 18.4 数据库整库迁移实战:pg_dump 备份与 pg_restore 恢复完整操作手册
运维·服务器·数据库·postgresql·pg_dump·pg_restore
我科绝伦(Huanhuan Zhou)1 小时前
Linux 通用压力测试脚本合集:磁盘 + 网络
linux·网络·压力测试
无人生还别怕2 小时前
如何安装任意版本docker
运维·docker·容器
土星云SaturnCloud3 小时前
土星云智慧服务区方案:AI视觉+边缘计算+视频存管一体化的全场景智能化落地解析
服务器·人工智能·ai·边缘计算
T0uken3 小时前
Colab:SSH 访问免费 GPU Server
运维·ssh
Python大数据分析@3 小时前
通过 MCP 实现 AI Agent自动化数据采集
运维·人工智能·爬虫·自动化
Initialize293083 小时前
告警一条接一条刷屏?五步治理告警风暴
运维
帷幕落秋3 小时前
Docker镜像加速
运维·docker
Soneswy4 小时前
Linux系统篇--重定向与文件操作命令
linux