加载中…
个人资料
  • 博客等级:
  • 博客积分:
  • 博客访问:
  • 关注人气:
  • 获赠金笔:0支
  • 赠出金笔:0支
  • 荣誉徽章:
正文 字体大小:

【FreeRTOS操作系统教程】第26章 FreeRTOS任务事件标志组

(2016-09-07 16:51:52)
标签:

fatfs

freertos

stemwin

stm32

usb

分类: FreeRTOS

第26章      FreeRTOS任务事件标志组



本章节为大家讲解FreeRTOS事件标志组的另一种实现方式----基于任务通知(Task Notifications)的事件标志组,这里我们将这种方式实现的事件标志组称之为任务事件标志组。这种方式实现的事件标志组效率更高,需要的RAM空间更小。当然,缺点也是有的,它没有第18章介绍的事件标志组实现的功能全面。

本章教程配套的例子含Cortex-M3内核的STM32F103Cortex-M4内核的STM32F407以及F429

26.1 任务通知(Task Notifications)介绍

26.2 任务事件标志组

26.3 任务事件标志组API函数

26.4 实验例程说明(任务间通信)

26.5 实验例程说明(中断方式)

26.6       总结

 

 

26.1  任务通知(Task Notifications)介绍

(说明:第24,25,2627章这部分基础知识是相同的)

FreeRTOS每个已经创建的任务都有一个任务控制块(task control block),任务控制块就是一个结构体变量,用于记录任务的相关信息。结构体变量中有一个32位的变量成员ulNotifiedValue是专门用于任务通知的。

通过任务通知方式可以实现计数信号量,二值信号量,事件标志组和消息邮箱(消息邮箱就是消息队列长度为1的情况)。使用方法与前面章节讲解的事件标志组和信号量基本相同,只是换了不同的函数来实现。任务通知方式实现的计数信号量,二值信号量,事件标志组和消息邮箱是通过修改变量ulNotifiedValue实现的:

(1)设置接收任务控制块中的变量ulNotifiedValue可以实现消息邮箱。

(2)如果接收任务控制块中的变量ulNotifiedValue还没有被其接收到,也可以用新数据覆盖原有数据,这就是覆盖方式的消息邮箱。

(3)设置接收任务控制块中的变量ulNotifiedValuebit0-bit31数值可以实现事件标志组。

(4)设置接收任务控制块中的变量ulNotifiedValue数值进行加一或者减一操作可以实现计数信号量和二值信号量。

介绍了这么多,那么问题来了,采用这种方式有什么优势呢?根据官方的测试数据,唤醒由于信号量和事件标志组而处于阻塞态的任务,速度提升了45%,而且这种方式需要的RAM空间更小。但这种方式实现的信号量和事件标志组也有它的局限性,主要表现在以下两个方面:

(1)任务通知方式仅可以用在只有一个任务等待信号量,消息邮箱或者事件标志组的情况,不过实际项目项目中这种情况也是最多的

(2)使用任务通知方式实现的消息邮箱替代前面章节讲解的消息队列时,发送消息的任务不支持超时等待,即消息队列中的数据已经满了,可以等待消息队列有空间可以存新的数据,而任务通知方式实现的消息邮箱不支持超时等待。

26.2 任务事件标志组

前面第18章,我们对事件标志组进行了讲解。本章节讲解的任务事件标志组与第18章讲解的事件标志组要实现的功能是一样的,不同的是调用的函数和支持的事件标志个数,任务事件标志组支持32个事件标志设置,而第18章中介绍的事件标志组,每创建一个支持24个事件标志设置:

(1)任务事件标志组的事件标志位是通过任务控制块中的一个32位变量ulNotifiedValue实现。第18章讲解的事件标志组创建后会有自己可以设置的事件标志位。

(2)任务事件标志组是通过函数xTaskNotifyWait()替代第18章讲解的函数xEventGroupWaitBits()实现等待事件标志位被设置。

(3)任务事件标志组是通过函数xTaskNotify() xTaskNotifyFromISR()替代第18章讲解的函数xEventGroupSetBits() xEventGroupSetBitsFromISR实现对事件标志位的设置。

