原文: https://www.toutiao.com/a6808804287469060612/?tt_from=mobile_qq&utm_campaign=client_share&timestamp=1585487900&app=news_article_social&utm_source=mobile_qq&utm_medium=toutiao_ios&req_id=202003292118200100140261301B6520C4&group_id=6808804287469060612

 #include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
// 仅仅是测试demo,分配4096字节的stack足够了。
#define STACK_SIZE		4096
/*
 * 为什么是72?
 * 因为我们在信号处理中增加了一个局部变量,这样pretcode的偏移就是32字节了。
 * 于是32+40=72!
 */
#define CONTEXT_OFFSET	72
// rip寄存器相对于局部变量a的偏移。注意rip在sigcontext中的偏移是16
#define PC_OFFSET		200
#define SCHEDULE_INTERVAL 1
int wait_start()
{
	for (;;) {
		sleep(1000);
	}
}

void thread1()
{
	int i = 1;
	while (1) {
		printf("I am thread:%d\n", i);
		sleep(1);
	}
}

void thread2()
{
	int i = 2;
	while (1) {
		printf("I am thread:%d\n", i);
		sleep(1);
	}
}

unsigned char *buf;
int start = 0;
struct sigcontext context[2];
struct sigcontext *curr_con;
unsigned long pc[2];
int idx = 0;
unsigned char *stack1, *stack2;
// SIGINT用来启动所有线程,每次信号启动一个。
void sig_start(int dunno)
{
	unsigned long a = 0, *p;
	if (start == 0) {  // 启动第一个线程
		// 首先定位到sigcontext的rip,启动线程仅仅修改rip即可,目标是跳入到thread1线程处理函数
		p = (unsigned long*)((unsigned char *)&a + PC_OFFSET);
		*p = pc[0];
		// 定位到sigcontext
		p = (unsigned long *)((unsigned char *)&a + CONTEXT_OFFSET);
		curr_con = (struct sigcontext *)p;
		// 初始化其堆栈寄存器为为该线程分配的独立堆栈空间。
		curr_con->rsp = curr_con->rbp = (unsigned long)((unsigned char *)stack1 + STACK_SIZE);
		start++;
	} else if (start == 1) { // 启动第二个线程
		// 定位线程1的sigcontext,保存其上下文,因为马上就要schedule线程2了。
		p = (unsigned long *)((unsigned char *)&a + CONTEXT_OFFSET);
		curr_con = (struct sigcontext *)p;
		memcpy((void *)&context[0], (const void *)curr_con, sizeof(struct sigcontext));
		// 保存第一个线程的上下文后再定位到sigcontext的rip并修改之,同线程1
		p = (unsigned long *)((char*)&a + PC_OFFSET);
		idx = 1;
		*p = pc[1];
		p = (unsigned long *)((unsigned char *)&a + CONTEXT_OFFSET);
		curr_con = (struct sigcontext *)p;
		// 初始化其堆栈寄存器为为该线程分配的独立堆栈空间。
		curr_con->rsp = curr_con->rbp = (unsigned long)((unsigned char *)stack2 + STACK_SIZE);
		start++;
		// 两个线程均启动完毕,开启时间片轮转调度吧。
		alarm(SCHEDULE_INTERVAL);
		signal(SIGINT, NULL);
	}
	return;
}
void sig_schedule(int unused)
{
	unsigned long a = 0;
	unsigned char *p;

	// 保存当前线程的上下文
	p = (unsigned char *)((unsigned char *)&a + CONTEXT_OFFSET);
	curr_con = (struct sigcontext *)p;
	memcpy((void *)&context[idx%2], curr_con, sizeof(struct sigcontext));

	// 轮转调度下一个线程,恢复其上下文。
	idx++;
	memcpy(curr_con, (void *)&context[idx%2], sizeof(struct sigcontext));
	// 2秒后再调度
	alarm(SCHEDULE_INTERVAL);
	return;
}
int main()
{
	printf("process id is %d  %p %p\n",getpid(), thread1, thread2);

	// 为两个线程分配stack空间。
	// 注意,线程的stack空间一定要独立,不然函数调用会冲突的。
	stack1 = (unsigned char *)calloc(1, 4096);
	stack2 = (unsigned char *)calloc(1, 4096);
	signal(SIGINT, sig_start);
	signal(SIGALRM, sig_schedule);
	pc[0] = (unsigned long)thread1;
	pc[1] = (unsigned long)thread2;
	wait_start();
}


在这里插入图片描述🔗

备份地址: 【Linux C实现纯用户态抢占式多线程