[轻量级RTSP服务]Linux|麒麟操作系统下实现屏幕|系统声音采集

简介: 随着国产操作系统的推进,传统行业对Linux平台的呼声和需求越来越大,之前几年,我们发布了Linux平台运营商级的RTSP转RTMP推送模块、RTMP推送模块和RTSP、RTMP播放模块,前段时间,有开发者问我们,是不是可以在Linux平台实现轻量级RTSP服务,通过采集摄像头或者屏幕,在Linux平台实现类似于IPC的功能,便于第三方系统对接。

背景

随着国产操作系统的推进,传统行业对Linux平台的呼声和需求越来越大,之前几年,我们发布了Linux平台运营商级的RTSP转RTMP推送模块、RTMP推送模块和RTSP、RTMP播放模块,前段时间,有开发者问我们,是不是可以在Linux平台实现轻量级RTSP服务,通过采集摄像头或者屏幕,在Linux平台实现类似于IPC的功能,便于第三方系统对接。

技术实现

轻量级RTSP服务实际上前几天我们在做Linux模块的时候,已经实现了,只是没有在demo上加这块,原因很简单,这块诉求一方面比较少,另一方面,我们windows、Android和iOS平台都有相关的接口和demo,接口调用基本类似。


废话不多说,上代码:


启动RTSP服务:

    bool StartRTSPService(NT_SmartPublisherSDKAPI* push_api)
    {
        push_api->OpenRtspServer(&rtsp_server_handle_, 0);
        if (!rtsp_server_handle_)
        {
            fprintf(stderr, "Create Rtsp Server failed..\n");
            return false;
        }
        else
        {
            int port = 18554;
            if (NT_ERC_OK != push_api->SetRtspServerPort(rtsp_server_handle_, port))
            {
                push_api->CloseRtspServer(rtsp_server_handle_);
                rtsp_server_handle_ = nullptr;
                fprintf(stderr, "Set Rtsp Server port failed, not in range..\n");
                return false;
            }
            //std::string user_name = "admin";
            //std::string password = "123456";
            //push_api->SetRtspServerUserNamePassword(rtsp_server_handle_, user_name.c_str(), password.c_str());
            //bool is_multicast = false;
            //push_api->SetRtspServerMulticast(rtsp_server_handle_, is_multicast ? 1 : 0);
            if (push_api->StartRtspServer(rtsp_server_handle_, 0) == 0) {
                fprintf(stdout, "Start Rtsp server succeed!\n");
            }
            else
            {
                push_api->CloseRtspServer(rtsp_server_handle_);
                rtsp_server_handle_ = nullptr;
                fprintf(stderr, "Start Rtsp server failed, please check if port in usage..");
                return false;
            }
        }
        return true;
    }

