freeRTOS源碼解析4--tasks.c 4

来源:https://www.cnblogs.com/freeManX1807/p/18403493
-Advertisement-
Play Games

在Linux系統中,磁碟大小和文件系統大小是兩個不同的概念,它們之間存在明顯的區別。以下是對這兩個概念的詳細解析: 磁碟大小 定義: 磁碟大小指的是物理存儲設備的總容量,即硬碟或固態硬碟(SSD)等存儲介質上能夠存儲數據的總空間。這個大小是固定的,由磁碟的製造工藝和規格決定。 特點: 固定性:磁碟一 ...


4.2.9 周期任務用的延遲--xTaskDelayUntil

介面:BaseType_t xTaskDelayUntil( TickType_t * const pxPreviousWakeTime, const TickType_t xTimeIncrement )

形參1:pxPreviousWakeTime,上一次喚醒時間,第一次需要用介面xTaskGetTickCount()獲取;
形參2:xTimeIncrement,想要延遲的時間。

返回值:用於判讀任務是否確實需要delay。

 1 BaseType_t xTaskDelayUntil( TickType_t * const pxPreviousWakeTime,
 2                                 const TickType_t xTimeIncrement )
 3 {
 4     TickType_t xTimeToWake;
 5     BaseType_t xAlreadyYielded, xShouldDelay = pdFALSE;
 6 
 7     configASSERT( pxPreviousWakeTime );
 8     configASSERT( ( xTimeIncrement > 0U ) );
 9 
10     vTaskSuspendAll();
11     {
12         /* Minor optimisation. The tick count cannot change in this
13          * block. */
14         const TickType_t xConstTickCount = xTickCount;
15 
16         configASSERT( uxSchedulerSuspended == 1U );
17 
18         /* Generate the tick time at which the task wants to wake. */
19         /* 計算任務下次喚醒的時間 */
20         xTimeToWake = *pxPreviousWakeTime + xTimeIncrement;
21 
22         if( xConstTickCount < *pxPreviousWakeTime )
23         {
24             /* The tick count has overflowed since this function was
25              * lasted called.  In this case the only time we should ever
26              * actually delay is if the wake time has also  overflowed,
27              * and the wake time is greater than the tick time.  When this
28              * is the case it is as if neither time had overflowed. */
29             /* tick值已經溢出了, 只有xTimeToWake也溢出並大於xConstTickCount
30              * 時, 才需要延遲 */
31             /* 假設tick是8位的, 當前值為0x5(從FF加到5), pxPreviousWakeTime為0xFD,
32              * 當xTimeIncrement為1時, xTimeToWake未溢出, 當xTimeIncrement為0x15時,
33              * xTimeToWake溢出。 顯然當xTimeIncrement為1時, xTimeToWake比
34              * pxPreviousWakeTime和xConstTickCount都大, 但不需要延遲, 因為tick已過,
35              * 當xTimeIncrement為0x15時, xTimeToWake比pxPreviousWakeTime小但比xConstTickCount
36              * 大, tick要等到0x12才到延遲時間, 所以需要進行延遲. */
37             if( ( xTimeToWake < *pxPreviousWakeTime ) && ( xTimeToWake > xConstTickCount ) )
38             {
39                 xShouldDelay = pdTRUE;
40             }
41             else
42             {
43                 mtCOVERAGE_TEST_MARKER();
44             }
45         }
46         else
47         {
48             /* The tick time has not overflowed.  In this case we will
49              * delay if either the wake time has overflowed, and/or the
50              * tick time is less than the wake time. */
51             /* tick沒有溢出, 那麼xTimeToWake溢出或比xConstTickCount小,
52              * 必然是需要延遲的. */
53             if( ( xTimeToWake < *pxPreviousWakeTime ) || ( xTimeToWake > xConstTickCount ) )
54             {
55                 xShouldDelay = pdTRUE;
56             }
57             else
58             {
59                 mtCOVERAGE_TEST_MARKER();
60             }
61         }
62 
63         /* Update the wake time ready for the next call. */
64         *pxPreviousWakeTime = xTimeToWake;
65 
66         if( xShouldDelay != pdFALSE )
67         {
68             /* prvAddCurrentTaskToDelayedList() needs the block time, not
69              * the time to wake, so subtract the current tick count. */
70             /* 延遲列表記錄的是阻塞時間而不是喚醒時間, 所以需要減去tick. */
71             prvAddCurrentTaskToDelayedList( xTimeToWake - xConstTickCount, pdFALSE );
72         }
73         else
74         {
75             mtCOVERAGE_TEST_MARKER();
76         }
77     }
78     xAlreadyYielded = xTaskResumeAll();
79 
80     /* Force a reschedule if xTaskResumeAll has not already done so, we may
81      * have put ourselves to sleep. */
82     if( xAlreadyYielded == pdFALSE )
83     {
84         taskYIELD_WITHIN_API();
85     }
86     else
87     {
88         mtCOVERAGE_TEST_MARKER();
89     }
90 
91     return xShouldDelay;
92 }
xTaskDelayUntil

 4.2.10 成對的信息獲取介面,一般的和帶有FromISR尾碼的

