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_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
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最小的线程
这个线程被认为是"最短作业"
如果找到更短的作业,就进行抢占
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
在中断处理中执行调度决策
PSJF通过时间片计数保证短作业优先
MLFQ通过多级队列和优先级调整实现公平调度
使用链表实现队列,支持O(1)的入队和出队操作
上下文切换使用系统提供的ucontext函数,保证可靠性
调度策略可以通过修改current_policy轻松切换
队列级数可通过修改MLFQ_LEVELS调整