(4)第18章中讲解的函数xEventGroupSetBitsFromISR是通过给Daemon任务(定时器任务)发消息,在定时器任务中执行实际的操作,而我们本章节要介绍的函数xTaskNotifyFromISR是直接在中断服务程序里面执行操作,效率要高很多。

 

具体任务间任务事件标志组的实现过程和中断方式任务事件标志组的实现过程参看第18.1.2小节和18.1.3小节即可。实际项目中,如果使用事件标志组和任务事件标志组都能实现相应功能,强烈建议使用任务事件标志组。

26.3 任务事件标志组API函数

使用如下9个函数可以实现FreeRTOS的任务事件标志组:

      (1)xTaskNotifyGive()
(2)    vTaskNotifyGiveFromISR()
(3)    ulTaskNotifyTake()
(4)    xTaskNotify()
(5)    xTaskNotifyAndQuery()
(6)    xTaskNotifyAndQueryFromISR()
(7)    xTaskNotifyFromISR()
(8)    xTaskNotifyWait()
(9)    xTaskNotifyStateClear()

关于这9个函数的讲解及其使用方法可以看FreeRTOS在线版手册:

 

【FreeRTOS操作系统教程】第26章 <wbr>FreeRTOS任务事件标志组

 

这里我们重点的说以下3个函数:

(1)    xTaskNotify

(2)    xTaskNotifyFromISR

(3)    xTaskNotifyWait

因为本章节配套的例子使用的是这3个函数。

26.3.1   函数xTaskNotify

函数原型:

BaseType_t xTaskNotify( TaskHandle_t xTaskToNotify,

                         uint32_t ulValue,          

                         eNotifyAction eAction );  

函数描述:

函数xTaskNotify通过设置任务控制块中的变量ulNotifiedValue可以在任务代码中实现任务事件标志组,任务计数信号量,任务消息邮箱和任务二值信号量四种方式的消息通知(见26.1说明)。

u  1个参数是任务句柄。

u  2个参数是用来更新任务控制块中的32位变量ulNotifiedValue

u  3个参数是任务通知模式设置,支持以下5个参数:

 【FreeRTOS操作系统教程】第26章 <wbr>FreeRTOS任务事件标志组


u  返回值,根据上面第3个参数的说明,将其设置为eSetValueWithoutOverwrite,有可能返回pdFALSE,其余所有情况都返回值pdPASS

使用这个函数要注意以下问题:

1.     任务创建后,任务控制块中的变量ulNotifiedValue初始计数值是0

2.     默认配置此函数可以使用的的宏定义已经在FreeRTOS.h文件中使能:

#define  configUSE_TASK_NOTIFICATIONS        1

当然,如果用户不需要使用任务通知功能相关的函数,可以在FreeRTOSConfig.h文件中配置此宏定义为0来禁止,这样创建的每个任务可以节省8个字节的需求。

3.     此函数是用于任务代码中调用的,故不可以在中断服务程序中调用此函数,中断服务程序中使用的是xTaskNotifyFromISR

4.     根据FreeRTOS的建议,实现二值信号量和计数信号量时使用函数xTaskNotifyGive()替代此函数xTaskNotify()

使用举例:

static TaskHandle_t xHandleTaskMsgPro = NULL;

 

 

static void vTaskTaskUserIF(void *pvParameters)

{

     uint8_t ucKeyCode;

     uint8_t pcWriteBuffer[500];

    

 

    while(1)

    {

         ucKeyCode = bsp_GetKey();

        

         if (ucKeyCode != KEY_NONE)

         {

              switch (ucKeyCode)

              {

                  

                  

                   case KEY_DOWN_K2:

                       printf("K2键按下,任务vTaskMsgPro事件标志位bit0被置位 \r\n");

                       xTaskNotify(xHandleTaskMsgPro,

                                     BIT_0,             

                                     eSetBits);         

                       break;

                  

                  

                   case KEY_DOWN_K3:

                       printf("K3键按下,任务vTaskMsgPro事件标志位bit1被置位 \r\n");

                       xTaskNotify(xHandleTaskMsgPro,

                                     BIT_1,             

                                     eSetBits);        

                  

                  

                   default:                    

                       break;

              }

         }

        

         vTaskDelay(20);

     }

}

26.3.2  函数xTaskNotifyFromISR

