第一坑:VM硬件设置

环境概况:

  • VM15(处理器6, 11GB, 硬盘60GB), ubuntu20.04tls, Qt5.15

可能出现的问题:

  • g++: internal compiler error: Killed (program cc1plus)

以往都是硬盘装双系统没有遇到过的这种情况,使用make -j 6进行编译并行编译的数量为6,一开始内存设置为4g。出现1所显示的错误,大概率是因为内存不足杀死了正在编译的进程导致编译失败;

方法1: 设置内存为8G,并且使用make -j 2减少并行编译数量;

  • 结果:仍然出现了该错误,因为在编译Qt15.0的Qtwebengine这部分的chrome内核时会使用ninja进行编译,此处仍然会按照最大并行数量进行编译导致失败。

方法2: 内存设置为11GB;

  • 结果:没有出现该错误。

第二坑:没有仔细configure之后检查的看依赖项直接make

configure命令如下:

./configure -prefix /home/ss/qt/qt5.15 -opensource -qpa xcb -xcb -xcb-xlib -bundled-xcb-xinput -nomake tests

可能出现的错误:

  • 1.Project ERROR: xcb development package not found #1

  • 2.surface_factory_qt.cpp:(.text._ZN15QtWebEngineCore16SurfaceFactoryQtC2Ev+0x41) undefined reference to `vtable for ui::GLOzoneGLXQt’collect2: error: ld returned 1 exit status

  • 3.undefined reference to `vtable for ui::GLOzoneGLXQt’

一些依赖项没有安装,如x11,qpa-xcb,opengl相关库引发错误,尤其是某些检测到但其实并没有安装开发版的包也会引发错误,如下图所示:

例子

qpa-xcb如若没有安装成功,即使编译成功,在写程序编译运行时将会报错,在安装目录下./plugins/platforms会缺少libqxcb.so这个库,可能的提示:

  • Failed to load platform plugin “xcb”.

请根据下列网址,逐个下载对应版本的dev包

https://doc.qt.io/qt-5/linux-deployment.html

https://doc.qt.io/qt-5/linux-requirements.html


其他

剩余一部分错误的依赖提示都比较明显就不在此赘述记录了。最后configure在–recheck-all之前最好删除编译的东西重新编译(好费时),所以最好把依赖补充完整,以免长时间的编译…(折磨),提示如下:

Prior to reconfiguration, make sure you remove any leftovers from
the previous build.

Rc4主要是运用了两次异或运算还原原字符串,代码如下


namespace Encryption {

const int RC4BOXSIZE = 256;

class RC4 {
public:
    RC4(const std::string &keyStr) : m_SBox(nullptr) {
        const char *keydata = keyStr.c_str();
        const int keySize = keyStr.size();

        m_SBox = new unsigned char[RC4BOXSIZE];
        for (int i = 0; i < RC4BOXSIZE; ++i) {
            m_SBox[i] = i;
        }

        for (int i = 0, j = 0; i < RC4BOXSIZE; ++i) {
            j = (j + m_SBox[i] + keydata[i % keySize] + 20) % RC4BOXSIZE;
            std::swap(m_SBox[i], m_SBox[j]); // 搅乱box数据
        }
    }
    ~RC4() {
        delete[] m_SBox;
        m_SBox = nullptr;
    }

    std::string encry(const std::string& inputStr) {
        return encry(inputStr.c_str(), inputStr.size());
    }

    char *encry(const char *inputDatas, int len) {
        if (!inputDatas || len == 0 || !m_SBox)
            return "";

        unsigned char *cpBox = new unsigned char[RC4BOXSIZE];
        memcpy(cpBox, m_SBox, RC4BOXSIZE);

        std::string outputStr;
        char *outDatas = new char[len];
        int i = 0, j = 0;
        for (int k = 0; k < len; ++k) {
            i = (i + 1) % RC4BOXSIZE;
            j = (j + cpBox[i]) % RC4BOXSIZE;
            std::swap(cpBox[i], cpBox[j]);

            //(m_SBox[i] + cpBox[j]) % 256  的值是一致的,异或反转
            outDatas[k] = inputDatas[k] ^ cpBox[(m_SBox[i] + cpBox[j]) % 256]; 
        }
        return outDatas;
    }
private:
    unsigned char *m_SBox;
};

}

void testRc4() {
    string keyStr = "this is a test";
    Encryption::RC4* rc4 = new Encryption::RC4(keyStr);

    const char* datas = "start encryption";
    // 这里应当是个固定的大小,加密后长度不变
    size_t len = datas, strlen(datas);
    char *newData = rc4->encry(datas, len);  
    char *newData1 = rc4->encry(newData, len);


    assert(datas != newData1, "error");

    delete[] newData;
    delete[] newData1;
    delete rc4;
}

