用户级线程库

my_phread.c

my_pthread.c:

// File:	my_pthread.c
// Author:	Yujie REN, Jieming Yin
// Date:	April 2025

/*
 * 线程库实现采用用户级线程方案,优点是上下文切换开销小,缺点是无法利用多核
 * 调度策略支持PSJF和MLFQ两种,可以通过编译时选项切换
 */

#include "my_pthread_t.h"

// INITAILIZE ALL YOUR VARIABLES HERE
static void schedule();
static void sched_stcf();
static void sched_mlfq();
static void enqueue_mlfq(tcb *thread, int priority);
// YOUR CODE HERE
// 全局变量
static queue *run_queue = NULL;        // 运行队列(用于 PSJF)
static queue *mlfq_queues[MLFQ_LEVELS]; // 多级反馈队列
static tcb *current_thread = NULL;      // 当前运行的线程
static schedPolicy current_policy = POLICY_PSJF; // 默认调度策略

/*
 * 初始化多级反馈队列
 * 每个优先级队列独立维护,高优先级时间片短,低优先级时间片长
 */
void init_mlfq() {
    for (int i = 0; i < MLFQ_LEVELS; i++) {
        mlfq_queues[i] = NULL;
    }
}

/* create a new thread 
 * 线程创建需要考虑的关键点:
 * TCB的初始化和内存管理
 * 栈空间的分配
 * 上下文的初始设置
 */
int my_pthread_create(my_pthread_t *thread, pthread_attr_t *attr, 
	void *(*function)(void*), void *arg) {
    // 分配TCB内存
    tcb *new_tcb = (tcb *)malloc(sizeof(tcb));
    if (!new_tcb) {
        // TODO: 错误处理:内存分配失败
        return -1;
    }

    // 初始化线程控制块
    new_tcb->thread_id = syscall(SYS_gettid); // TODO: 考虑使用自定义ID生成
    new_tcb->status = NOT_STARTED;
    new_tcb->stack = malloc(SIGSTKSZ); // TODO: 考虑可配置的栈大小
    if (!new_tcb->stack) {
        free(new_tcb); // 清理已分配的TCB
        return -1;
    }

    // 初始化上下文
    getcontext(&new_tcb->context);
    new_tcb->context.uc_stack.ss_sp = new_tcb->stack;
    new_tcb->context.uc_stack.ss_size = SIGSTKSZ;
    new_tcb->context.uc_link = NULL;
    makecontext(&new_tcb->context, (void (*)())function, 1, arg);

    *thread = new_tcb->thread_id;

    // TODO: 实现线程队列操作
    // enqueue(new_tcb);

    return 0;
}

/* give CPU possession to other user level threads voluntarily 
 * yield操作是协作式调度的基础
 * 需要正确保存当前上下文
 */
int my_pthread_yield() {
    // TODO: 实现上下文保存
    // save_context(current_thread);

    schedule();
    return 0;
}

/* terminate a thread 
 * 需要考虑的关键点:
 *   资源清理(栈内存等)
 *   通知等待的线程
 *   处理最后一个线程的特殊情况
 */
void my_pthread_exit(void *value_ptr) {
    // TODO: 添加资源清理
    current_thread->return_value = value_ptr;
    current_thread->status = TERMINATED;

    // TODO: 处理等待该线程的其他线程
    schedule();
}

/* wait for thread termination 
 * 需要实现线程查找机制
 * 考虑添加超时机制
 * 处理目标线程不存在的情况
 */
int my_pthread_join(my_pthread_t thread, void **value_ptr) {
    // TODO: 实现线程查找
    tcb *target_thread = find_thread_by_id(thread);
    if (!target_thread) {
        return -1; // 线程不存在
    }

    while (target_thread->status != TERMINATED) {
        // TODO: 添加超时机制
        block_current_thread();
    }

    if (value_ptr) {
        *value_ptr = target_thread->return_value;
    }

    // TODO: 清理目标线程资源
    return 0;
}

/* initialize the mutex lock 
 * 互斥锁的关键特性:
 * 原子性操作
 * 所有权跟踪
 * 阻塞队列管理
 */
int my_pthread_mutex_init(my_pthread_mutex_t *mutex, 
    const pthread_mutexattr_t *mutexattr) {
    if (!mutex) return -1;
    
    mutex->locked = 0;
    mutex->owner = NULL;
    mutex->block_queue = NULL;
    return 0;
}