函数原型:

 BaseType_t xTaskNotifyFromISR( TaskHandle_t xTaskToNotify,

                                uint32_t ulValue,          

                                eNotifyAction eAction,     

                             BaseType_t *pxHigherPriorityTaskWoken );

函数描述:

函数xTaskNotifyFromISR通过设置任务控制块中的变量ulNotifiedValue可以在中断服务程序中实现任务事件标志组,任务计数信号量,任务消息邮箱和任务二值信号量四种方式的消息通知(见26.1说明)。

u  1个参数是任务句柄。

u  2个参数是用来更新任务控制块中的32位变量ulNotifiedValue

u  3个参数是任务通知模式设置,支持以下5个参数:

【FreeRTOS操作系统教程】第26章 <wbr>FreeRTOS任务事件标志组


u  4个参数用于保存是否有高优先级任务准备就绪。如果函数执行完毕后,此参数的数值是pdTRUE,说明有高优先级任务要执行,否则没有。

u  返回值,根据上面第3个参数的说明,将其设置为eSetValueWithoutOverwrite,有可能返回pdFALSE,其余所有情况都返回值pdPASS

使用这个函数要注意以下问题:

1.     任务创建后,任务控制块中的变量ulNotifiedValue初始计数值是0

2.     默认配置此函数可以使用的的宏定义已经在FreeRTOS.h文件中使能:

#define  configUSE_TASK_NOTIFICATIONS        1

当然,如果用户不需要使用任务通知功能相关的函数,可以在FreeRTOSConfig.h文件中配置此宏定义为0来禁止,这样创建的每个任务可以节省8个字节的需求。

3.     此函数是用于中断服务程序中调用的,故不可以在任务代码中调用此函数,任务代码中使用的是xTaskNotify

4.     根据FreeRTOS的建议,实现二值信号量和计数信号量时使用函数vTaskNotifyGiveFromISR ()替代此函数xTaskNotifyFromISR ()

使用举例:

static TaskHandle_t xHandleTaskMsgPro = NULL;

 

 

static void TIM2_IRQHandler (void)

{

     BaseType_t xHigherPriorityTaskWoken = pdFALSE;

 

    

     ……

 

     xTaskNotifyFromISR(xHandleTaskMsgPro, 

                         BIT_0,    

                         eSetBits, 

                        &xHigherPriorityTaskWoken);

 

    

     portYIELD_FROM_ISR(xHigherPriorityTaskWoken);

}

 

26.3.3  函数xTaskNotifyWait

函数原型: 

BaseType_t xTaskNotifyWait(

 

uint32_t ulBitsToClearOnEntry, 

 

uint32_t ulBitsToClearOnExit,

 

uint32_t *pulNotificationValue, 

                    

TickType_t xTicksToWait );   

函数描述:

函数xTaskNotifyWait可以在任务代码中实现任务事件标志组,任务计数信号量,任务消息邮箱和任务二值信号量四种方式的消息获取(见26.1说明)。

u  1个参数ulBitsToClearOnEntry用于函数执行之前,将任务控制块中的变量ulNotifiedValue进行如下操作 :     

ulNotifiedValue &= ~ulBitsToClearOnEntry

简单的说就是参数ulBitsToClearOnEntry哪个位是1,那么变量ulNotifiedValue的那个位就会被清零。比如ulBitsToClearOnEntry = 0x01表示将变量ulNotifiedValuebit0清零,又比如ulBitsToClearOnEntry = 0xffffffff表示将变量ulNotifiedValue的所有位清零。

u  2个参数 ulBitsToClearOnExit用于函数退出前,将任务控制块中的变量ulNotifiedValue进行如下操作

ulNotifiedValue &= ~ ulBitsToClearOnExit

简单的说就是参数ulBitsToClearOnExit哪个位是1,那么变量ulNotifiedValue的那个位就会被清零。比如ulBitsToClearOnExit= 0x01表示将变量ulNotifiedValuebit0清零,又比如ulBitsToClearOnExit= 0xffffffff表示将变量ulNotifiedValue的所有位清零。

u  3个参数用于将任务控制块中的变量ulNotifiedValue保存到此参数指针所指向的存储单元。如果此参数没有用上,可以将其设置为NULL

u  4个参数是没有消息时,等待消息的最大等待时间,单位系统时钟节拍。

