You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何页故障未使线程延后执行?同核线程执行顺序不符预期

问题描述

我编写了如下代码,在file.c中故意让其中一个线程触发页故障。我使用rdtscp()作为内存屏障,确保打印语句在加载操作完成后执行。由于线程t2会触发页故障,我预期t1的打印语句会先执行。我通过taskset -c 10 ./file让两个线程运行在同一核心上,但实际看到t2先输出打印语句,请问这是什么原因?


代码实现

util.c

#include "util.h"

// to use as a fence() instruction
extern inline __attribute__((always_inline))
CYCLES rdtscp(void) {
    CYCLES cycles;
    asm volatile ("rdtscp" :  "=a" (cycles));

    return cycles;
}

// initialize address
void init_ram_address(char* FILE_NAME){
    char *filename = FILE_NAME;
    int fd = open(filename, O_RDWR);
    if(fd == -1) {
        printf("Could not open file .\n");
        exit(0);
    }
    void *file_address = mmap(NULL, DEFAULT_FILE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_POPULATE, fd, 0);
    ram_address = (int *) file_address;
}

// initialize address
void init_disk_address(char* FILE_NAME){
    char *filename = FILE_NAME;
    int fd = open(filename, O_RDWR);
    if(fd == -1) {
        printf("Could not open file .\n");
        exit(0);
    }
    void *file_address = mmap(NULL, DEFAULT_FILE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    disk_address = (int *) file_address;
}

file.c

#include "util.h"

void *f1();
void *f2();

pthread_barrier_t barrier;
pthread_mutex_t mutex;

int main(int argc, char **argv)
{
    pthread_t t1, t2;

    // in ram
    init_ram_address(RAM_FILE_NAME);
    // in disk
    init_disk_address(DISK_FILE_NAME);

    pthread_create(&t1, NULL, &f1, NULL);
    pthread_create(&t2, NULL, &f2, NULL);

    
    pthread_join(t1, NULL);
    pthread_join(t2, NULL);
    
    return 0;
}

void *f1()
{
    rdtscp();
    int load = *(ram_address);
    rdtscp();
    printf("Expecting this to be run first.\n");
}

void *f2()
{
    rdtscp();
    int load = *(disk_address);
    rdtscp();
    printf("Expecting this to be run second.\n");
}

问题分析与解答

出现这种现象的核心原因有三个:

  1. 线程启动无同步机制
    你定义了pthread_barrier_t但未初始化、未使用,两个线程的启动时间完全由操作系统调度器决定,不遵循创建顺序。t2可能比t1更早被调度执行,哪怕绑定到同一核心也无法保证执行顺序。

  2. 页故障处理的不确定性
    如果目标文件的页已经被操作系统缓存,t2的加载操作不会触发真正的磁盘IO,页故障处理会在极短时间内完成——甚至比t1的加载+打印流程更快,导致t2的打印先输出。想要确保触发真实磁盘IO的页故障,可在运行程序前执行sync; echo 3 > /proc/sys/vm/drop_caches(需要root权限)清除文件缓存。

  3. 对rdtscp屏障作用的误解
    rdtscp仅能保证当前线程内,它之前的指令全部执行完成后,后续指令才会开始,但无法干预线程间的调度顺序。它只是确保t2的打印一定在自身加载操作完成后执行,不能控制t1和t2之间的执行先后。

解决方法

  • 添加线程同步:初始化并使用屏障,确保两个线程同时开始执行:
    在main中添加pthread_barrier_init(&barrier, NULL, 2);,并在t1和t2函数开头添加pthread_barrier_wait(&barrier);。
  • 强制清除缓存:运行程序前执行缓存清除命令,确保t2触发真实磁盘页故障。

内容的提问来源于stack exchange,提问作者Sathvik Swaminathan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.19 00:45:40