将v上调至2的幂

    inline unsigned long long qNextPowerOfTwo(unsigned int v)
    {
        v |= v >> 1;
        v |= v >> 2;
        v |= v >> 4;
        v |= v >> 8;
        v |= v >> 16;
        ++v;
        return v;
    }

    inline unsigned long long qNextPowerOfTwo(unsigned long long v)
    {
        v |= v >> 1;    // 步骤1
        v |= v >> 2;    // 步骤2
        v |= v >> 4;    // 步骤3
        v |= v >> 8;
        v |= v >> 16;
        v |= v >> 32; 
        ++v;            //  最终
        return v;
    } 

基本思路:想要获取到2的幂那么只需要获取到最高位1的位置,然后向这个位置的右边全部置0,然后该位左移动一位即可获得。

而上面两个函数则是将该位的右侧全部置1,然后+1使得进位获得结果

即如:

初始:    1 0 0 0 0 0 0
步骤1:   1 1 0 0 0 0 0
步骤2:    1 1 1 1 0 0 0
步骤3:    1 1 1 1 1 1 1
...
最终:   1 0 0 0 0 0 0 0

将v上调至8的倍数

    const unsigned align = 8;
    unsigned int RoundUp(unsigned int v) 
    {
        return (v + align - 1) & ~(align - 1);
    }

基本思路:想要获得8的倍数,那么就要最后三位置0即可,即:v & ~(align - 1) 考虑到1-7要到8所以需要加上7即有:(v + align - 1)

统计v的二进制1和0的个数

    size_t countOneV1(int v) {
        size_t c = 0;
        while (v) 
        {
            if (v & 1) ++c;
            v >>= 1;
        }
        return c;
    }

    size_t countOneV2(int v) {
        size_t c = 0;
        while (v) 
        {
            ++c;
            v = v & (v - 1);
        }
        return c;
    }

    size_t countZero(int v) {
        size_t c = 0;
        while(v) 
        {
            ++c;
            v = v | (v + 1);
        }
        return c;
    }

基本思路:V1版本通过逐位与1遍历即可得到方便理解;V2版本在(v-1)时会有两种情况

1. 当前位是1,那么如 v = 101, v - 1 = 100 
2. 当前位是0,那么如 v = 100, v - 1 = 011

两种情况都会将最右的1给清0,所以每次都会消除一个1。同理对0计数也是一样的

1.什么是WMI?

WMI全称Windows Management Instrumentation,是Microsoft基于Web的企业管理(WBEM)的实现,WBEM是一项行业倡议,旨在开发用于在企业环境中访问管理信息的标准技术….其他不再赘述,详情见MSDN - About WMI

The CIMWin32 WMI providers支持CimWin32.dll中支持的类,由核心的CIM WMI类和电源管理事件,以下用到了Win32_DiskDrive, Win32_DiskDriveToDiskPartition, Win32_LogicalDiskToPartition等类。相关链接WMI providers

2. 开始

如果系统只有一个硬盘,那其实就可以直接通过命令行直接获取结果,例如:

wmic diskdrive where index=0 get serialnumber

倘若不只只有一个硬盘呢?有以下思路:

1.首先获取程序所在逻辑硬盘位置 
2.通过逻辑硬盘位置和硬盘的序列号关联表查询序列号
3.获取到当前硬盘序列号

其中的难点在于第二步,如何获取硬盘的逻辑分区情况,这时刚好可以通过WQL查询获得,以下是通过MSDN连接WMI例子而加工的代码:

HRESULT hres;

// Step 1: --------------------------------------------------
// Initialize COM. ------------------------------------------

hres = CoInitializeEx(0, COINIT_MULTITHREADED);
if (FAILED(hres))
{
    cout << "Failed to initialize COM library. Error code = 0x"
        << hex << hres << endl;
    return 1;                  // Program has failed.
}

// Step 2: --------------------------------------------------
// Set general COM security levels --------------------------
hres = CoInitializeSecurity(
    NULL,
    -1,                          // COM authentication
    NULL,                        // Authentication services
    NULL,                        // Reserved
    RPC_C_AUTHN_LEVEL_DEFAULT,   // Default authentication 
    RPC_C_IMP_LEVEL_IMPERSONATE, // Default Impersonation  
    NULL,                        // Authentication info
    EOAC_NONE,                   // Additional capabilities 
    NULL                         // Reserved
);

// note: 此处步骤1,2 如果已经初始化过,第二次会false,所以视情况调用

if (FAILED(hres))
{
    cout << "Failed to initialize security. Error code = 0x"
        << hex << hres << endl;
    CoUninitialize();
    return 1;                    // Program has failed.
}

// Step 3: ---------------------------------------------------
// Obtain the initial locator to WMI -------------------------

IWbemLocator* pLoc = NULL;