u  返回值,如果成功接收到消息返回pdTRUE,否则返回pdFALSE,比如在设置的超时时间内没有收到消息。

使用这个函数要注意以下问题:

1.     任务创建后,任务控制块中的变量ulNotifiedValue初始计数值是0

2.     默认配置此函数可以使用的的宏定义已经在FreeRTOS.h文件中使能:

#define  configUSE_TASK_NOTIFICATIONS        1

当然,如果用户不需要使用任务通知功能相关的函数,可以在FreeRTOSConfig.h文件中配置此宏定义为0来禁止,这样创建的每个任务可以节省8个字节的需求。

3.     如果用户将FreeRTOSConfig.h文件中的宏定义INCLUDE_vTaskSuspend配置为1且第2个参数配置为portMAX_DELAY,那么此函数会永久等待直到消息可用。

4.     根据FreeRTOS的建议,实现二值信号量和计数信号量时使用函数ulTaskNotifyTake ()替代此函数xTaskNotifyWait ()

使用举例:

 

static void vTaskMsgPro(void *pvParameters)

{

     BaseType_t xResult;

     const TickType_t xMaxBlockTime = pdMS_TO_TICKS(500);

     uint32_t ulValue;

    

    while(1)

    {

        

        

         xResult = xTaskNotifyWait(0x00000000,     

                                      0xFFFFFFFF,     

                                      &ulValue,        

                                      xMaxBlockTime); 

        

         if( xResult == pdPASS )

         {

             

              if((ulValue & BIT_0) != 0)

              {

                   printf("接收到K2按键按下消息, ulNotifiedValue = 0xx\r\n", ulValue);

        

              }

 

              if((ulValue & BIT_1) != 0)

              {

                   printf("接收到K3按键按下消息, ulNotifiedValue = 0xx\r\n", ulValue);

              }

         }

         else

         {

             

              bsp_LedToggle(1);

              bsp_LedToggle(4);

         }

    }

}

 

26.4实验例程说明(任务间通信)

26.4.1STM32F103开发板实验

配套例子:

V4-329_FreeRTOS实验_任务事件标注组

实验目的:

1.     学习FreeRTOS的任务通知实现事件标志组。

实验内容:

1.     K1按键按下,串口打印任务执行情况(波特率115200,数据位8,奇偶校验位无,停止位1)。

2.     K2键按下,直接发送事件标志设置给任务vTaskMsgPro,置位bit0

3.     K3键按下,直接发送事件标志设置给任务vTaskMsgPro,置位bit1

4.     各个任务实现的功能如下:

              vTaskUserIF任务 :按键消息处理。

              vTaskLED任务   LED闪烁。

              vTaskMsgPro任务:使用函数xTaskNotifyWait接收任务vTaskTaskUserIF发送的事件标志位设置。

              vTaskStart任务   :启动任务,也是最高优先级任务,这里实现按键扫描。

FreeRTOS的配置:

FreeRTOSConfig.h文件中的配置如下:

 

#if defined(__ICCARM__) || defined(__CC_ARM) || defined(__GNUC__)

 #include

 extern volatile uint32_t ulHighFrequencyTimerTicks;

#endif

 

#define configUSE_PREEMPTION         1

#define configUSE_IDLE_HOOK          0

#define configUSE_TICK_HOOK          0

#define configCPU_CLOCK_HZ           ( ( unsigned long ) 72000000 )  

#define configTICK_RATE_HZ           ( ( TickType_t ) 1000 )

#define configMAX_PRIORITIES         ( 5 )

#define configMINIMAL_STACK_SIZE     ( ( unsigned short ) 128 )

#define configTOTAL_HEAP_SIZE        ( ( size_t ) ( 17 * 1024 ) )

#define configMAX_TASK_NAME_LEN      ( 16 )

#define configUSE_TRACE_FACILITY      1

#define configUSE_16_BIT_TICKS       0

#define configIDLE_SHOULD_YIELD      1

 

 

#define configGENERATE_RUN_TIME_STATS                1

#define configUSE_STATS_FORMATTING_FUNCTIONS         1

#define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS()     (ulHighFrequencyTimerTicks = 0ul)

#define portGET_RUN_TIME_COUNTER_VALUE()             ulHighFrequencyTimerTicks

