rt-thread icon indicating copy to clipboard operation
rt-thread copied to clipboard

[Bug] 多线程情况下触发用户态代码段的缺页异常处理失败

Open heyuanjie87 opened this issue 11 months ago • 4 comments

RT-Thread Version

master

Hardware Type/Architectures

qemu-virt64-riscv/k230

Develop Toolchain

GCC

Describe the bug

触发问题的代码

#include <atomic>
#include <iomanip>
#include <iostream>
#include <sstream>
#include <thread>
#include <unistd.h>
#include <vector>

using namespace std;

atomic_flag lock_stream = ATOMIC_FLAG_INIT;

void append_number_unlock(int x)
{
    int num;
    cout << "https://www";
    for (int i = 0; i < 1000000; i++)
        num++;
    cout << ".rt-";
    for (int i = 0; i < 1000000; i++)
        num++;
    cout << "thread";
    for (int i = 0; i < 1000000; i++)
        num++;
    cout << ".org/";
    for (int i = 0; i < 1000000; i++)
        num++;
    cout << setw(3) << setfill('0') << x << endl;
}

void append_number_lock(int x)
{
    while (lock_stream.test_and_set()) {
    }
    int num;
    cout << "https://www";
    for (int i = 0; i < 1000000; i++)
        num++;
    cout << ".rt-";
    for (int i = 0; i < 1000000; i++)
        num++;
    cout << "thread";
    for (int i = 0; i < 1000000; i++)
        num++;
    cout << ".org/";
    for (int i = 0; i < 1000000; i++)
        num++;
    cout << setw(3) << setfill('0') << x << endl;
    lock_stream.clear();
}

void string_split(const string &str, const char split, vector<string> &res)
{
    if (str == "")
        return;
    // 在字符串末尾也加入分隔符,方便截取最后一段
    string strs = str + split;
    size_t pos  = strs.find(split);

    // 若找不到内容则字符串搜索函数返回 npos
    while (pos != strs.npos) {
        string temp = strs.substr(0, pos);
        res.push_back(temp);
        // 去掉已分割的字符串,在剩下的字符串中进行分割
        strs = strs.substr(pos + 1, strs.size());
        pos  = strs.find(split);
    }
}

int main()
{
    cout << "un_lock" << endl;
    vector<thread> threads_unlock;
    for (int i = 1; i <= 20; ++i)
        threads_unlock.push_back(thread(append_number_unlock, i));
    for (auto &th : threads_unlock)
        th.join();
    cout << "lock" << endl;

    stringstream ss;
    streambuf   *buffer = cout.rdbuf();
    cout.rdbuf(ss.rdbuf());

    vector<thread> threads_lock;
    for (int i = 1; i <= 20; ++i)
        threads_lock.push_back(thread(append_number_lock, i));
    for (auto &th : threads_lock)
        th.join();

    cout.rdbuf(buffer);
    string s(ss.str());
    cout << s << endl;

    vector<string> str_list;
    string_split(s, '\n', str_list);
    bool b = true;
    for (auto str : str_list) {
        cout << str.length() << " ";
        if (str != "")
            if (str.length() != 29) {
                b = false;
            }
    }

    if (b)
        printf("{Test PASS}.\n");
    else
        printf("{Test FAIL}.\n");

    return 0;
}

编译器

针对k230带V指令的gcc13(通用编译器gcc10无法触发)

  • 编译参数: -static

现象描述

当两个线程同时触发同一指令地址的缺页异常时,一个线程成功映射页,另一个线程处理这个异常时被判定为权限问题导致异常处理失败

Other additional context

No response

heyuanjie87 avatar Jan 01 '25 05:01 heyuanjie87

可以确认下,k230上不使用v指令时是否存在问题。k230的v指令,感觉在rt-smart支持上并不算完好,导致上下文切换栈深度太深了

BernardXiong avatar Jan 01 '25 13:01 BernardXiong