开始发布RTSP流:

    NT_HANDLE StartPush(NT_SmartPublisherSDKAPI* push_api, const std::string& rtmp_url, int dst_fps)
    {
        NT_INT32 pulse_device_number = 0;
        if (NT_ERC_OK == push_api->GetAuidoInputDeviceNumber(2, &pulse_device_number))
        {
            fprintf(stdout, "Pulse device num:%d\n", pulse_device_number);
            char device_name[512];
            for (auto i = 0; i < pulse_device_number; ++i)
            {
                if (NT_ERC_OK == push_api->GetAuidoInputDeviceName(2, i, device_name, 512))
                {
                    fprintf(stdout, "index:%d name:%s\n", i, device_name);
                }
            }
        }
        NT_INT32 alsa_device_number = 0;
        if (pulse_device_number < 1)
        {
            if (NT_ERC_OK == push_api->GetAuidoInputDeviceNumber(1, &alsa_device_number))
            {
                fprintf(stdout, "Alsa device num:%d\n", alsa_device_number);
                char device_name[512];
                for (auto i = 0; i < alsa_device_number; ++i)
                {
                    if (NT_ERC_OK == push_api->GetAuidoInputDeviceName(1, i, device_name, 512))
                    {
                        fprintf(stdout, "index:%d name:%s\n", i, device_name);
                    }
                }
            }
        }
        NT_INT32 capture_speaker_flag = 0;
        if ( NT_ERC_OK == push_api->IsCanCaptureSpeaker(2, &capture_speaker_flag) )
        {
            if (capture_speaker_flag)
                fprintf(stdout, "Support speaker capture\n");
            else
                fprintf(stdout, "UnSupport speaker capture\n");
        }
        NT_INT32 is_support_window_capture = 0;
        if (NT_ERC_OK == push_api->IsCaptureXWindowSupported(NULL, &is_support_window_capture))
        {
            if (is_support_window_capture)
                fprintf(stdout, "Support window capture\n");
            else
                fprintf(stdout, "UnSupport window capture\n");
        }
        if (is_support_window_capture)
        {
            NT_INT32 win_count = 0;
            if (NT_ERC_OK == push_api->UpdateCaptureXWindowList(NULL, &win_count) && win_count > 0 )
            {
                fprintf(stdout, "X Capture Winows list++\n");
                for (auto i = 0; i < win_count; ++i)
                {
                    NT_UINT64 wid;
                    char title[512];
                    if (NT_ERC_OK == push_api->GetCaptureXWindowInfo(i, &wid, title, sizeof(title) / sizeof(char)))
                    {
                        x_win_list.push_back(wid);
                        fprintf(stdout, "wid:%llu, title:%s\n", wid, title);
                    }
                }
                fprintf(stdout, "X Capture Winows list--\n");
            }
        }
        std::vector<CameraInfo> cameras;
        GetCameraInfo(push_api, cameras);
        if (!cameras.empty())
        {
            fprintf(stdout, "cameras count:%d\n", (int)cameras.size());
            for (const auto& c : cameras)
            {
                fprintf(stdout, "camera name:%s, id:%s, cap_num:%d\n", c.name_.c_str(), c.id_.c_str(), (int)c.capabilities_.size());
                for (const auto& i : c.capabilities_)
                {
                    fprintf(stdout, "cap w:%d, h:%d, fps:%d\n", i.width_, i.height_, i.max_frame_rate_);
                }
            }
        }
        NT_UINT32 auido_option = NT_PB_E_AUDIO_OPTION_NO_AUDIO;
        if (pulse_device_number > 0 || alsa_device_number > 0)
        {
            auido_option = NT_PB_E_AUDIO_OPTION_CAPTURE_MIC;
        }
        else if (capture_speaker_flag)
        {
            auido_option = NT_PB_E_AUDIO_OPTION_CAPTURE_SPEAKER;
        }
        //auido_option = NT_PB_E_AUDIO_OPTION_CAPTURE_MIC_SPEAKER_MIXER;
        NT_UINT32 video_option = NT_PB_E_VIDEO_OPTION_SCREEN;
        if (!cameras.empty())
        {
            video_option = NT_PB_E_VIDEO_OPTION_CAMERA;
        }
        else if (is_support_window_capture)
        {
            video_option = NT_PB_E_VIDEO_OPTION_WINDOW;
        }
        // video_option = NT_PB_E_VIDEO_OPTION_LAYER;
        //video_option = NT_PB_E_VIDEO_OPTION_NO_VIDEO;
        NT_HANDLE push_handle = nullptr;
        //if (NT_ERC_OK != push_api->Open(&push_handle, NT_PB_E_VIDEO_OPTION_LAYER, NT_PB_E_AUDIO_OPTION_CAPTURE_SPEAKER, 0, NULL))
        if (NT_ERC_OK != push_api->Open(&push_handle, video_option, auido_option, 0, NULL))
        {
            return nullptr;
        }
        push_api->SetEventCallBack(push_handle, nullptr, OnSDKEventHandle);
        //push_api->SetXDisplayName(push_handle, ":0");
        //push_api->SetXDisplayName(push_handle, NULL);
        // 视频层配置方式
        if (NT_PB_E_VIDEO_OPTION_LAYER == video_option)
        {
            std::vector<std::shared_ptr<nt_pb_sdk::layer_conf_wrapper_base> > layer_confs;
            auto index = 0;
             第0层填充RGBA矩形, 目的是保证帧率, 颜色就填充全黑
            auto rgba_layer_c0 = std::make_shared<nt_pb_sdk::RGBARectangleLayerConfigWrapper>(index++, true, 0, 0, 1280, 720);
            rgba_layer_c0->conf_.red_ = 200;
            rgba_layer_c0->conf_.green_ = 200;
            rgba_layer_c0->conf_.blue_ = 200;
            rgba_layer_c0->conf_.alpha_ = 255;
            layer_confs.push_back(rgba_layer_c0);
            // 第一层为桌面层
            //auto screen_layer_c1 = std::make_shared<nt_pb_sdk::ScreenLayerConfigWrapper>(index++, true, 0, 0, 1280, 720);
            //screen_layer_c1->conf_.scale_filter_mode_ = 3;
            //layer_confs.push_back(screen_layer_c1);
             第一层为窗口
            if (!x_win_list.empty())
            {
                auto window_layer_c1 = std::make_shared<nt_pb_sdk::WindowLayerConfigWrapper>(index++, true, 0, 0, 640, 360);
                window_layer_c1->conf_.xwindow_ = x_win_list.back();
                layer_confs.push_back(window_layer_c1);
            }
             摄像头层
            if (!cameras.empty())
            {
                auto camera_layer_c1 = std::make_shared<nt_pb_sdk::CameraLayerConfigWrapper>(index++, true,
                    640, 0, 640, 360);
                strcpy(camera_layer_c1->conf_.device_unique_id_, cameras.front().id_.c_str());
                camera_layer_c1->conf_.is_flip_horizontal_ = 0;
                camera_layer_c1->conf_.is_flip_vertical_ = 0;
                camera_layer_c1->conf_.rotate_degress_ = 0;
                layer_confs.push_back(camera_layer_c1);
                if (cameras.size() > 1)
                {
                    auto camera_layer_c2 = std::make_shared<nt_pb_sdk::CameraLayerConfigWrapper>(index++, true,
                        640, 0, 320, 240);
                    strcpy(camera_layer_c2->conf_.device_unique_id_, cameras.back().id_.c_str());
                    camera_layer_c2->conf_.is_flip_horizontal_ = 0;
                    camera_layer_c2->conf_.is_flip_vertical_ = 0;
                    camera_layer_c2->conf_.rotate_degress_ = 0;
                    layer_confs.push_back(camera_layer_c2);
                }
            }
            auto image_layer1 = std::make_shared<nt_pb_sdk::ImageLayerConfigWrapper>(index++, true, 650, 120, 324, 300);
            strcpy(image_layer1->conf_.file_name_utf8_, "./testpng/tca.png");
            layer_confs.push_back(image_layer1);
            auto image_layer2 = std::make_shared<nt_pb_sdk::ImageLayerConfigWrapper>(index++, true, 120, 380, 182, 138);
            strcpy(image_layer2->conf_.file_name_utf8_, "./testpng/t4.png");
            layer_confs.push_back(image_layer2);
            std::vector<const NT_PB_LayerBaseConfig* > layer_base_confs;
            for (const auto& i : layer_confs)
            {
                layer_base_confs.push_back(i->getBase());
            }
            if (NT_ERC_OK != push_api->SetLayersConfig(push_handle, 0, layer_base_confs.data(),
                layer_base_confs.size(), 0, nullptr))
            {
                push_api->Close(push_handle);
                push_handle = nullptr;
                return nullptr;
            }
        }
        // push_api->SetScreenClip(push_handle, 0, 0, 1280, 720);
        if (video_option == NT_PB_E_VIDEO_OPTION_CAMERA)
        {
            if (!cameras.empty())
            {
                push_api->SetVideoCaptureDeviceBaseParameter(push_handle, cameras.front().id_.c_str(),
                    640, 480);
                //push_api->FlipVerticalCamera(push_handle, 1);
                //push_api->FlipHorizontalCamera(push_handle, 1);
                //push_api->RotateCamera(push_handle, 0);
            }
        }
        if (video_option == NT_PB_E_VIDEO_OPTION_WINDOW)
        {
            if (!x_win_list.empty())
            {
                //push_api->SetCaptureXWindow(push_handle, x_win_list[0]);
                push_api->SetCaptureXWindow(push_handle, x_win_list.back());
            }
        }
        push_api->SetFrameRate(push_handle, dst_fps); // 帧率设置
        push_api->SetVideoEncoder(push_handle, 0, 1, NT_MEDIA_CODEC_ID_H264, 0);
        push_api->SetVideoBitRate(push_handle, 2000);  // 平均码率2000kbps
        push_api->SetVideoQuality(push_handle, 26); 
        push_api->SetVideoMaxBitRate(push_handle, 4000); // 最大码率4000kbps
        // openh264 配置特定参数
        push_api->SetVideoEncoderSpecialInt32Option(push_handle, "usage_type", 0); //0是摄像头编码, 1是屏幕编码
        push_api->SetVideoEncoderSpecialInt32Option(push_handle, "rc_mode", 1); // 0是质量模式, 1是码率模式
        push_api->SetVideoEncoderSpecialInt32Option(push_handle, "enable_frame_skip", 0); // 0是关闭跳帧, 1是打开跳帧
        push_api->SetVideoKeyFrameInterval(push_handle, dst_fps*2); // 关键帧间隔
        push_api->SetVideoEncoderProfile(push_handle, 3); // H264 high
        push_api->SetVideoEncoderSpeed(push_handle, 3); // 编码速度设置到3
        if (pulse_device_number > 0)
        {
            push_api->SetAudioInputLayer(push_handle, 2);
            push_api->SetAuidoInputDeviceId(push_handle, 0);
        }
        else if (alsa_device_number > 0)
        {
            push_api->SetAudioInputLayer(push_handle, 1);
            push_api->SetAuidoInputDeviceId(push_handle, 0);
        }
        push_api->SetEchoCancellation(push_handle, 1, 0);
        push_api->SetNoiseSuppression(push_handle, 1);
        push_api->SetAGC(push_handle, 1);
        push_api->SetVAD(push_handle, 1);
        push_api->SetInputAudioVolume(push_handle, 0, 1.0);
        push_api->SetInputAudioVolume(push_handle, 1, 0.2);
        // 音频配置
        push_api->SetPublisherAudioCodecType(push_handle, 1);
        //push_api->SetMute(push_handle, 1);
        if ( NT_ERC_OK != push_api->SetURL(push_handle, rtmp_url.c_str(), NULL) )
        {
            push_api->Close(push_handle);
            push_handle = nullptr;
            return nullptr;
        }
        //启动轻量级RTSP服务
        bool is_rtsp_service_started = StartRTSPService(push_api);
        if (is_rtsp_service_started)
        {
            if (!rtsp_server_handle_)
            {
                fprintf(stderr, "StartRtspStream rtsp server handle is null..");
                return nullptr;
            }
            std::string rtsp_stream_name = "stream1";
            push_api->SetRtspStreamName(push_handle, rtsp_stream_name.c_str());
            push_api->ClearRtspStreamServer(push_handle);
            push_api->AddRtspStreamServer(push_handle, rtsp_server_handle_, 0);
            if (NT_ERC_OK != push_api->StartRtspStream(push_handle, 0))
            {
                push_api->Close(push_handle);
                push_handle = nullptr;
                return nullptr;
            }
            fprintf(stdout, "StartRtspStream succeed..\n");
        }
        return push_handle;
    }
}

