C 伪线程一 - setjmp longjmp 验证

5 篇文章 2 订阅
本文通过使用setjmp和longjmp函数在单片机的裸机环境中模拟多线程,创建了状态机实现的任务切换。讨论了在不同数量的任务中如何进行任务调度,并尝试封装成延迟函数,但遇到函数调用导致的堆栈破坏问题。最后,文章揭示了这种实现方式的局限性。
摘要由CSDN通过智能技术生成

需求来源于困难,在单片机上我们一般都是跑逻裸机,主要就是单线程加中断的方式写代码。

我个人很不喜欢用状态机的方式写代码,会很多的状态变量和全局参数。

但是我在一些场合遇到了不得不用状态机的时候,我写起来又不想又很多的全局变量,还希望代码模块化。到最后把我自己的代码眶的超难受。所以就萌生了伪多线程的需求。

之前就知道 setjmp longjmp两个函数可以实现函数间跳转,所以便尝试研究起来。

测试一、3个任务成环

#include "stdio.h"
#include <setjmp.h>
#include "stdbool.h"
#include "stdlib.h"
#include "stdint.h"
#include "string.h"



jmp_buf bf1;
jmp_buf bf2;
jmp_buf bf3;

bool flag1 = false;

bool flag2 = false;

bool flag3 = false;
void zfun3();
void zfun2();

void zfun1()
{
	while (1)
	{

		printf("zfun1 执行\r\n");
		if (setjmp(bf1) != 0)
		{
			printf("\tfun1 被调\r\n");

		}
		else
		{
			flag1 = true;
			if (flag2)
			{
				longjmp(bf2, 1);
			}
			else
			{
				zfun2();

				printf("\tfun1 退出zfun2\r\n");
			}

		}
	}
}

void zfun2()
{
	while (true)
	{
		printf("zfun2 执行\r\n");
		if (setjmp(bf2) != 0)
		{
			printf("\tfun2 被调\r\n");

		}
		else
		{
			flag2 = true;
			if (flag3)
			{
				longjmp(bf3, 1);
			}
			else
			{
				zfun3();
				printf("\tzfun2 退出zfun3\r\n");
			}
		}
	}
}

void zfun3()
{
	while (true)
	{
		printf("zfun3 执行\r\n");
		if (setjmp(bf3) != 0)
		{
			printf("\tfun3 被调\r\n");

		}
		else
		{
			flag3 = true;
			if (flag1)
			{
				longjmp(bf1, 1);
			}
			else
			{
				zfun1();
				printf("\tzfun2 退出zfun1\r\n");
			}
		}
	}
}

int main()
{

	printf_s("main 1\r\n");

	//for (int i = 0; i < 10;i++)
	zfun1();


	printf_s("执行结束\r\n");

	getchar();
}

测试二、试着开个口子


#include "stdio.h"
#include <setjmp.h>
#include "stdbool.h"
#include "stdlib.h"
#include "stdint.h"
#include "string.h"


jmp_buf bf1;
jmp_buf bf2;
jmp_buf bf3;

bool flag1 = false;

bool flag2 = false;

bool flag3 = false;
void zfun3();
void zfun2();

void zfun1()
{
	//while (1)
	{

		printf("zfun1 执行\r\n");
		if (setjmp(bf1) != 0)
		{
			printf("\tfun1 被调\r\n");

		}
		else
		{
			flag1 = true;
			if (flag2)
			{
				longjmp(bf2, 1);
			}
			else
			{
				zfun2();

				printf("\tfun1 退出zfun2\r\n");
			}

		}
	}
}

void zfun2()
{
	while (true)
	{
		printf("zfun2 执行\r\n");
		if (setjmp(bf2) != 0)
		{
			printf("\tfun2 被调\r\n");

		}
		else
		{
			flag2 = true;
			if (flag3)
			{
				longjmp(bf3, 1);
			}
			else
			{
				zfun3();
				printf("\tzfun2 退出zfun3\r\n");
			}
		}
	}
}

void zfun3()
{
	while (true)
	{
		printf("zfun3 执行\r\n");
		if (setjmp(bf3) != 0)
		{
			printf("\tfun3 被调\r\n");

		}
		else
		{
			flag3 = true;
			if (flag1)
			{
				longjmp(bf1, 1);
			}
			else
			{
				zfun1();
				printf("\tzfun2 退出zfun1\r\n");
			}
		}
	}
}



int main()
{

	printf_s("main 1\r\n");

	//for (int i = 0; i < 10;i++)
	while (true)
	{
		zfun1();

	}


	printf_s("执行结束\r\n");

	getchar();
}