确认过这个示例运行时没执行到v指令,因为我的qemu6不支持v指令,当时qemu运行时编译内核没开v支持也没报错。这里只是一个单纯的缺页处理不完善的问题

heyuanjie87 avatar Jan 04 '25 04:01 heyuanjie87

用这个示例在k230第一次运行能触发数据区的缺页处理异常(原因同上)

#include <iomanip>
#include <iostream>
#include <mutex>
#include <sstream>
#include <thread>
#include <unistd.h>
#include <vector>

using namespace std;

std::mutex mtx;

void append_number_unlock(int x)
{
    int num;
    cout << "https://www";
    for (int i = 0; i < 1000000; i++)
        num++;
    cout << ".rt-";
    for (int i = 0; i < 1000000; i++)
        num++;
    cout << "thread";
    for (int i = 0; i < 1000000; i++)
        num++;
    cout << ".org/";
    for (int i = 0; i < 1000000; i++)
        num++;
    cout << setw(3) << setfill('0') << x << endl;
}

void append_number_lock(int x)
{
    mtx.lock();
    int num;
    cout << "https://www";
    for (int i = 0; i < 1000000; i++)
        num++;
    cout << ".rt-";
    for (int i = 0; i < 1000000; i++)
        num++;
    cout << "thread";
    for (int i = 0; i < 1000000; i++)
        num++;
    cout << ".org/";
    for (int i = 0; i < 1000000; i++)
        num++;
    cout << setw(3) << setfill('0') << x << endl;
    mtx.unlock();
}

void string_split(const string &str, const char split, vector<string> &res)
{
    if (str == "")
        return;
    // 在字符串末尾也加入分隔符,方便截取最后一段
    string strs = str + split;
    size_t pos  = strs.find(split);

    // 若找不到内容则字符串搜索函数返回 npos
    while (pos != strs.npos) {
        string temp = strs.substr(0, pos);
        res.push_back(temp);
        // 去掉已分割的字符串,在剩下的字符串中进行分割
        strs = strs.substr(pos + 1, strs.size());
        pos  = strs.find(split);
    }
}

int main()
{
    cout << "un_lock" << endl;
    vector<thread> threads_unlock;
    for (int i = 1; i <= 20; ++i)
        threads_unlock.push_back(thread(append_number_unlock, i));
    for (auto &th : threads_unlock)
        th.join();
    cout << "lock" << endl;

    stringstream ss;
    streambuf   *buffer = cout.rdbuf();
    cout.rdbuf(ss.rdbuf());

    vector<thread> threads_lock;
    for (int i = 1; i <= 20; ++i)
        threads_lock.push_back(thread(append_number_lock, i));
    for (auto &th : threads_lock)
        th.join();

    cout.rdbuf(buffer);
    string s(ss.str());
    cout << s << endl;

    vector<string> str_list;
    string_split(s, '\n', str_list);
    bool b = true;
    for (auto str : str_list) {
        cout << str.length() << " ";
        if (str != "")
            if (str.length() != 29) {
                b = false;
            }
    }

    if (b)
        printf("{Test PASS}.\n");
    else
        printf("{Test FAIL}.\n");

    return 0;
}

heyuanjie87 avatar Jan 09 '25 06:01 heyuanjie87

这个问题是否也可以尝试在aarch64平台上验证下?

BernardXiong avatar Jan 12 '25 08:01 BernardXiong

具体是啥样的错误啊,不知道是不是k230工具链提供的cpp相关库函数有问题,我用他们的工具链编译一个很简单的cpp程序也报错load page fault。换成userapp提供的工具链编译就没出现这种问题😂

eatvector avatar Jul 21 '25 12:07 eatvector

具体是啥样的错误啊,不知道是不是k230工具链提供的cpp相关库函数有问题,我用他们的工具链编译一个很简单的cpp程序也报错load page fault。换成userapp提供的工具链编译就没出现这种问题😂

k230的工具链在2024年11月左右修过无法运行cpp的问题,也许是你的编译器比较旧

heyuanjie87 avatar Jul 24 '25 01:07 heyuanjie87