/* acquire the mutex lock 
 * 使用原子操作保证互斥性
 * 需要处理死锁情况
*/
int my_pthread_mutex_lock(my_pthread_mutex_t *mutex) {
    while (__sync_lock_test_and_set(&mutex->locked, 1)) {
        // TODO: 实现阻塞队列操作
        // enqueue(mutex->block_queue, current_thread);
        schedule();
    }
    mutex->owner = current_thread;
    return 0;
}

/* release the mutex lock 
 * 验证所有权很重要
 * 需要唤醒等待的线程
 * 考虑优先级反转问题
 */
int my_pthread_mutex_unlock(my_pthread_mutex_t *mutex) {
    if (mutex->owner != current_thread) return -1;

    mutex->locked = 0;
    mutex->owner = NULL;

    // TODO: 实现阻塞队列操作
    // move_blocked_threads_to_run_queue(mutex->block_queue);

    return 0;
}

/* destroy the mutex 
 * 
 * 需要确保没有线程在等待该锁
 * 完整的资源清理很重要
 */
int my_pthread_mutex_destroy(my_pthread_mutex_t *mutex) {
    if (mutex->locked) return -1;
    
    // TODO: 确保没有线程在等待该锁
    free(mutex->block_queue);
    return 0;
}

/* scheduler */
static void schedule() {
    if (current_policy == POLICY_PSJF) {
        sched_stcf();
    } else if (current_policy == POLICY_MLFQ) {
        sched_mlfq();
    }
}

static void sched_stcf() {
    tcb *shortest_job = NULL;
    queue *prev = NULL, *curr = run_queue;

    // 找到时间片计数器最小的线程
    while (curr) {
        if (!shortest_job || curr->thread->time_slices < shortest_job->time_slices) {
            shortest_job = curr->thread;
        }
        curr = curr->next;
    }

    if (shortest_job) {
        // 切换到最短作业线程
        tcb *prev_thread = current_thread;
        current_thread = shortest_job;

        if (prev_thread && prev_thread->status == RUNNING) {
            prev_thread->status = SUSPENDED;
        }
        current_thread->status = RUNNING;

        // 切换上下文
        if (prev_thread) {
            swapcontext(&prev_thread->context, ¤t_thread->context);
        } else {
            setcontext(¤t_thread->context);
        }
    }
}

static void sched_mlfq() {
    tcb *next_thread = NULL;

    // 从最高优先级队列中选择线程
    for (int i = 0; i < MLFQ_LEVELS; i++) {
        if (mlfq_queues[i]) {
            next_thread = mlfq_queues[i]->thread;
            mlfq_queues[i] = mlfq_queues[i]->next; // 出队
            break;
        }
    }

    if (next_thread) {
        // 切换到选中的线程
        tcb *prev_thread = current_thread;
        current_thread = next_thread;

        if (prev_thread && prev_thread->status == RUNNING) {
            prev_thread->status = SUSPENDED;
        }
        current_thread->status = RUNNING;

        // 切换上下文
        if (prev_thread) {
            swapcontext(&prev_thread->context, ¤t_thread->context);
        } else {
            setcontext(¤t_thread->context);
        }
    }
}

// Feel free to add any other functions you need

// YOUR CODE HERE
void timer_interrupt_handler(int signum) {
    if (current_policy == POLICY_MLFQ) {
        // 降级当前线程
        if (current_thread->priority < MLFQ_LEVELS - 1) {
            current_thread->priority++;
        }
        // 将当前线程重新加入对应优先级队列
        enqueue_mlfq(current_thread, current_thread->priority);
    } else if (current_policy == POLICY_PSJF) {
        // 增加当前线程的时间片计数器
        current_thread->time_slices++;
    }

    // 切换到调度器
    schedule();
}

// 设置定时器
void setup_timer(int time_quantum_ms) {
    struct sigaction sa;
    struct itimerval timer;

    sa.sa_handler = &timer_interrupt_handler;
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = SA_RESTART;
    sigaction(SIGALRM, &sa, NULL);

    timer.it_value.tv_sec = 0;
    timer.it_value.tv_usec = time_quantum_ms * 1000;
    timer.it_interval.tv_sec = 0;
    timer.it_interval.tv_usec = time_quantum_ms * 1000;

    setitimer(ITIMER_REAL, &timer, NULL);
}

void enqueue(queue **q, tcb *thread) {
    queue *new_node = (queue *)malloc(sizeof(queue));
    new_node->thread = thread;
    new_node->next = NULL;

    if (!*q) {
        *q = new_node;
    } else {
        queue *temp = *q;
        while (temp->next) {
            temp = temp->next;
        }
        temp->next = new_node;
    }
}