测试三、再添加一个任务


#include "stdio.h"
#include <setjmp.h>
#include "stdbool.h"
#include "stdlib.h"
#include "stdint.h"
#include "string.h"


jmp_buf bf1;
jmp_buf bf2;
jmp_buf bf3;
jmp_buf bf4;

bool flag1 = false;

bool flag2 = false;

bool flag3 = false;


bool flag4 = false;

void zfun3();
void zfun2();
void zfun4();

void zfun1()
{
	//while (1)
	{

		printf("zfun1 执行\r\n");
		if (setjmp(bf1) != 0)
		{
			printf("\tfun1 被调\r\n");

		}
		else
		{
			flag1 = true;
			if (flag2)
			{
				longjmp(bf2, 1);
			}
			else
			{
				zfun2();

				printf("\tfun1 退出zfun2\r\n");
			}

		}
	}
}

void zfun2()
{
	while (true)
	{
		printf("zfun2 执行\r\n");
		if (setjmp(bf2) != 0)
		{
			printf("\tfun2 被调\r\n");

		}
		else
		{
			flag2 = true;
			if (flag3)
			{
				longjmp(bf3, 1);
			}
			else
			{
				zfun3();
				printf("\tzfun2 退出zfun3\r\n");
			}
		}
	}
}

void zfun3()
{
	while (true)
	{
		printf("zfun3 执行\r\n");
		if (setjmp(bf3) != 0)
		{
			printf("\tfun3 被调\r\n");

		}
		else
		{
			flag3 = true;
			if (flag4)
			{
				longjmp(bf4, 1);
			}
			else
			{
				zfun4();
				printf("\tzfun3 退出zfun4\r\n");
			}
		}
	}
}


void zfun4()
{
	while (true)
	{
		printf("zfun4 执行\r\n");
		if (setjmp(bf4) != 0)
		{
			printf("\tfun4 被调\r\n");

		}
		else
		{
			flag4 = true;
			if (flag1)
			{
				longjmp(bf1, 1);
			}
			else
			{
				zfun1();
				printf("\tzfun4 退出zfun1\r\n");
			}
		}
	}
}



int main()
{

	printf_s("main 1\r\n");

	//for (int i = 0; i < 10;i++)
	while (true)
	{
		zfun1();

	}


	printf_s("执行结束\r\n");

	getchar();
}

 测试四、5个线程调用正常


#include "stdio.h"
#include <setjmp.h>
#include "stdbool.h"
#include "stdlib.h"
#include "stdint.h"
#include "string.h"


jmp_buf bf1;
jmp_buf bf2;
jmp_buf bf3;
jmp_buf bf4;
jmp_buf bf5;

bool flag1 = false;

bool flag2 = false;

bool flag3 = false;


bool flag4 = false;

bool flag5 = false;

void zfun3();
void zfun2();
void zfun4();
void zfun5();



void zfun1()
{
	//while (1)
	{

		printf("zfun1 执行\r\n");
		if (setjmp(bf1) != 0)
		{
			

		}
		else
		{
			flag1 = true;
			if (flag2)
			{
				longjmp(bf2, 1);
			}
			else
			{
				zfun2();

				printf("\tfun1 退出zfun2\r\n");
			}

		}
	}
}

void zfun2()
{
	static int i = 0;
	while (true)
	{
		printf("zfun2 执行 %d \r\n",i++);
		if (setjmp(bf2) != 0)
		{
			

		}
		else
		{
			flag2 = true;
			if (flag3)
			{
				longjmp(bf3, 1);
			}
			else
			{
				zfun3();
				printf("\tzfun2 退出zfun3\r\n");
			}
		}
	}
}

void zfun3()
{
	static int i = 0;
	while (true)
	{
		printf("zfun3 执行 %d \r\n", i++);
		if (setjmp(bf3) != 0)
		{
			

		}
		else
		{
			flag3 = true;
			if (flag4)
			{
				longjmp(bf4, 1);
			}
			else
			{
				zfun4();
				printf("\tzfun3 退出zfun4\r\n");
			}
		}
	}
}


void zfun4()
{
	static int i = 0;
	while (true)
	{
		printf("zfun4 执行 %d \r\n", i++);
		if (setjmp(bf4) != 0)
		{
			

		}
		else
		{
			flag4 = true;
			if (flag5)
			{
				longjmp(bf5, 1);
			}
			else
			{
				zfun5();
				printf("\tzfun4 退出zfun5\r\n");
			}
		}
	}
}