hres = CoCreateInstance(
    CLSID_WbemLocator,
    0,
    CLSCTX_INPROC_SERVER,
    IID_IWbemLocator, (LPVOID*)&pLoc);

if (FAILED(hres))
{
    cout << "Failed to create IWbemLocator object."
        << " Err code = 0x"
        << hex << hres << endl;
    CoUninitialize();
    return 1;                 // Program has failed.
}

// Step 4: -----------------------------------------------------
// Connect to WMI through the IWbemLocator::ConnectServer method

IWbemServices* pSvc = NULL;

// Connect to the root\cimv2 namespace with
// the current user and obtain pointer pSvc
// to make IWbemServices calls.
hres = pLoc->ConnectServer(
    _bstr_t(L"ROOT\\CIMV2"), // Object path of WMI namespace
    NULL,                    // User name. NULL = current user
    NULL,                    // User password. NULL = current
    0,                       // Locale. NULL indicates current
    NULL,                    // Security flags.
    0,                       // Authority (for example, Kerberos)
    0,                       // Context object 
    &pSvc                    // pointer to IWbemServices proxy
);

if (FAILED(hres))
{
    cout << "Could not connect. Error code = 0x"
        << hex << hres << endl;
    pLoc->Release();
    CoUninitialize();
    return 1;                // Program has failed.
}

cout << "Connected to ROOT\\CIMV2 WMI namespace" << endl;


// Step 5: --------------------------------------------------
// Set security levels on the proxy -------------------------

hres = CoSetProxyBlanket(
    pSvc,                        // Indicates the proxy to set
    RPC_C_AUTHN_WINNT,           // RPC_C_AUTHN_xxx
    RPC_C_AUTHZ_NONE,            // RPC_C_AUTHZ_xxx
    nullptr,                        // Server principal name 
    RPC_C_AUTHN_LEVEL_CALL,      // RPC_C_AUTHN_LEVEL_xxx 
    RPC_C_IMP_LEVEL_IMPERSONATE, // RPC_C_IMP_LEVEL_xxx
    nullptr,                        // client identity
    EOAC_NONE                    // proxy capabilities 
);

if (FAILED(hres))
{
    cout << "Could not set proxy blanket. Error code = 0x"
        << hex << hres << endl;
    pSvc->Release();
    pLoc->Release();
    CoUninitialize();
    return 1;               // Program has failed.
}

// Step 6: --------------------------------------------------
// Use the IWbemServices pointer to make requests of WMI ----

// For example, get the name of the operating system
IEnumWbemClassObject* pEnumerator = nullptr;

hres = pSvc->ExecQuery(
    bstr_t("WQL"),
    bstr_t("SELECT * FROM Win32_DiskDrive"),
    WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
    NULL,
    &pEnumerator);

// note: 
// 1. 这里通过查询得到DEVICEID = \\\\.\\PHYSICALDRIVE0, 然而还需要将\ 替换为 \\

// 2. WQL语句中“=”左右不能有空格,否则ExecQuery之后会查询失败
//    如:ASSOCIATORS OF {Win32_DiskDrive.DeviceID=\" xxx \"}就会失败

// 3. gcc环境中 bstr_t("xxx") 可能会编译报错, 使用 "BSTR xxx = SysAllocString(L"sql .. ") " 和 SysFreeString(xxx) 组合代替
//    如:bstr_t("WQL") ——> BSTR strQueryLanguage = SysAllocString(L"WQL"); SysFreeString(strQueryLanguage) 

//hres = pSvc->ExecQuery(
//    bstr_t("WQL"),
//    bstr_t("ASSOCIATORS OF {Win32_DiskDrive.DeviceID=\"\\\\\\\\.\\\\PHYSICALDRIVE0\"} WHERE AssocClass=Win32_DiskDriveToDiskPartition"),
//    //bstr_t("ASSOCIATORS OF {Win32_LogicalDisk.DeviceID=\"C:\"}"),
//    WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
//    NULL,
//    &pEnumerator);

if (FAILED(hres))
{
    cout << "Query for operating system name failed."
        << " Error code = 0x"
        << hex << hres << endl;
    pSvc->Release();
    pLoc->Release();
    CoUninitialize();
    return 1;               // Program has failed.
}

// Step 7: -------------------------------------------------
// Get the data from the query in step 6 -------------------

IWbemClassObject* pclsObj = nullptr;
ULONG uReturn = 0;