main函数调用如下:

/*
 * Author: daniusdk.com
*/
int main(int argc, char *argv[])
{
    //signal(SIGINT, &OnSigIntHandler);
    //signal(SIGFPE, &OnSigIntHandler);
    struct sigaction act;
    sigemptyset(&act.sa_mask);
    act.sa_sigaction = OnSaSigaction;
    act.sa_flags = SA_SIGINFO;
    sigaction(SIGINT, &act, NULL);
    sigaction(SIGFPE, &act, NULL);
    XInitThreads(); // X支持多线程, 必须调用
    auto display = XOpenDisplay(nullptr);
    if (!display)
    {
        fprintf(stderr, "Cannot connect to X server\n");
        return 0;
    }
    auto screen = DefaultScreen(display);
    auto root = XRootWindow(display, screen);
    XWindowAttributes root_win_att;
    if (!XGetWindowAttributes(display, root, &root_win_att))
    {
        fprintf(stderr, "Get Root window attri failed\n");
        XCloseDisplay(display);
        return 0;
    }
    int main_w = root_win_att.width / 2, main_h = root_win_att.height / 2;
    auto black_pixel = BlackPixel(display, screen);
    auto white_pixel = WhitePixel(display, screen);
    auto main_wid = XCreateSimpleWindow(display, root, 0, 0, main_w, main_h, 0, white_pixel, black_pixel);
    if (!main_wid)
    {
        fprintf(stderr, "Cannot Create Main Window\n");
        XCloseDisplay(display);
        return 0;
    }
    XSelectInput(display, main_wid, StructureNotifyMask | KeyPressMask);
    auto sub_wid = CreateSubWindow(display, screen, main_wid);
    if (!sub_wid)
    {
        fprintf(stderr, "Cannot Create Render Window\n");
        XDestroyWindow(display, main_wid);
        XCloseDisplay(display);
        return 0;
    }
    XMapWindow(display, main_wid);
    XStoreName(display, main_wid, "Video Preview");
    XMapWindow(display, sub_wid);
    LogInit();
    NT_SmartPublisherSDKAPI push_api;
    if (!PushSDKInit(push_api))
    {
        XDestroyWindow(display, sub_wid);
        XDestroyWindow(display, main_wid);
        XCloseDisplay(display);
        return 0;
    }
    auto push_handle = StartPush(&push_api, "rtmp://192.168.0.103:1935/hls/test1", 30);
    if (!push_handle)
    {
        fprintf(stderr, "start push failed.\n");
        XDestroyWindow(display, sub_wid);
        XDestroyWindow(display, main_wid);
        XCloseDisplay(display);
        push_api.UnInit();
        return 0;
    }
    // 开启预览,也可以不开启, 根据需求来
    push_api.SetPreviewXWindow(push_handle, "", sub_wid);
    push_api.StartPreview(push_handle, 0, nullptr);
    // auto push_handle1 = StartPush(&push_api, "rtmp://192.168.0.154:1935/live/test1", 30);
    while (!g_is_exit)
    {
        while (MY_X11_Pending(display, 10))
        {
            XEvent xev;
            memset(&xev, 0, sizeof(xev));
            XNextEvent(display, &xev);
            if (xev.type == ConfigureNotify)
            {
                if (xev.xconfigure.window == main_wid)
                {
                    if (xev.xconfigure.width != main_w || xev.xconfigure.height != main_h)
                    {
                        main_w = xev.xconfigure.width;
                        main_h = xev.xconfigure.height;
                        XMoveResizeWindow(display, sub_wid, 0, 0, main_w - 4, main_h - 4);
                    }
                }
            }
            else if (xev.type == KeyPress)
            {
                if (xev.xkey.keycode == XKeysymToKeycode(display, XK_Escape))
                {
                    fprintf(stdout, "ESC Key Press\n");
                    g_is_exit = true;
                }
            }
            if (g_is_exit)
                break;
        }
    }
    fprintf(stdout, "Skip run loop, is_exit:%d\n", g_is_exit);
    push_api.StopPreview(push_handle);
    push_api.StopRtspStream(push_handle);
    push_api.StopRtspServer(rtsp_server_handle_);
    push_api.CloseRtspServer(rtsp_server_handle_);
    rtsp_server_handle_ = nullptr;
    push_api.Close(push_handle);
    push_handle = nullptr;
    XDestroyWindow(display, sub_wid);
    XDestroyWindow(display, main_wid);
    XCloseDisplay(display);
    push_api.UnInit();
    fprintf(stdout, "SDK UnInit..\n");
    return 0;
}

