现在是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的缺点
-
性能问题
- shell/cmd/ps都是解释型语言,使用的时候必然新开线程进行解释,然后是调用执行
- 每次调用SHELL脚本时,都会启动一个新的进程,这可能会导致性能开销,尤其是在频繁调用的情况下。
- SHELL脚本的解释执行通常比直接调用系统调用或库函数要慢。
-
可移植性问题
- SHELL脚本通常依赖于特定的SHELL环境(如Bash、Zsh等),这可能会限制程序的可移植性。
- 不同UNIX系统上的SHELL环境可能存在差异,导致脚本在不同系统上的行为不一致。
-
安全问题
- 直接运行SHELL脚本可能会引入安全漏洞,尤其是在处理用户输入时,容易受到SHELL注入攻击。
- 如果脚本文件被恶意修改,可能会导致程序执行不安全的操作。
特性 | SHELL脚本 | C++程序 |
---|---|---|
执行方式 | 解释执行,需要SHELL解释器 | 编译为机器码,直接执行 |
系统API调用 | 通过命令间接调用 | 直接调用 |
效率 | 低(启动进程 + 解释执行) | 高(直接执行机器码) |
跨平台性 | 差(依赖SHELL环境和命令) | 好(通过编译器和库屏蔽平台差异) |
安全性 | 较低(容易受到SHELL注入攻击) | 较高(直接调用API,输入可控) |
维护性 | 较差(混合脚本和C++代码,难以调试) | 较好(纯C++代码,易于维护和扩展) |
当然,这些是对底层开发而言的ヾ(•ω•`)o 诸君小规模需求,实现就行了,没必要死磕性能