while (pEnumerator)
{
    HRESULT hr = pEnumerator->Next(WBEM_INFINITE, 1,
        &pclsObj, &uReturn);

    if (0 == uReturn)
    {
        break;
    }

    VARIANT vtProp;

    // Get the value of the Name property
    hr = pclsObj->Get(L"DeviceID", 0, &vtProp, 0, 0);
    //wcout << "DeviceID : " << vtProp.bstrVal << endl;
    wstring deviceID = vtProp.bstrVal;
    wcout << deviceID << endl;

    hr = pclsObj->Get(L"SerialNumber", 0, &vtProp, 0, 0);
    wstring serialNumber = vtProp.bstrVal;
    wcout << "SerialNumber : " << serialNumber << endl;

    VariantClear(&vtProp);
    pclsObj->Release();

    replace(deviceID, L"\\", L"\\\\");

    wstring query;
    query.append(L"ASSOCIATORS OF {Win32_DiskDrive.DeviceID=\"");
    query.append(deviceID);
    query.append(L"\"} WHERE AssocClass=Win32_DiskDriveToDiskPartition");

    IEnumWbemClassObject* partitionEnumerator = nullptr;
    hres = pSvc->ExecQuery(
        bstr_t("WQL"),
        bstr_t(query.c_str()),
        WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
        nullptr,
        &partitionEnumerator);

    IWbemClassObject* diskPartition = nullptr;
    while (partitionEnumerator)
    {
        HRESULT hr = partitionEnumerator->Next(WBEM_INFINITE, 1,
            &diskPartition, &uReturn);

        if (0 == uReturn)
        {
            break;
        }
        VARIANT vtProp;

        // Get the value of the Name property
        hr = diskPartition->Get(L"DeviceID", 0, &vtProp, 0, 0);
        wstring diskPartitionDeviceID = vtProp.bstrVal;
        wcout << "diskPartitionDeviceID : " << diskPartitionDeviceID << endl;

        VariantClear(&vtProp);

        diskPartition->Release();
        diskPartition = nullptr;

        wstring query;
        query.append(L"ASSOCIATORS OF {Win32_DiskPartition.DeviceID=\"");
        query.append(diskPartitionDeviceID);
        query.append(L"\"} WHERE AssocClass=Win32_LogicalDiskToPartition");

        IEnumWbemClassObject* logicalDiskEnumerator = nullptr;
        hres = pSvc->ExecQuery(
            bstr_t("WQL"),
            bstr_t(query.c_str()),
            WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
            nullptr,
            &logicalDiskEnumerator);

        IWbemClassObject* logicalPartition = nullptr;
        while (logicalDiskEnumerator) {

            HRESULT hr = logicalDiskEnumerator->Next(WBEM_INFINITE, 1,
                &logicalPartition, &uReturn);

            if (0 == uReturn)
            {
                break;
            }

            VARIANT vtProp;

            hr = logicalPartition->Get(L"DeviceID", 0, &vtProp, 0, 0);
            wstring logicalDiskDeviceID = vtProp.bstrVal;
            wcout << "logicalDiskDeviceID : " << logicalDiskDeviceID << endl; // 通过上方得到的SerialNumber和此处的logicalDiskDeviceID建立map就完成了步骤2

            VariantClear(&vtProp);

            logicalPartition->Release();
            logicalPartition = nullptr;

        }
    }
}

// Cleanup
// ========

pSvc->Release();
pLoc->Release();
pEnumerator->Release();
CoUninitialize();

return 0;   // Program successfully completed.

代码中踩坑的地方做了注释,记录这些以备不时之需。

其中WQL查询参考文章How Can I Correlate Logical Drives and Physical Disks?

最近使用了下Grunt和WebPack,这里总结下使用中的一些问题。

环境: node@v8.9.3\ grunt@v1.0.2\ grunt-cli@v1.2.0

Part1 版本问题

安装grunt-contrib-image提示

WARN node unsupported "node@v8.9.3" is incompatible with
grunt-contrib-imagemin@2.0.1 › imagemin-optipng@5.2.1 ›
optipng-bin@3.1.4 › bin-build@2.2.0 › download@4.4.3 ›
got@^5.0.0, expected node@>=0.10.0 <7

imagemin安装版本v2.0.1,期望的版本是node@>=0.10.0 <7 与8.9.3不匹配,这个警告并不影响imagemin的使用,配置好image插件之后,输入grunt命令就自动执行压缩

配置如下

	imagemin: {
	  dist: {
	    options: {
	      optimizationLevel: 6,
	      progressive: true
	    },
	    files: [{
	      expand: true,
	      cwd: 'images/',
	      src: '{,*/}*.{png,jpg,jpeg,ico}',
	      dest: 'images/'
	    }]
	  }
	}

Part2 配置说明

配置插件选项的说明的一些链接,以便于之后配置使用

jshint: https://jshint.com/docs/options/

其他大部分可以在npmjsgrunt搜索找到说明或在github的readme中,部分链接如下:

concat: https://github.com/gruntjs/grunt-contrib-concat

uglify;https://www.npmjs.com/package/grunt-contrib-uglify

htmlmin: https://www.npmjs.com/package/grunt-contrib-htmlmin