音视频选项,可设置的类型如下:

/*定义Video源选项*/
typedef enum _NT_PB_E_VIDEO_OPTION
{
    NT_PB_E_VIDEO_OPTION_NO_VIDEO = 0x0,
    NT_PB_E_VIDEO_OPTION_SCREEN   = 0x1, // 采集屏幕
    NT_PB_E_VIDEO_OPTION_CAMERA      = 0x2, // 摄像头采集
    NT_PB_E_VIDEO_OPTION_LAYER    = 0x3, // 视频合并,比如桌面叠加摄像头等
    NT_PB_E_VIDEO_OPTION_ENCODED_DATA = 0x4, // 已经编码的视频数据,目前支持H264
    NT_PB_E_VIDEO_OPTION_WINDOW   = 0x5, // 采集窗口
} NT_PB_E_VIDEO_OPTION;
/*定义Auido源选项*/
typedef enum _NT_PB_E_AUDIO_OPTION
{
    NT_PB_E_AUDIO_OPTION_NO_AUDIO                    = 0x0,
    NT_PB_E_AUDIO_OPTION_CAPTURE_MIC                = 0x1, // 采集麦克风音频
    NT_PB_E_AUDIO_OPTION_CAPTURE_SPEAKER            = 0x2, // 采集扬声器
    NT_PB_E_AUDIO_OPTION_CAPTURE_MIC_SPEAKER_MIXER    = 0x3, // 麦克风扬声器混音
    NT_PB_E_AUDIO_OPTION_ENCODED_DATA                = 0x4, // 编码后的音频数据,目前支持AAC, speex宽带(wideband mode)
    NT_PB_E_AUDIO_OPTION_EXTERNAL_PCM_DATA            = 0x5, /*外部PCM数据*/
    NT_PB_E_AUDIO_OPTION_MIC_EXTERNAL_PCM_MIXER     = 0x6, /* 麦克风和外部PCM数据混音 当前只支持一路外部音频和内置麦克风混音*/
    NT_PB_E_AUDIO_OPTION_TWO_EXTERNAL_PCM_MIXER     = 0x7, /* 两路外部PCM数据混音*/
} NT_PB_E_AUDIO_OPTION;