void enqueue_mlfq(tcb *thread, int priority) {
    enqueue(&mlfq_queues[priority], thread);
}

my_phread.h

my_pthread.h 
#ifndef MY_PTHREAD_T_H
#define MY_PTHREAD_T_H
#define USE_MY_PTHREAD 1
#define _GNU_SOURCE
/*
 * 用户级线程库头文件
 * 定义了线程库的核心数据结构和API接口
 * 线程控制块(TCB)结构
 * 线程状态定义
 * 调度策略枚举
 * 互斥锁结构
 * 线程队列结构
 */



#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

/* 
 * 线程标识符类型
 * 使用无符号整数表示,确保唯一性
 */
typedef unsigned int my_pthread_t;

/*
 * 线程状态枚举
 * NOT_STARTED: 新创建,尚未运行
 * RUNNING: 正在运行
 * SUSPENDED: 被挂起
 * TERMINATED: 已终止
 * FINISHED: 已完成
 */
typedef enum threadStatus {
    NOT_STARTED = 0,
    RUNNING,
    SUSPENDED,
    TERMINATED,
    FINISHED,
} threadStatus;

/*
 * 调度策略枚举
 * POLICY_RR: 轮转调度(Round Robin)
 * POLICY_MLFQ: 多级反馈队列(Multi-Level Feedback Queue)
 * POLICY_PSJF: 抢占式最短作业优先(Preemptive Shortest Job First)
 */
typedef enum schedPolicy {
    POLICY_RR = 0,
    POLICY_MLFQ,
    POLICY_PSJF
} schedPolicy;

/*
 * 线程控制块(Thread Control Block)
 * 包含线程的所有必要信息:
 * - thread_id: 线程唯一标识符
 * - status: 线程当前状态
 * - context: 线程上下文信息
 * - stack: 线程栈指针
 * - return_value: 线程返回值
 * - priority: 线程优先级(用于MLFQ)
 * - time_slices: 已使用的时间片数(用于PSJF)
 */
typedef struct threadControlBlock {
    my_pthread_t thread_id;
    threadStatus status;
    ucontext_t context;
    void *stack;
    void *return_value;
    int priority;
    int time_slices;
} tcb;

/*
 * 多级反馈队列级数
 * 定义了MLFQ调度策略中的队列数量
 * 每个级别对应不同的时间片长度和优先级
 */
#define MLFQ_LEVELS 4

/*
 * 互斥锁结构
 * locked: 锁的状态(0表示未锁定,1表示锁定)
 * owner: 当前持有锁的线程
 * block_queue: 等待获取锁的线程队列
 */
typedef struct my_pthread_mutex_t {
    int locked;
    tcb *owner;
    struct queue *block_queue;
} my_pthread_mutex_t;

/*
 * 线程队列节点结构
 * 用于实现线程的等待队列和就绪队列
 * thread: 指向线程控制块的指针
 * next: 指向队列中下一个节点
 */
typedef struct queue {
    tcb *thread;
    struct queue *next;
} queue;

/* 
 * 线程管理函数
 * create: 创建新线程
 * yield: 主动让出CPU
 * exit: 终止当前线程
 * join: 等待指定线程结束
 */
int my_pthread_create(my_pthread_t *thread, pthread_attr_t *attr, void *(*function)(void*), void *arg);
int my_pthread_yield();
void my_pthread_exit(void *value_ptr);
int my_pthread_join(my_pthread_t thread, void **value_ptr);

/*
 * 互斥锁操作函数
 * init: 初始化互斥锁
 * lock: 获取锁
 * unlock: 释放锁
 * destroy: 销毁锁
 */
int my_pthread_mutex_init(my_pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr);
int my_pthread_mutex_lock(my_pthread_mutex_t *mutex);
int my_pthread_mutex_unlock(my_pthread_mutex_t *mutex);
int my_pthread_mutex_destroy(my_pthread_mutex_t *mutex);

#endif

两种调度算法介绍:

PSJF (Preemptive Shortest Job First) 实现

static void sched_stcf() {

    tcb *shortest_job = NULL;

    queue *prev = NULL, *curr = run_queue;

    // 找到时间片计数器最小的线程

    while (curr) {

        if (!shortest_job || curr->thread->time_slices < shortest_job->time_slices) {

            shortest_job = curr->thread;

        }

        curr = curr->next;

    }

    if (shortest_job) {

        // 切换到最短作业线程

        tcb *prev_thread = current_thread;

        current_thread = shortest_job;

        if (prev_thread && prev_thread->status == RUNNING) {

            prev_thread->status = SUSPENDED;

        }

        current_thread->status = RUNNING;

        // 切换上下文

        if (prev_thread) {

            swapcontext(&prev_thread->context, ¤t_thread->context);

        } else {

            setcontext(¤t_thread->context);

        }

    }

}

