任务场景:现在我们很多iic访问的接口,像读光模块信息,读取芯片温度,eeprom读写信息,而接口上每个口都插有光模块,现在我们需要对光模块进行读模块信息和写告警门限两种操作,而光模块信息和芯片温度我们是需要监控的,告警门限值我们是通过命令行的形式去下发的,eeprom的信息我们是需要手动去写和读取的,所以这个时候如果我们用多进程的时候,同时去读写iic,可以会出现读写数据错误的情况,这个时候我们首先想到的去加进程锁,这种情况当然是可以的,如果加进程锁,可以参考我的linux进程锁举例说明_linux 进程锁-CSDN博客
下面我来说一下我的第二种实现,把多进程访问同一资源改成单进程访问同一资源,我们来看一下下面的示例代码,使用freertos平台实现的:
#include "FreeRTOS.h"
#include "task.h"
#include "semphr.h"
/* Priorities at which the tasks are created. */
#define mainQUEUE_RECEIVE_TASK_PRIORITY ( tskIDLE_PRIORITY + 2 )
#define mainQUEUE_SEND_TASK_PRIORITY ( tskIDLE_PRIORITY + 1 )
/* The rate at which data is sent to the queue. The 200ms value is converted
to ticks using the portTICK_PERIOD_MS constant. */
#define mainQUEUE_SEND_FREQUENCY_MS pdMS_TO_TICKS( 1000 )
/* The number of items the queue can hold. This is 1 as the receive task
will remove items as they are added, meaning the send task should always find
the queue empty. */
#define mainQUEUE_LENGTH ( 1 )
static QueueHandle_t xQueue = NULL;
int dal_temp_threshold_mode_set(int port, void *pvParameters)
{
int iic_id = port;
int msg = *((int*)pvParameters);
pthread_mutex_lock(&iic_lock);
ret = cpld_iic_write(iic_id, &msg);//此处涉及到cpld 模拟iic ,有通道切换,所以需要用到锁
pthread_mutex_unlock(&iic_lock);
return ret;
}
int dal_get_port_message(int port, void *pvParameters)
{
int iic_id = port;
pthread_mutex_lock(&iic_lock);
ret = cpld_iic_read(iic_id, pvParameters);//此处涉及到cpld 模拟iic ,有通道切换,所以需要用到锁
pthread_mutex_unlock(&iic_lock);
return ret;
}
//此两个任务是为了多进程里面set 硬件所引入的队列,因为set一般是网管下发的数据,所以通过下发的数据加入队列,通过队列来发送数据,处理轮询的进程收到此消息
后可以处理sfp_set ,从而避免了多进程访问引入的iic 读写异常情况。
static int IIC_Set_Task( void *pvParameters )
{
uint32_t ulReceivedValue;
const uint32_t TempHighLimit = 80UL;
int port = dal_get_gport();
int ret = 0;
for( ;; )
{
xQueueReceive( xQueue, &ulReceivedValue, portMAX_DELAY );
switch(ulReceivedValue)
case TempHighLimit:
ret = dal_temp_threshold_mode_set(port,&TempHighLimit);
break;
defalut:
break;
}
}
return ret;
}
static int IIC_Get_Task( void *pvParameters )
{
int port = dal_get_gport();
int ret = 0;
for( ;; )
{
ret = dal_get_port_message(port, pvParameters);
}
return ret;
}
//此处是iic set 的入口,提供给应用层调用。
static void prvQueuesendPara( void *pvParameters )
{
TickType_t xNextWakeTime;
uint32_t ulValueToSend = *((int*)pvParameters);
xNextWakeTime = xTaskGetTickCount();
vTaskDelayUntil( &xNextWakeTime, mainQUEUE_SEND_FREQUENCY_MS );
xQueueSend(xQueue, &ulValueToSend, 0U );
}
void main(void)
{
xQueue = xQueueCreate(mainQUEUE_LENGTH, sizeof( uint32_t ) );
BaseType_t xReturn = pdPASS;/* 定义一个创建信息返回值,默认为pdPASS */
taskENTER_CRITICAL(); //进入临界区
/* 创建IIC_Get_Task任务 */
xReturn = xTaskCreate((TaskFunction_t )IIC_Get_Task, /* 任务入口函数 */
(const char* )"IIC_Get_Task",/* 任务名字 */
(uint16_t )512, /* 任务栈大小 */
(void* )NULL, /* 任务入口函数参数 */
(UBaseType_t )2, /* 任务的优先级 */
NULL);/* 任务控制块指针 */
if(pdPASS == xReturn)
printf("xTaskCreate IIC_Get_Task SUCCESS!\r\n");
/* 创建SFP_Set_Task任务 */
xReturn = xTaskCreate((TaskFunction_t )IIC_Set_Task, /* 任务入口函数 */
(const char* )"IIC_Set_Task",/* 任务名字 */
(uint16_t )512, /* 任务栈大小 */
(void* )NULL, /* 任务入口函数参数 */
(UBaseType_t )3, /* 任务的优先级 */
NULL);/* 任务控制块指针 */
if(pdPASS == xReturn)
printf("xTaskCreate IIC_Set_Task SUCCESS!\r\n");
taskEXIT_CRITICAL(); //退出临界区
}
在同一进程中创建两个任务,两个任务分别执行iic的读和写操作,而读采用轮询的情况,写操作虽然也轮询的,但写是由收到的TempHighLimit消息类型来控制写的次数,也就是说只有上层下发写的消息后队列收到写的消息时才进行写操作。