调用流程和Windows平台轻量级RTSP服务基本一致(实际上接口本身几无差别)。

总结

Linux平台轻量级RTSP服务,对我们来说,轻车熟路了,主要是demo展示这块,整体编码性能和延迟,配合我们的RTSP播放器测试下来,几百毫秒,可完全满足无纸化同屏、教育类等传统行业技术诉求。

相关实践学习
CentOS 7迁移Anolis OS 7
龙蜥操作系统Anolis OS的体验。Anolis OS 7生态上和依赖管理上保持跟CentOS 7.x兼容,一键式迁移脚本centos2anolis.py。本文为您介绍如何通过AOMS迁移工具实现CentOS 7.x到Anolis OS 7的迁移。
相关文章
|
17天前
|
Linux
在 Linux 系统中,“cd”命令用于切换当前工作目录
在 Linux 系统中,“cd”命令用于切换当前工作目录。本文详细介绍了“cd”命令的基本用法和常见技巧,包括使用“.”、“..”、“~”、绝对路径和相对路径,以及快速切换到上一次工作目录等。此外,还探讨了高级技巧,如使用通配符、结合其他命令、在脚本中使用,以及实际应用案例,帮助读者提高工作效率。
58 3
|
17天前
|
监控 安全 Linux
在 Linux 系统中,网络管理是重要任务。本文介绍了常用的网络命令及其适用场景
在 Linux 系统中,网络管理是重要任务。本文介绍了常用的网络命令及其适用场景,包括 ping(测试连通性)、traceroute(跟踪路由路径)、netstat(显示网络连接信息)、nmap(网络扫描)、ifconfig 和 ip(网络接口配置)。掌握这些命令有助于高效诊断和解决网络问题,保障网络稳定运行。
48 2
|
11天前
|
Ubuntu Linux 网络安全
linux系统ubuntu中在命令行中打开图形界面的文件夹
在Ubuntu系统中,通过命令行打开图形界面的文件夹是一个高效且实用的操作。无论是使用Nautilus、Dolphin还是Thunar,都可以根据具体桌面环境选择合适的文件管理器。通过上述命令和方法,可以简化日常工作,提高效率。同时,解决权限问题和图形界面问题也能确保操作的顺利进行。掌握这些技巧,可以使Linux操作更加便捷和灵活。
15 3
|
17天前
|
安全 网络协议 Linux
本文详细介绍了 Linux 系统中 ping 命令的使用方法和技巧,涵盖基本用法、高级用法、实际应用案例及注意事项。
本文详细介绍了 Linux 系统中 ping 命令的使用方法和技巧,涵盖基本用法、高级用法、实际应用案例及注意事项。通过掌握 ping 命令,读者可以轻松测试网络连通性、诊断网络问题并提升网络管理能力。
53 3
|
20天前
|
安全 Linux 数据安全/隐私保护
在 Linux 系统中,查找文件所有者是系统管理和安全审计的重要技能。
在 Linux 系统中,查找文件所有者是系统管理和安全审计的重要技能。本文介绍了使用 `ls -l` 和 `stat` 命令查找文件所有者的基本方法,以及通过文件路径、通配符和结合其他命令的高级技巧。还提供了实际案例分析和注意事项,帮助读者更好地掌握这一操作。
37 6
|
16天前
|
监控 Linux
如何检查 Linux 内存使用量是否耗尽?这 5 个命令堪称绝了!
本文介绍了在Linux系统中检查内存使用情况的5个常用命令:`free`、`top`、`vmstat`、`pidstat` 和 `/proc/meminfo` 文件,帮助用户准确监控内存状态,确保系统稳定运行。
105 6
|
24天前
|
缓存 监控 Linux
|
28天前
|
Linux Shell 数据安全/隐私保护
|
28天前
|
域名解析 网络协议 安全

热门文章

最新文章