为什么不推荐在程序中使用system(“...“) 进行系统交互

现在是2025/3/9 ,前些日子intel 发布了他们的雷电接口的开发工具。代码写的可以说是相当糟糕,英特尔现在是这个水平吗?一个底层的驱动,里面各种脚本调用system(“* * *”)满天飞 ,不用猜这八成是英特尔的印度程序员们写出来的咖喱味代码

所以,为什么我们觉得在代码中运行 b a s h / c m d / p s 是不好,甚至愚蠢的习惯? 所以,为什么我们觉得在代码中运行bash/cmd/ps 是不好,甚至愚蠢的习惯? 所以,为什么我们觉得在代码中运行bash/cmd/ps是不好,甚至愚蠢的习惯?

我们先来看看一个例子,获得系统连接到的wifi名称

一个是使用CMD来获得,一个使用WINAPI来获得系统连接到的WIFI的SSID 。

第一个代码很亲切啊,估计很多人都爱这么写,我也爱这么写 。核心代码只有一行。简单又简洁

int main()
{
    auto timebegin = std::chrono::steady_clock::now();
    
    system(" netsh wlan show interfaces | findstr \"SSID\"");
    
    auto timeend = std::chrono::steady_clock::now();
    
    std::cout << "Time: " << std::chrono::duration_cast<std::chrono::milliseconds>(timeend - timebegin).count() << "ms" << std::endl;

	return 0;
}

运行结果

    SSID                   : stu-xdwlan
    BSSID                  : =========
Time: 66ms

很好,速度也不慢吧?

第二个代码是调用WINAPI

一般来说,正常人类不会去这么写,因为这需要查询API手册反复调试。而且,第三方库(比如boost/asio)提供了这些WINAPI的封装(通过之前的文章,我们知道涉及硬件交互的代码不可能绕开API,所以第三方库只是提供了一个方便的封装)

#include <windows.h>
#include <wlanapi.h>
#include <objbase.h>
#include <wtypes.h>
#include <string>
#include <iostream> 
#pragma comment(lib, "wlanapi.lib")

std::string GetCurrentWifiSSID() {
    DWORD dwVersion = 0;
    HANDLE hClient = NULL;
    DWORD dwResult = 0;
    PWLAN_INTERFACE_INFO_LIST pInterfaceList = NULL;
    std::string ssid;

    // 初始化WLAN API
    dwResult = WlanOpenHandle(2, NULL, &dwVersion, &hClient);
    if (dwResult != ERROR_SUCCESS) return "";

    // 枚举无线接口
    dwResult = WlanEnumInterfaces(hClient, NULL, &pInterfaceList);
    if (dwResult != ERROR_SUCCESS) {
        WlanCloseHandle(hClient, NULL);
        return "";
    }

    // 遍历所有无线接口
    for (DWORD i = 0; i < pInterfaceList->dwNumberOfItems; i++) {
        PWLAN_INTERFACE_INFO pInterface = &pInterfaceList->InterfaceInfo[i];
        PWLAN_CONNECTION_ATTRIBUTES pConnectInfo = NULL;
        DWORD connectInfoSize = 0;
        WLAN_OPCODE_VALUE_TYPE opType;

        // 获取连接信息
        dwResult = WlanQueryInterface(hClient, &pInterface->InterfaceGuid,
            wlan_intf_opcode_current_connection, NULL,
            &connectInfoSize, (PVOID*)&pConnectInfo, &opType);

        if (dwResult == ERROR_SUCCESS && pConnectInfo) {
            if (pConnectInfo->isState == wlan_interface_state_connected) {
                auto pSsid = &pConnectInfo->wlanAssociationAttributes.dot11Ssid;
                ssid.assign((char*)pSsid->ucSSID, pSsid->uSSIDLength);
                WlanFreeMemory(pConnectInfo);
                break;
            }
            WlanFreeMemory(pConnectInfo);
        }
    }

    if (pInterfaceList) WlanFreeMemory(pInterfaceList);
    WlanCloseHandle(hClient, NULL);
    return ssid;
}

int main() {
	auto timebegin = std::chrono::steady_clock::now();

	std::string ssid = GetCurrentWifiSSID();
	if (ssid.empty()) {
		std::cout << "无法获取当前连接的WiFi SSID" << std::endl;
	}
	else {
		std::cout << "当前连接的WiFi SSID: " << ssid << std::endl;
	}
    
	auto timeend = std::chrono::steady_clock::now();    
	std::cout << "Time: " << std::chrono::duration_cast<std::chrono::milliseconds>(timeend - timebegin).count() << "ms" << std::endl;
    
	return 0;
}

运行结果

当前连接的WiFi SSID: stu-xdwlan
Time: 4ms

对比得出,API调用4ms bash操作却花了66ms 。API 花的时间仅仅是bash的17分之1。这只是单次任务,如果我们编写的是驱动,一份代码要跑成千上万遍,性能上的浪费有多大?。那太可怕了,所以程序最好不要写bash

写到这里,你或许明白了为什么那些咖喱味的代码有多讨厌了。

条分缕析的说明bash的缺点

  1. 性能问题

    • shell/cmd/ps都是解释型语言,使用的时候必然新开线程进行解释,然后是调用执行
    • 每次调用SHELL脚本时,都会启动一个新的进程,这可能会导致性能开销,尤其是在频繁调用的情况下。
    • SHELL脚本的解释执行通常比直接调用系统调用或库函数要慢。
  2. 可移植性问题

    • SHELL脚本通常依赖于特定的SHELL环境(如Bash、Zsh等),这可能会限制程序的可移植性。
    • 不同UNIX系统上的SHELL环境可能存在差异,导致脚本在不同系统上的行为不一致。
  3. 安全问题

    • 直接运行SHELL脚本可能会引入安全漏洞,尤其是在处理用户输入时,容易受到SHELL注入攻击
    • 如果脚本文件被恶意修改,可能会导致程序执行不安全的操作。
特性SHELL脚本C++程序
执行方式解释执行,需要SHELL解释器编译为机器码,直接执行
系统API调用通过命令间接调用直接调用
效率低(启动进程 + 解释执行)高(直接执行机器码)
跨平台性差(依赖SHELL环境和命令)好(通过编译器和库屏蔽平台差异)
安全性较低(容易受到SHELL注入攻击)较高(直接调用API,输入可控)
维护性较差(混合脚本和C++代码,难以调试)较好(纯C++代码,易于维护和扩展)

当然,这些是对底层开发而言的ヾ(•ω•`)o 诸君小规模需求,实现就行了,没必要死磕性能

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值