//#define portALT_GET_RUN_TIME_COUNTER_VALUE           1

 

 

#define configUSE_CO_ROUTINES            0

#define configMAX_CO_ROUTINE_PRIORITIES ( 2 )

 

 

 

#define INCLUDE_vTaskPrioritySet          1

#define INCLUDE_uxTaskPriorityGet         1

#define INCLUDE_vTaskDelete               1

#define INCLUDE_vTaskCleanUpResources      0

#define INCLUDE_vTaskSuspend              1

#define INCLUDE_vTaskDelayUntil           1

#define INCLUDE_vTaskDelay                1

 

 

#ifdef __NVIC_PRIO_BITS

    

     #define configPRIO_BITS              __NVIC_PRIO_BITS

#else

     #define configPRIO_BITS                   

#endif

 

 

#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY              0x0f

 

 

#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY         0x01

几个重要选项说明:

u  #define configUSE_PREEMPTION        1

使能抢占式调度器

u  #define configCPU_CLOCK_HZ     ( ( unsigned long ) 72000000 )   

系统主频72MHz

u  #define configTICK_RATE_HZ              ( ( TickType_t ) 1000 )

系统时钟节拍1KHz,即1ms

u  #define configMAX_PRIORITIES          ( 5 )

定义可供用户使用的最大优先级数,如果这个定义的是5,那么用户可以使用的优先级号是0,1,2,3,4不包含5,对于这一点,初学者要特别的注意。

u  #define configTOTAL_HEAP_SIZE        ( ( size_t ) ( 17 * 1024 ) )

定义堆大小,FreeRTOS内核,用户动态内存申请,任务栈等都需要用这个空间。

u  configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY          0x01

定义受FreeRTOS管理的最高优先级中断。简单的说就是允许用户在这个中断服务程序里面调用FreeRTOSAPI的最高优先级。为了进一步说明这个宏定义的的作用,解释如下:

l  使用CM内核的MCU,官方强烈建议将NVIC的优先级分组配置为全抢占式优先级,全部配置为抢占式优先级的好处就是方便管理。

l  对于STM32来说,设置NVIC的优先级分组为4时,NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4)就是全部配置为抢占式优先级。又因为STM32的优先级设置仅使用CM内核8bit中的高4bit,即只能区分2^4 = 16种优先级。因此当优先级分组设置为4的时候可供用户选择抢占式优先级为015,共16个优先级,配置为0表示最高优先级,配置为15表示最低优先级,不存在子优先级。

l  这里配置configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY0x01表示用户可以在抢占式优先级为115的中断里面调用FreeRTOSAPI函数,抢占式优先级为0的中断里面是不允许调用的。

更多关于这个参数说明请参看第12章。

FreeRTOS任务调试信息(按K1按键,串口打印):

【FreeRTOS操作系统教程】第26章 <wbr>FreeRTOS任务事件标志组

 

上面截图中打印出来的任务状态字母B, R, D, S对应如下含义:

#define tskBLOCKED_CHAR          ( 'B' )  任务阻塞

#define tskREADY_CHAR          ( 'R' )  任务就绪

#define tskDELETED_CHAR           ( 'D' )  任务删除

#define tskSUSPENDED_CHAR   ( 'S' )  任务挂起

程序设计:

u  任务栈大小分配:

vTaskUserIF任务   2048字节

vTaskLED任务     2048字节

vTaskMsgPro任务 :2048字节

vTaskStart任务    2048字节

任务栈空间是在任务创建的时候从FreeRTOSConfig.h文件中定义的heap空间中申请的

#define configTOTAL_HEAP_SIZE        ( ( size_t ) ( 17 * 1024 ) )

u  系统栈大小分配:

【FreeRTOS操作系统教程】第26章 <wbr>FreeRTOS任务事件标志组

 

u  FreeROTS初始化:

 

int main(void)

{

    

     __set_PRIMASK(1); 

    

    

     bsp_Init();

    

    

     vSetupSysInfoTest();

    

    

     AppTaskCreate();

    

   

    vTaskStartScheduler();

 

    

     while(1);

}

u  硬件外设初始化

硬件外设的初始化是在bsp.c文件实现:

 

void bsp_Init(void)

{

    

    

          NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);

 

     bsp_InitUart();   

     bsp_InitLed();    

     bsp_InitKey();    

}