作业长度估计:

使用time_slices计数器记录每个线程已经运行的时间片数

假设已运行时间越长的线程,剩余时间也越长

在定时器中断处理时更新计数器:

     if (current_policy == POLICY_PSJF) {

         current_thread->time_slices++;

     }

调度决策:

遍历运行队列,找到time_slices最小的线程

这个线程被认为是"最短作业"

如果找到更短的作业,就进行抢占

MLFQ (Multi-Level Feedback Queue) 实现

static void sched_mlfq() {

    tcb *next_thread = NULL;

    // 从最高优先级队列中选择线程

    for (int i = 0; i < MLFQ_LEVELS; i++) {

        if (mlfq_queues[i]) {

            next_thread = mlfq_queues[i]->thread;

            mlfq_queues[i] = mlfq_queues[i]->next; // 出队

            break;

        }

    }

    if (next_thread) {

        // 切换到选中的线程

        tcb *prev_thread = current_thread;

        current_thread = next_thread;

        if (prev_thread && prev_thread->status == RUNNING) {

            prev_thread->status = SUSPENDED;

        }

        current_thread->status = RUNNING;

        // 切换上下文

        if (prev_thread) {

            swapcontext(&prev_thread->context, ¤t_thread->context);

        } else {

            setcontext(¤t_thread->context);

        }

    }

}

多级队列管理:

使用数组mlfq_queues[MLFQ_LEVELS]维护多个优先级队列

每个队列对应一个优先级级别

通过init_mlfq()初始化队列:

     void init_mlfq() {

         for (int i = 0; i < MLFQ_LEVELS; i++) {

             mlfq_queues[i] = NULL;

         }

     }

优先级调整:

在定时器中断中处理优先级降级:

     if (current_policy == POLICY_MLFQ) {

         if (current_thread->priority < MLFQ_LEVELS - 1) {

             current_thread->priority++;

         }

         enqueue_mlfq(current_thread, current_thread->priority);

     }

线程用完时间片后降到低优先级队列

使用enqueue_mlfq将线程加入对应优先级的队列

 上下文切换实现

   ucontext_t context;  // 在TCB中保存上下文信息

使用ucontext_t结构保存线程的执行状态

包含程序计数器、栈指针等关键寄存器

切换操作:

   if (prev_thread) {

       swapcontext(&prev_thread->context, ¤t_thread->context);

   } else {

       setcontext(¤t_thread->context);

   }

swapcontext:保存当前上下文并切换到新上下文

setcontext:直接切换到新上下文(用于首次运行)

定时器中断:

   void setup_timer(int time_quantum_ms) {

       struct sigaction sa;

       struct itimerval timer;

       // ... 设置定时器和信号处理

       setitimer(ITIMER_REAL, &timer, NULL);

   }

使用setitimer设置定时器

定时器触发时调用timer_interrupt_handler

在中断处理中执行调度决策

  1. 公平性

PSJF通过时间片计数保证短作业优先

MLFQ通过多级队列和优先级调整实现公平调度

  1. 效率

使用链表实现队列,支持O(1)的入队和出队操作

上下文切换使用系统提供的ucontext函数,保证可靠性

  1. 可扩展性

调度策略可以通过修改current_policy轻松切换

队列级数可通过修改MLFQ_LEVELS调整

总结

  1. 通过实现TCB(线程控制块)和线程队列,理解了线程的生命周期管理和状态转换。用户级线程的实现让我明白了线程切换的底层原理,以及用户态和内核态的区别。
  2. 实现PSJF(抢占式最短作业优先)和MLFQ(多级反馈队列)两种调度算法,加深了对进程调度策略的理解。特别是MLFQ的实现,展示了如何平衡系统响应性和公平性。
  3. 使用ucontext系列函数实现上下文切换,让我理解了程序执行状态的保存和恢复机制。这也加深了对程序栈、程序计数器等概念的认识。
  4. 在实现过程中接触到程序的内存布局(堆、栈、数据段、代码段、BSS段),理解了线程栈空间的分配和管理方式。
  5. 通过实现互斥锁,深入理解了线程同步的重要性和实现原理,以及如何避免死锁等并发问题。也顺便巩固了c++的unique_lock,lock_guard,mutex,condition_variable的使用

你可能感兴趣的:(C++,linux,c语言)