這類介面比較簡單,主要用於獲取TCB中的某些參數,這裡挑選任務優先順序獲取介面為例,重點在於帶有FromISR的介面。一般的介面很簡單,這裡就直接放出來即可。

 1 UBaseType_t uxTaskPriorityGet( const TaskHandle_t xTask )
 2 {
 3     TCB_t const * pxTCB;
 4     UBaseType_t uxReturn;
 5 
 6     taskENTER_CRITICAL();
 7     {
 8         /* If null is passed in here then it is the priority of the task
 9          * that called uxTaskPriorityGet() that is being queried. */
10         pxTCB = prvGetTCBFromHandle( xTask );
11         uxReturn = pxTCB->uxPriority;
12     }
13     taskEXIT_CRITICAL();
14 
15     return uxReturn;
16 }
 1 UBaseType_t uxTaskPriorityGetFromISR( const TaskHandle_t xTask )
 2 {
 3     TCB_t const * pxTCB;
 4     UBaseType_t uxReturn;
 5     UBaseType_t uxSavedInterruptStatus;
 6 
 7     /* RTOS ports that support interrupt nesting have the concept of a
 8      * maximum  system call (or maximum API call) interrupt priority.
 9      * Interrupts that are  above the maximum system call priority are keep
10      * permanently enabled, even when the RTOS kernel is in a critical section,
11      * but cannot make any calls to FreeRTOS API functions.  If configASSERT()
12      * is defined in FreeRTOSConfig.h then
13      * portASSERT_IF_INTERRUPT_PRIORITY_INVALID() will result in an assertion
14      * failure if a FreeRTOS API function is called from an interrupt that has
15      * been assigned a priority above the configured maximum system call
16      * priority.  Only FreeRTOS functions that end in FromISR can be called
17      * from interrupts  that have been assigned a priority at or (logically)
18      * below the maximum system call interrupt priority.  FreeRTOS maintains a
19      * separate interrupt safe API to ensure interrupt entry is as fast and as
20      * simple as possible.  More information (albeit Cortex-M specific) is
21      * provided on the following link:
22      * https://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html */
23     /* 檢查調用帶有FromISR尾碼的中斷優先順序是否高於
24      * configMAX_SYSCALL_INTERRUPT_PRIORITY, 是的話會進入assert */
25     portASSERT_IF_INTERRUPT_PRIORITY_INVALID();
26 
27     /* MISRA Ref 4.7.1 [Return value shall be checked] */
28     /* More details at: https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/main/MISRA.md#dir-47 */
29     /* coverity[misra_c_2012_directive_4_7_violation] */
30     /* 獲取當前中斷屏蔽優先順序, 並設置中斷屏蔽優先順序為configMAX_SYSCALL_INTERRUPT_PRIORITY
31      * 相當於進入了臨界區並返回了當前中斷屏蔽優先順序 */
32     uxSavedInterruptStatus = ( UBaseType_t ) taskENTER_CRITICAL_FROM_ISR();
33     {
34         /* If null is passed in here then it is the priority of the calling
35          * task that is being queried. */
36         pxTCB = prvGetTCBFromHandle( xTask );
37         uxReturn = pxTCB->uxPriority;
38     }
39     /* 將保存的中斷屏蔽優先順序設置回去, 相當於退出臨界區 */
40     taskEXIT_CRITICAL_FROM_ISR( uxSavedInterruptStatus );
41 
42     return uxReturn;
43 }

4.2.11 設置任務優先順序--vTaskPrioritySet