void zfun5()
{
	static int i = 0;
	while (true)
	{
		printf("zfun5 执行 %d \r\n", i++);
		if (setjmp(bf5) != 0)
		{
			

		}
		else
		{
			flag5 = true;
			if (flag1)
			{
				longjmp(bf1, 1);
			}
			else
			{
				zfun1();
				printf("\tzfun5 退出zfun1\r\n");
			}
		}
	}
}



int main()
{

	printf_s("main 1\r\n");

	for (int i = 0; i < 10;i++)
	{
		zfun1();
	}


	printf_s("执行结束\r\n");

	getchar();
}

测试五、封装

delay采用宏没问题,改成函数就会报错


#define delay()                                   \
	{                                             \
		if (setjmp(context[ctx_id]->bf1) != 0)    \
		{                                         \
		}                                         \
		else                                      \
		{                                         \
			context[ctx_id]->flag1 = true;        \
			ctx_id++;                             \
			if (ctx_id >= 4)                      \
				ctx_id = 0;                       \
                                                  \
			if (context[ctx_id]->flag1)           \
			{                                     \
				longjmp(context[ctx_id]->bf1, 1); \
			}                                     \
			else                                  \
			{                                     \
				context[ctx_id]->zfun();          \
			}                                     \
		}                                         \
	}

这样就正常了


#include "stdio.h"
#include <setjmp.h>
#include "stdbool.h"
#include "stdlib.h"
#include "stdint.h"
#include "string.h"


jmp_buf bf1;
jmp_buf bf2;
jmp_buf bf3;
jmp_buf bf4;
jmp_buf bf5;

bool flag1 = false;

bool flag2 = false;

bool flag3 = false;


bool flag4 = false;

bool flag5 = false;

void zfun1();
void zfun3();
void zfun2();
void zfun4();
void zfun5();


typedef struct
{
	jmp_buf bf1;
	bool flag1;
	void(*zfun)();
}_context_t, *context_t;


context_t context[5];

int ctx_index = 0;

int ctx_id = 0;


void CreatTask(void(*fun)())
{

	context_t ctx = (context_t)malloc(sizeof(_context_t));
	memset(ctx, 0, sizeof(_context_t));

	ctx->zfun = fun;

	context[ctx_index++] = ctx;

}



void thread_init()
{
	CreatTask(zfun1);
	CreatTask(zfun2);
	CreatTask(zfun3);
	CreatTask(zfun4);
	CreatTask(zfun5);



	ctx_id = 0;

	zfun1();//首次启动

}

#define delay()\
{\
if (setjmp(context[ctx_id]->bf1) != 0)\
{\
	\
	\
}\
	else\
{\
	context[ctx_id]->flag1 = true; \
	ctx_id++; \
if (ctx_id >= 4)\
	ctx_id = 0; \
	\
if (context[ctx_id]->flag1)\
{\
	longjmp(context[ctx_id]->bf1, 1); \
}\
		else\
{\
	context[ctx_id]->zfun(); \
}\
}\
}


void delay1()
{

	if (setjmp(bf1) != 0)
	{


	}
	else
	{
		flag1 = true;
		if (flag2)
		{
			longjmp(bf2, 1);
		}
		else
		{
			zfun2();

			printf("\tfun1 退出zfun2\r\n");
		}

	}
}

void delay2()
{
	if (setjmp(bf2) != 0)
	{


	}
	else
	{
		flag2 = true;
		if (flag3)
		{
			longjmp(bf3, 1);
		}
		else
		{
			zfun3();
			printf("\tzfun2 退出zfun3\r\n");
		}
	}
}



void zfun1()
{
	//while (1)
	{

		printf("zfun1 执行\r\n");

		delay();

	}
}

void zfun2()
{
	static int i = 0;
	while (true)
	{
		printf("zfun2 执行 %d \r\n", i++);

		delay();

		printf("zfun2 执行 END %d \r\n", i++);
	}
}

void zfun3()
{
	static int i = 0;
	while (true)
	{
		printf("zfun3 执行 %d \r\n", i++);
		delay();
	}
}


void zfun4()
{
	static int i = 0;
	while (true)
	{
		printf("zfun4 执行 %d \r\n", i++);
		delay();
	}
}

void zfun5()
{
	static int i = 0;
	while (true)
	{
		printf("zfun5 执行 %d \r\n", i++);
		delay();
	}
}



int main()
{

	printf_s("main 1\r\n");

	thread_init();

	for (int i = 0; i < 10; i++)
	{
		zfun1();
	}


	printf_s("执行结束\r\n");

	getchar();
}

结论 

        这次的验证说明 delay函数调用会破坏保存的堆栈结构

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值