u  FreeRTOS任务创建:

 

static void AppTaskCreate (void)

{

    xTaskCreate( vTaskTaskUserIF,  

                 "vTaskUserIF",    

                 512,              

                 NULL,             

                 1,                

                 &xHandleTaskUserIF );  /* 任务句柄  */

    

    

     xTaskCreate( vTaskLED,          

                 "vTaskLED",        

                 512,               

                 NULL,              

                 2,                 

                 &xHandleTaskLED );

    

     xTaskCreate( vTaskMsgPro,           

                 "vTaskMsgPro",          

                 512,                    

                 NULL,                   

                 3,                      

                 &xHandleTaskMsgPro );  /* 任务句柄  */

    

    

     xTaskCreate( vTaskStart,            

                 "vTaskStart",           

                 512,                    

                 NULL,                   

                 4,                      

                 &xHandleTaskStart );   

}

u  四个FreeRTOS任务的实现:

 

static void vTaskTaskUserIF(void *pvParameters)

{

     uint8_t ucKeyCode;

     uint8_t pcWriteBuffer[500];

    

 

    while(1)

    {

         ucKeyCode = bsp_GetKey();

        

         if (ucKeyCode != KEY_NONE)

         {

              switch (ucKeyCode)

              {

                  

                   case KEY_DOWN_K1:          

                       printf("=================================================\r\n");

                       printf("任务名      任务状态 优先级   剩余栈 任务序号\r\n");

                       vTaskList((char *)&pcWriteBuffer);

                       printf("%s\r\n", pcWriteBuffer);

                  

                       printf("\r\n任务名       运行计数         使用率\r\n");

                       vTaskGetRunTimeStats((char *)&pcWriteBuffer);

                       printf("%s\r\n", pcWriteBuffer);

                       break;

                  

                  

                   case KEY_DOWN_K2:

                       printf("K2键按下,任务vTaskMsgPro事件标志位bit0被置位 \r\n");

                       xTaskNotify(xHandleTaskMsgPro,

                                     BIT_0,             

                                     eSetBits);         

                       break;

                  

                  

                   case KEY_DOWN_K3:

                       printf("K3键按下,任务vTaskMsgPro事件标志位bit1被置位 \r\n");

                       xTaskNotify(xHandleTaskMsgPro,

                                     BIT_1,            

                                     eSetBits);        

                  

                  

                   default:                    

                       break;

              }

         }

        

         vTaskDelay(20);

     }

}

 

 

static void vTaskLED(void *pvParameters)

{

     TickType_t xLastWakeTime;

     const TickType_t xFrequency = 200;

 

    

    xLastWakeTime = xTaskGetTickCount();

    

    while(1)

    {

         bsp_LedToggle(2);

         bsp_LedToggle(3);

        

         

        vTaskDelayUntil(&xLastWakeTime, xFrequency);

    }

}

 

 

static void vTaskMsgPro(void *pvParameters)

{

     BaseType_t xResult;

     const TickType_t xMaxBlockTime = pdMS_TO_TICKS(500);

     uint32_t ulValue;

    

    while(1)

    {

        

        

         xResult = xTaskNotifyWait(0x00000000,     

                                      0xFFFFFFFF,     

                                      &ulValue,        

                                      xMaxBlockTime); 

        

         if( xResult == pdPASS )

         {

             

              if((ulValue & BIT_0) != 0)

              {

                   printf("接收到K2按键按下消息, ulNotifiedValue = 0xx\r\n", ulValue);

        

              }

 

              if((ulValue & BIT_1) != 0)

              {

                   printf("接收到K3按键按下消息, ulNotifiedValue = 0xx\r\n", ulValue);

              }

         }

         else

         {

             

              bsp_LedToggle(1);

              bsp_LedToggle(4);

         }

    }

}

 

 

static void vTaskStart(void *pvParameters)

{

    while(1)

    {

         

         bsp_KeyScan();

        vTaskDelay(10);

    }

}

0

阅读 收藏 喜欢 打印举报/Report
  

新浪BLOG意见反馈留言板 欢迎批评指正

新浪简介 | About Sina | 广告服务 | 联系我们 | 招聘信息 | 网站律师 | SINA English | 产品答疑

新浪公司 版权所有