介面雖然較長,但註釋較多,且邏輯較為簡單,直接看代碼里的註釋即可。

  1 void vTaskPrioritySet( TaskHandle_t xTask, UBaseType_t uxNewPriority )
  2 {
  3     TCB_t * pxTCB;
  4     UBaseType_t uxCurrentBasePriority, uxPriorityUsedOnEntry;
  5     BaseType_t xYieldRequired = pdFALSE;
  6 
  7     configASSERT( uxNewPriority < configMAX_PRIORITIES );
  8 
  9     /* Ensure the new priority is valid. */
 10     if( uxNewPriority >= ( UBaseType_t ) configMAX_PRIORITIES )
 11     {
 12         uxNewPriority = ( UBaseType_t ) configMAX_PRIORITIES - ( UBaseType_t ) 1U;
 13     }
 14     else
 15     {
 16         mtCOVERAGE_TEST_MARKER();
 17     }
 18 
 19     taskENTER_CRITICAL();
 20     {
 21         /* If null is passed in here then it is the priority of the calling
 22          * task that is being changed. */
 23         pxTCB = prvGetTCBFromHandle( xTask );
 24 
 25         #if ( configUSE_MUTEXES == 1 )
 26         {
 27             uxCurrentBasePriority = pxTCB->uxBasePriority;
 28         }
 29         #else
 30         {
 31             uxCurrentBasePriority = pxTCB->uxPriority;
 32         }
 33         #endif
 34 
 35         if( uxCurrentBasePriority != uxNewPriority )
 36         {
 37             /* The priority change may have readied a task of higher
 38              * priority than a running task. */
 39             if( uxNewPriority > uxCurrentBasePriority )
 40             {
 41                 /* 提升優先順序 */
 42                 #if ( configNUMBER_OF_CORES == 1 )
 43                 {
 44                     if( pxTCB != pxCurrentTCB )
 45                     {
 46                         /* The priority of a task other than the currently
 47                          * running task is being raised.  Is the priority being
 48                          * raised above that of the running task? */
 49                         /* 設置的優先順序比當前運行的任務高, 需要yield */
 50                         if( uxNewPriority > pxCurrentTCB->uxPriority )
 51                         {
 52                             xYieldRequired = pdTRUE;
 53                         }
 54                         else
 55                         {
 56                             mtCOVERAGE_TEST_MARKER();
 57                         }
 58                     }
 59                     else
 60                     {
 61                         /* The priority of the running task is being raised,
 62                          * but the running task must already be the highest
 63                          * priority task able to run so no yield is required. */
 64                     }
 65                 }
 66                 #endif /* #if ( configNUMBER_OF_CORES == 1 ) */
 67             }
 68             else if( taskTASK_IS_RUNNING( pxTCB ) == pdTRUE )
 69             {
 70                 /* Setting the priority of a running task down means
 71                  * there may now be another task of higher priority that
 72                  * is ready to execute. */
 73                 /* 降低當前運行任務的優先順序, 則更高優先順序的就緒任務需要搶占 */
 74                 #if ( configUSE_TASK_PREEMPTION_DISABLE == 1 )
 75                     if( pxTCB->xPreemptionDisable == pdFALSE )
 76                 #endif
 77                 {
 78                     xYieldRequired = pdTRUE;
 79                 }
 80             }
 81             else
 82             {
 83                 /* Setting the priority of any other task down does not
 84                  * require a yield as the running task must be above the
 85                  * new priority of the task being modified. */
 86             }
 87 
 88             /* Remember the ready list the task might be referenced from
 89              * before its uxPriority member is changed so the
 90              * taskRESET_READY_PRIORITY() macro can function correctly. */
 91             /* 獲取任務當前的優先順序, 有可能是繼承來的優先順序 */
 92             uxPriorityUsedOnEntry = pxTCB->uxPriority;
 93 
 94             #if ( configUSE_MUTEXES == 1 )
 95             {
 96                 /* Only change the priority being used if the task is not
 97                  * currently using an inherited priority or the new priority
 98                  * is bigger than the inherited priority. */
 99                 /* 只有非繼承來的優先順序, 或提升優先順序才可以直接修改pxTCB->uxPriority */
100                 if( ( pxTCB->uxBasePriority == pxTCB->uxPriority ) || ( uxNewPriority > pxTCB->uxPriority ) )
101                 {
102                     pxTCB->uxPriority = uxNewPriority;
103                 }
104                 else
105                 {
106                     mtCOVERAGE_TEST_MARKER();
107                 }
108 
109                 /* The base priority gets set whatever. */
110                 pxTCB->uxBasePriority = uxNewPriority;
111             }
112             #else /* if ( configUSE_MUTEXES == 1 ) */
113             {
114                 pxTCB->uxPriority = uxNewPriority;
115             }
116             #endif /* if ( configUSE_MUTEXES == 1 ) */
117 
118             /* Only reset the event list item value if the value is not
119              * being used for anything else. */
120             /* xEventListItem的值未被使用, 才允許更新這個值 */
121             if( ( listGET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ) ) & taskEVENT_LIST_ITEM_VALUE_IN_USE ) == ( ( TickType_t ) 0U ) )
122             {
123                 listSET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ), ( ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) uxNewPriority ) );
124             }
125             else
126             {
127                 mtCOVERAGE_TEST_MARKER();
128             }
129 
130             /* If the task is in the blocked or suspended list we need do
131              * nothing more than change its priority variable. However, if
132              * the task is in a ready list it needs to be removed and placed
133              * in the list appropriate to its new priority. */
134             /* 如果任務處於就緒列表, 則需要將其放到修改後的優先順序就緒列表 */
135             if( listIS_CONTAINED_WITHIN( &( pxReadyTasksLists[ uxPriorityUsedOnEntry ] ), &( pxTCB->xStateListItem ) ) != pdFALSE )
136             {
137                 /* The task is currently in its ready list - remove before
138                  * adding it to its new ready list.  As we are in a critical
139                  * section we can do this even if the scheduler is suspended. */
140                 if( uxListRemove( &( pxTCB->xStateListItem ) ) == ( UBaseType_t ) 0 )
141                 {
142                     /* It is known that the task is in its ready list so
143                      * there is no need to check again and the port level
144                      * reset macro can be called directly. */
145                     portRESET_READY_PRIORITY( uxPriorityUsedOnEntry, uxTopReadyPriority );
146                 }
147                 else
148                 {
149                     mtCOVERAGE_TEST_MARKER();
150                 }
151 
152                 prvAddTaskToReadyList( pxTCB );
153             }
154             else
155             {
156                 #if ( configNUMBER_OF_CORES == 1 )
157                 {
158                     mtCOVERAGE_TEST_MARKER();
159                 }
160                 #endif
161             }
162 
163             if( xYieldRequired != pdFALSE )
164             {
165                 /* The running task priority is set down. Request the task to yield. */
166                 /* 開始yield, 但是在退出臨界區後才會進入sv中斷? */
167                 taskYIELD_TASK_CORE_IF_USING_PREEMPTION( pxTCB );
168             }
169             else
170             {
171                 mtCOVERAGE_TEST_MARKER();
172             }
173 
174             /* Remove compiler warning about unused variables when the port
175              * optimised task selection is not being used. */
176             ( void ) uxPriorityUsedOnEntry;
177         }
178     }
179     taskEXIT_CRITICAL();
180 }
vTaskPrioritySet

  補個流程圖

 

4.2.12 掛起任務--vTaskSuspend

還是直接看代碼註釋,解釋得比較清楚了,介面功能可以顧名思義,邏輯也還好不複雜。

  1 void vTaskSuspend( TaskHandle_t xTaskToSuspend )
  2 {
  3     TCB_t * pxTCB;
  4 
  5     taskENTER_CRITICAL();
  6     {
  7         /* If null is passed in here then it is the running task that is
  8          * being suspended. */
  9         pxTCB = prvGetTCBFromHandle( xTaskToSuspend );
 10 
 11         /* Remove task from the ready/delayed list and place in the
 12          * suspended list. */
 13         if( uxListRemove( &( pxTCB->xStateListItem ) ) == ( UBaseType_t ) 0 )
 14         {
 15             taskRESET_READY_PRIORITY( pxTCB->uxPriority );
 16         }
 17         else
 18         {
 19             mtCOVERAGE_TEST_MARKER();
 20         }
 21 
 22         /* Is the task waiting on an event also? */
 23         /* 任務掛起, 任務就不管是否在等待事件 */
 24         if( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) != NULL )
 25         {
 26             ( void ) uxListRemove( &( pxTCB->xEventListItem ) );
 27         }
 28         else
 29         {
 30             mtCOVERAGE_TEST_MARKER();
 31         }
 32 
 33         vListInsertEnd( &xSuspendedTaskList, &( pxTCB->xStateListItem ) );
 34 
 35         /* 如果在等待通知, 也直接退出等待 */
 36         #if ( configUSE_TASK_NOTIFICATIONS == 1 )
 37         {
 38             BaseType_t x;
 39 
 40             for( x = ( BaseType_t ) 0; x < ( BaseType_t ) configTASK_NOTIFICATION_ARRAY_ENTRIES; x++ )
 41             {
 42                 if( pxTCB->ucNotifyState[ x ] == taskWAITING_NOTIFICATION )
 43                 {
 44                     /* The task was blocked to wait for a notification, but is
 45                      * now suspended, so no notification was received. */
 46                     pxTCB->ucNotifyState[ x ] = taskNOT_WAITING_NOTIFICATION;
 47                 }
 48             }
 49         }
 50         #endif /* if ( configUSE_TASK_NOTIFICATIONS == 1 ) */
 51     }
 52     taskEXIT_CRITICAL();
 53 
 54     #if ( configNUMBER_OF_CORES == 1 )
 55     {
 56         UBaseType_t uxCurrentListLength;
 57 
 58         if( xSchedulerRunning != pdFALSE )
 59         {
 60             /* Reset the next expected unblock time in case it referred to the
 61              * task that is now in the Suspended state. */
 62             /* 如果下個解除阻塞的時間對應的任務正好是掛起的任務,
 63              * 無論如何重置一下下個解除阻塞的時間 */
 64             taskENTER_CRITICAL();
 65             {
 66                 prvResetNextTaskUnblockTime();
 67             }
 68             taskEXIT_CRITICAL();
 69         }
 70         else
 71         {
 72             mtCOVERAGE_TEST_MARKER();
 73         }
 74 
 75         if( pxTCB == pxCurrentTCB )
 76         {
 77             if( xSchedulerRunning != pdFALSE )
 78             {
 79                 /* The current task has just been suspended. */
 80                 /* 當前運行的任務掛起, 需要yield */
 81                 configASSERT( uxSchedulerSuspe

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 前言 推薦一款基於.NET 8、WPF、Prism.DryIoc、MVVM設計模式、Blazor以及MySQL資料庫構建的企業級工作流系統的WPF客戶端框架-AIStudio.Wpf.AClient 6.0。 項目介紹 框架採用了 Prism 框架來實現 MVVM 模式,不僅簡化了 MVVM 的典型 ...
  • docker安裝普羅米修斯+Granfan並監控容器 一、基本概念 ​ 1、之間的關係 ​ prometheus與grafana之間是相輔相成的關係。作為完美的分散式監控系統的Prometheus,就想布加迪威龍一樣示例和動力強勁。在猛的車也少不了儀錶盤來觀察。於是優雅的可視化平臺Grafana出現 ...
  • 大家好,我是痞子衡,是正經搞技術的痞子。今天痞子衡給大家分享的是JLink命令行以及JFlash對於下載演算法的作用地址範圍認定。 最近痞子衡在給一個 RT1170 客戶定製一個 Infineon MirrorBit 類型 64MB Flash 的 SEGGER 下載演算法,做完之後在 JFlash 下 ...
  • 寫在前面 本隨筆是非常菜的菜雞寫的。如有問題請及時提出。 可以聯繫:[email protected] GitHhub:https://github.com/WindDevil (目前啥也沒有 編程題 第一題 擴展內核,能夠顯示操作系統切換任務的過程。 首先先回憶一下操作系統切換任務的過程. 因此只 ...
  • 【新品發佈】正點原子ZYNQ7015開發板發佈!ZYNQ 7000系列、雙核ARM、PCIe2.0、SFPX2,性能強悍,資料豐富! 正點原子Z15 ZYNQ開發板,搭載Xilinx Zynq7000系列晶元,核心板主控晶元的型號是XC7Z015CLG485-2。開發板由核心板+底板組成,外設資源豐 ...
  • 編寫樹莓派內核映像的過程可以等同於gcc的編譯過程:預處理、編譯、彙編、鏈接,後面還會加一步:將可執行文件轉換成二進位的鏡像文件。 在MakeFile里的構建過程分為3步: 1.將.c文件經過預處理、編譯、彙編生成.o文件,將.S文件經過彙編生成.o文件。 2.將.o文件經過鏈接生成.elf可執行文 ...
  • 寫在前面 主要使用軟體: VMware Workstation Pro17 Navicat Premium17 Xshell7 Xftp7 1.在虛擬機安裝CentOS7 訪問阿裡雲鏡像站 ,選擇標記鏡像文件下載 打開虛擬機VMware,新建虛擬機 點擊“新CD/DVD”,“使用ISO映像文件”,設 ...
  • 大家好,我是痞子衡,是正經搞技術的痞子。今天痞子衡給大家分享的是在MDK開發環境下自定義安裝與切換不同編譯器版本的方法。 Keil MDK 想必是嵌入式開發者最熟悉的工具之一了,自 2005 年 Arm 公司收購 Keil 公司之後,MDK 就走上了發展快車道,從 v2.50a 一路狂奔到現在最新的 ...
一周排行
    -Advertisement-
    Play Games
  • 示例項目結構 在 Visual Studio 中創建一個 WinForms 應用程式後,項目結構如下所示: MyWinFormsApp/ │ ├───Properties/ │ └───Settings.settings │ ├───bin/ │ ├───Debug/ │ └───Release/ ...
  • [STAThread] 特性用於需要與 COM 組件交互的應用程式,尤其是依賴單線程模型(如 Windows Forms 應用程式)的組件。在 STA 模式下,線程擁有自己的消息迴圈,這對於處理用戶界面和某些 COM 組件是必要的。 [STAThread] static void Main(stri ...
  • 在WinForm中使用全局異常捕獲處理 在WinForm應用程式中,全局異常捕獲是確保程式穩定性的關鍵。通過在Program類的Main方法中設置全局異常處理,可以有效地捕獲並處理未預見的異常,從而避免程式崩潰。 註冊全局異常事件 [STAThread] static void Main() { / ...
  • 前言 給大家推薦一款開源的 Winform 控制項庫,可以幫助我們開發更加美觀、漂亮的 WinForm 界面。 項目介紹 SunnyUI.NET 是一個基於 .NET Framework 4.0+、.NET 6、.NET 7 和 .NET 8 的 WinForm 開源控制項庫,同時也提供了工具類庫、擴展 ...
  • 說明 該文章是屬於OverallAuth2.0系列文章,每周更新一篇該系列文章(從0到1完成系統開發)。 該系統文章,我會儘量說的非常詳細,做到不管新手、老手都能看懂。 說明:OverallAuth2.0 是一個簡單、易懂、功能強大的許可權+可視化流程管理系統。 有興趣的朋友,請關註我吧(*^▽^*) ...
  • 一、下載安裝 1.下載git 必須先下載並安裝git,再TortoiseGit下載安裝 git安裝參考教程:https://blog.csdn.net/mukes/article/details/115693833 2.TortoiseGit下載與安裝 TortoiseGit,Git客戶端,32/6 ...
  • 前言 在項目開發過程中,理解數據結構和演算法如同掌握蓋房子的秘訣。演算法不僅能幫助我們編寫高效、優質的代碼,還能解決項目中遇到的各種難題。 給大家推薦一個支持C#的開源免費、新手友好的數據結構與演算法入門教程:Hello演算法。 項目介紹 《Hello Algo》是一本開源免費、新手友好的數據結構與演算法入門 ...
  • 1.生成單個Proto.bat內容 @rem Copyright 2016, Google Inc. @rem All rights reserved. @rem @rem Redistribution and use in source and binary forms, with or with ...
  • 一:背景 1. 講故事 前段時間有位朋友找到我,說他的窗體程式在客戶這邊出現了卡死,讓我幫忙看下怎麼回事?dump也生成了,既然有dump了那就上 windbg 分析吧。 二:WinDbg 分析 1. 為什麼會卡死 窗體程式的卡死,入口門檻很低,後續往下分析就不一定了,不管怎麼說先用 !clrsta ...
  • 前言 人工智慧時代,人臉識別技術已成為安全驗證、身份識別和用戶交互的關鍵工具。 給大家推薦一款.NET 開源提供了強大的人臉識別 API,工具不僅易於集成,還具備高效處理能力。 本文將介紹一款如何利用這些API,為我們的項目添加智能識別的亮點。 項目介紹 GitHub 上擁有 1.2k 星標的 C# ...