深入了解linux系統—— 進程信號的保存

信號

信號,什么是信號?

在現實生活中,鬧鐘,紅綠燈,電話鈴聲等等;這些都是現實生活中的信號,當鬧鐘想起時,我就要起床;當電話鈴聲想起時,我就知道有人給我打電話,就要接聽電話;

現實生活中的這些信號,我們接收到之后就要停止當前正在做的事;所以可以說:**號是發送給進程的,而信號是一種事件通知的異步通知機制

在計算機操作系統中,信號是發送給進程的,而信號是一種事件通知的異步通知機制

簡單來說就是,進程在沒有收到信號時,在執行自己的代碼;信號的產生和進程的運行,是異步的

同步異步

這里簡單了解以下同步異步:

同步: 任務按照順序執行,前面任務沒有完成,后面任務就要阻塞等待;

異步: 多個任務可以同時執行,也就是說事件可以同時發生。

相關概念

在深入探究信號之前,先來了解信號相關的概念:

  • 在沒有產生信號時,進程就已經知道如何處理信號了

就像在現實生活中一樣,在鬧鐘沒有響之前,我們就知道鬧鐘響了就要起床了。

  • 信號處理,可以立即處理,也可以過一段時間再處理(在合適的時候處理)
  • 進程當中早已內置了對于信號的識別和處理

我們知道操作系統也是程序員寫的,在設計寫操作系統時,進程當中已內置了如何接受信號和處理信號。

  • 信號源非常多

信號是發送給進程的,那信號是誰產生發送給進程的呢?

信號的產生源非常多,就比如Ctrl + CCtrl + \kill指令都是給進程發送信號。

信號分類

簡單了解了信號是什么,那在Linux系統中都存在哪些信號呢?如何查看這些信號呢?

kill -l命令用來查看所有的信號:

在這里插入圖片描述

可以看到一共有62個信號,對于這62個信號可以粗略的分為兩部分:

  • 1 - 31號信號:這部分信號可以不被立即處理(非實時信號
  • 34 - 64號信號:這部分信號必須被立即處理(實時信號

信號處理

信號從產生到處理,可以分為信號產生、信號保存、信號處理三個階段;

進程對于信號的處理方式有三種:

  1. 默認處理:SIG_DFL,進程處理信號的默認處理方式就是終止進程。
  2. 自定義處理:我們可以修改進程對于信號的處理方式。
  3. 忽略處理:SIG_IGN

信號產生

了解了信號是發送給進程的,那信號是如何產生的呢?

1. 通過終端按鍵(鍵盤)產生信號

在這里插入圖片描述

在之前,我們通過Ctrl + C可以終止進程,為什么呢?

這就是因為Ctrl + C本質上就是向目標進程發送信號,而進程對于相當一部分信號的處理方式都是終止進程。

Ctrl + C是向進程發送幾號信號呢?

這里Ctrl + C是向進程發送2號信號。

系統調用signal

在這里插入圖片描述

signal用來替換進程某種信號的默認處理方式;

存在兩個參數:signum表示要替換信號的數字標號handler是函數指針類型,表示要替換的函數

在這里插入圖片描述

#include <iostream>
#include <unistd.h>
#include <signal.h>
void handler(int sig)
{std::cout << "收到信號 " << sig << std::endl;
}int main()
{signal(2, handler);int cnt = 0;while (true){printf("cnt : %d, pid : %d\n", cnt++, getpid());sleep(1);}return 0;
}

在這里插入圖片描述

可以看到,進程在收到2號信號之后,沒有執行默認處理方式,而是執行handler函數。

Ctrl + C就是給進程發送2號信號。

這里按Ctrl + C是給進程發送2號信號,除此之外Ctrl + \是發送3號信號、Ctrl + Z是發送20號信號。

這里就將進程對于1 - 31號信號的處理方式都替換成自定義處理:

#include <iostream>
#include <unistd.h>
#include <signal.h>
void handler(int sig)
{std::cout << "receive signal : " << sig << std::endl;
}
int main()
{for (int i = 1; i < 32; i++)signal(i, handler);int cnt = 0;while (true){printf("cnt : %d, pid : %d\n", cnt++, getpid());sleep(1);}return 0;
}

在這里插入圖片描述

可以看到Ctrl + cCtrl + \能夠讓進程退出就是給目標進程發送對應的信號,而進程對于信號的處理發送就是終止進程。

這里就存在一些疑問:

  1. 進程將對于所有的信號的處理方式都替換成自定義處理,那信號就不能殺死進程了?
  2. Ctrl + cCtrl + \是給目標進程發送信號,那目標進程是什么呢?

首先,在信號在存在一些信號,是不能替換進程對于該信號的處理方式的,例如9號信號(上面的進程我們依然可以發送9號信號殺掉該進程)。

目標進程

Ctrl + c這種通過鍵盤來給目標進程發送信號,那什么是目標進程呢?簡單來說就是前臺進程。

前臺/后臺進程

在這里插入圖片描述

如上圖所示,直接啟動程序,默認是在前臺運行的,這時我們輸入指令,沒有任何反應;

在程序退出后,命令才得以執行。

在這里插入圖片描述

而在啟動程序時,讓程序在后臺進程;也就是進程在后臺運行,此時輸入命令行指令,指令可以被執行。

前臺進程只有一個,后臺進程可以存在多個

這是因為,鍵盤輸入只有一個,也就是同時只能存在一個進程讀取鍵盤輸入的數據;也就是前臺進程。

而多個進程能夠同時向一個顯示器文件中寫入,也就是輸出到屏幕中。

相關操作

關于前臺進程和后臺進程,我們可以進程查看后臺進程、將后臺進程變成前臺進程、暫停前提正在運行的進程(讓它變成后臺)、以及讓后臺進程運行起來等一系列操作。

jobs查看后臺進程

在這里插入圖片描述

使用jobs命令可以查看當前所有的后臺進程,可以查看到所有后臺進程的任務號、狀態等等信息。

fg將后臺進程變成前臺進程

我們可以讓進程在后臺運行,也可以查看后臺進程;當然也可以將一個后臺進程變成前臺進程。

在這里插入圖片描述

Ctrl + Z暫停前臺進程

我們知道,Ctrl + Z可以暫停目標進程,而Ctrl + Z也是給目標進程發送信號;本質上來說Ctrl + Z就是給前臺進程發送20號信號

前臺進程被暫停之后,就會變成后臺進程

簡單來說就是,前臺進程要獲取我們用戶的輸入信息,前臺進程無法被暫停。

我們通過Ctrl + Z暫停一個前臺進程之后,該進程就會變成后臺進程了。

這里就不修改程序對于信號的處理方式了。

#include <iostream>
#include <unistd.h>
int main()
{while (true){std::cout << "pid : " << getpid() << std::endl;sleep(2);}return 0;
}

在這里插入圖片描述

bg讓后臺進程運行起來

前臺進程被暫停就會變成后臺進程,那處于暫停狀態的后臺進程呢?

我們可以通過bg命令來讓一個暫停狀態的后臺進程運行起來。

在這里插入圖片描述

OS如何管理硬件資源

先來看一下代碼:

int main()
{int x = 0;std::cout << "in begin" << std::endl;std::cin >> x;std::cout << "in sucess" << std::endl;return 0;
}

我們知道,在輸入cin/printf時,程序就會等待我們輸入數據之后,才會接著運行;也就是說進程會等待鍵盤輸入數據,進程就從運行態到阻塞態(內核數據結構從CPU運行隊列到鍵盤等待隊列)。

等待我們輸入數據時,進程才會繼續運行;

那進程是如何知道鍵盤上輸入數據了呢?

我們知道OS管理軟硬件資源,所以操作系統肯定是知道鍵盤上是否存在數據的,那問題是:OS是如何知道鍵盤存儲數據了呢?

這里并不是OS定期排查,來看鍵盤是否有數據的;

簡單來說,就是當鍵盤當中存在數據時,鍵盤就會向CPU發送硬件中斷;在CPU當中存在對應的針腳,CPU通過識別高低鍛電壓來區別是否存在硬件中斷;當存在硬件中斷時,就CPU就會執行操作系統處理數據的代碼;而OS就會停止當前工作,將數據讀入內存。

2. 通過系統調用發送信號

信號可以由終端按鍵,例如Ctrl + C目標進程發送信號;當然我們也可以通過系統調用來發送信號。

常用的系統調用有killraiseabort

kill

在這里插入圖片描述

kill系統調用可以給任意進程發送信號;

參數

pid:指要發送信號給進程,進程的pid

sig:指要發送幾號信號,信號的標號。

了解了kill系統調用可以給任意進程發送信號,那就可以使用kill來實現一個自己的kill命令:mykill

//mykill.cc
#include <iostream>
#include <string.h>
#include <signal.h>
int main(int argc, char* argv[])
{if(argc !=3){return -1;}int id = std::stoi(argv[2]);char* str = argv[1]+1;int sig = std::stoi(str);kill(id,sig);return 0;
}
//test.cc
#include <iostream>
#include <unistd.h>
#include <signal.h>
void handler(int sig)
{std::cout << "receive signal : " << sig << std::endl;
}
int main()
{for (int i = 1; i < 32; i++)signal(i, handler);std::cout << "pid : " << getpid() << std::endl;while (true){sleep(1);}return 0;
}

在這里插入圖片描述

raise

在這里插入圖片描述

kill系統調用可以給任意進程發送任意信號

raise是庫函數,它可以給進程自己發送任意信號。

簡單來說就是進程調用raise,可以給自己發送任意信號。

#include <iostream>
#include <unistd.h>
#include <signal.h>void handler(int sig)
{std::cout << "receive signal : " << sig << std::endl;
}int main()
{for (int i = 1; i < 32; i++)signal(i, handler);for (int i = 1; i < 32; i++){if (i == 9 || i == 19)continue;std::cout << "send signal " << i << std::endl;raise(i);}return 0;
}

這里9號信號和19號信號無法進程自定義捕捉,就不發送9和19 號信號。

在這里插入圖片描述

abort

在這里插入圖片描述

kill可以給任意進程發送任意信號、raise可以給進程自己發送任意信號;

abort用來給進程自己發送特定的信號(6號信號),來終止進程。

abort的作用就是終止進程。

#include <iostream>
#include <unistd.h>
#include <signal.h>
void handler(int sig)
{std::cout << "receive signal : " << sig << std::endl;
}
int main()
{for(int i = 1; i<32;i++)signal(i,handler);std::cout << "pid : " << getpid() << std::endl;abort();while(1)sleep(1);return 0;
}

在這里插入圖片描述

可以看到,abort是給進程自己發送6號信號;6號信號是SIGABRT

但是這里是修改了進程對于1-32的處理方式的(919無法修改),并且進程在收到abort發送的6號信號之后,是執行了自定義處理發送handler的,那為什么進程還是退出了?

abort函數的作用就是終止進程,這里就是修改了進程對于6號進程的處理發送,但是abort還是會終止進程。

3. 硬件異常

我們知道,當程序中存在/0、野指針(越界訪問)時,進程就會直接退出;那進程是如何退出的呢?

答案就是信號,當程序出現錯誤時,OS統就會給當前進程發送信號從而殺掉進程。

操作系統是如何知道程序出錯了呢?

當程序出錯時,操作系統會通過信號殺掉進程,那操作系統是如何知道程序出錯了呢?

例如/0CPU在執行/0操作,寄存器就會發生浮點數溢出,就會觸發硬件中斷,從而執行OS相關的方法。

野指針同理,當進行野指針訪問時,CPU在執行時發出錯就會觸發硬件中斷,然后執行OS相關方法。

0

void handler(int sig)
{std::cout<<"recive signal : "<< sig << std::endl;exit(1);
}
int main()
{for(int i = 1; i<32;i++)signal(i, handler);//除0int x = 3;x/=0;while(true){}return 0;
}

在這里插入圖片描述

野指針

void handler(int sig)
{std::cout<<"recive signal : "<< sig << std::endl;exit(1);
}
int main()
{for(int i = 1; i<32;i++)signal(i, handler);//野指針int* p = nullptr;*p = 1;//訪問nullptrwhile(true){}return 0;
}

在這里插入圖片描述

子進程退出core dump

還記得當子進程退出時,存在一個退出碼;退出碼的低7位指子進程被哪個信號殺死,而第8位在標識進程是否被信號殺死。

#include <iostream>
#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>
void handler(int sig)
{std::cout << "recive signal : " << sig << std::endl;
}
int main()
{int id = fork();if (id < 0)exit(1);else if (id == 0){sleep(1);int x = 10;x /= 0;}for (int i = 1; i < 32; i++)signal(i, handler);int status = 0;waitpid(-1, &status, 0);printf("status : %d, exit signal: %d, core dump: %d\n", status, status & 0x7F, (status >> 7) & 1);return 0;
}

在上述代碼中,父進程創建子進程,子進程進行/0操作,子進程就會被信號殺掉;

父進程修改對8(SIGFPE)信號的處理方式,然后獲取子進程的退出信息。

在子進程的退出信息中,低7位存儲子進程被幾號信號殺掉,第8位標識子進程是否被信號殺掉。
在這里插入圖片描述

4. 軟件條件

軟件異常產生中斷,顧名思義進程軟件條件不滿足從而產生信號;

例如:進程間通過管道文件進行通信,讀端退出,OS系統就會殺掉寫端;(通過發送信號讓寫端退出)。

這里簡單測試一下:

//process1.cc
#include <iostream>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#define PATHNAME "./fifo"
int main()
{// 創建管道文件mkfifo(PATHNAME, 0666);// 打開int rfd = open(PATHNAME, O_RDONLY);// 讀取char buff[1024];int cnt = 3;while (cnt--){int x = read(rfd, buff, sizeof(buff));buff[x] = 0;std::cout << "read : " << buff << std::endl;}// 關閉close(rfd);return 0;
}
//process2.cc
#include <iostream>
#include <fcntl.h>
#include <unistd.h>
#include <cstring>
#include <signal.h>
#define PATHNAME "./fifo"
void handler(int sig)
{std::cout << "receive signal : " << sig << std::endl;exit(1);
}
int main()
{for(int i =1 ;i<32;i++)signal(i,handler);// 打開管道文件int wfd = open(PATHNAME, O_WRONLY);// 寫入const char *msg = "abcd";while (true){write(wfd, msg, strlen(msg));sleep(1);std::cout << "write : " << msg << std::endl;}return 0;
}

在這里插入圖片描述

alarm

先來看一下alarm函數

在這里插入圖片描述

alarm只有一個參數secondsalarm的作用就是給當前進程設置鬧鐘;

簡單來說就是,在seconds秒后給進程發送信號。

對于alarm的返回值,可能為0,也可能是上次設置鬧鐘的剩余時間。

在這里插入圖片描述

#include <iostream>
#include <unistd.h>
#include <signal.h>
void handler(int sig)
{std::cout << "recive signal : " << sig << std::endl;
}
int main()
{for(int i = 1;i<32;i++)signal(i,handler);alarm(3);sleep(5);return 0;
}

在這里插入圖片描述

可以看到alarm設置鬧鐘,就是在seconds秒過后給進程發送14號信號。

所以,我們就可以通過給進程設定鬧鐘,讓進程周期性的完成一些任務;

#include <iostream>
#include <unistd.h>
#include <signal.h>
#include <functional>
#include <vector>
void sch()
{std::cout << "process scheduling" << std::endl;
}
void check()
{std::cout << "memory check" << std::endl;
}
std::vector<std::function<void()>> task_list;
void handler(int sig)
{for (auto &e : task_list){e();}alarm(1);
}
int main()
{task_list.push_back(sch);task_list.push_back(check);signal(14, handler);alarm(1);while (true){pause();}return 0;
}

在這里插入圖片描述

理解系統鬧鐘

系統鬧鐘,本質上就是操作系統給對應進程發送信號,所以操作系統本身就要具有定時的功能;(例如:時間戳)

而在OS中,定時器也可能存在很多,如此多的定時器也要被管理起來;Linux內核數據結構如下:

struct timer_list {struct list_head entry;unsigned long expires;void (*function)(unsigned long);unsigned long data;struct tvec_t_base_s *base;
};

可以看到timer_list也是被鏈表鏈接起來的;其中還包括exipries定時器超時時間和function處理方式。

管理定時器,采用的是時間輪的方法,可以簡單理解成堆結構。

總結

簡單總結上述內容:

  • 信號是事件的一種異步通知機制

  • 信號產生的方式

    終端按鍵

    系統調用:killraiseabort
    硬件異常:/0、野指針、子進程退出

    軟件條件:alarm、軟件條件不滿足

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/914419.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/914419.shtml
英文地址,請注明出處:http://en.pswp.cn/news/914419.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Redis 事務錯誤處理機制與開發應對策略

&#x1f4d8; Redis 事務錯誤處理機制與開發應對策略一、Redis 事務基礎回顧 Redis 中的事務由以下三組命令構成&#xff1a;命令作用說明MULTI開始一個事務&#xff0c;進入命令入隊模式命令集所有后續命令不會立即執行&#xff0c;而是入隊等待提交EXEC提交事務&#xff0c;…

信息學奧賽一本通 1549:最大數 | 洛谷 P1198 [JSOI2008] 最大數

【題目鏈接】 ybt 1549&#xff1a;最大數 洛谷 P1198 [JSOI2008] 最大數 【題目考點】 1. 線段樹&#xff1a;單點修改 區間查詢 知識點講解見&#xff1a;洛谷 P3374 【模板】樹狀數組 1&#xff08;線段樹解法&#xff09; 【解題思路】 本題為設線段樹維護區間最值&a…

【STM32】什么在使能寄存器或外設之前必須先打開時鐘?

這篇文章解釋一個非常基礎但是重要的問題&#xff1a; 為什么在使能寄存器或外設之前必須先打開時鐘&#xff1f; 我們會發現&#xff0c;如果不開時鐘就訪問寄存器 ? 會“寫不進去”或“讀取錯誤”。 因此&#xff0c;我們在寫代碼時&#xff0c;總是需要 先開時鐘&#xff0…

Go·并發處理http請求實現

一、Goroutine介紹 基本原理 goroutine 是 Go 運行時(Runtime)管理的??用戶態線程。與線程相比,其初始棧空間僅約 2KB,創建和切換的開銷更低,能夠同時運行大量并發任務。 創建goroutine的方法非常簡單,在將要調用的函數前加入go關鍵字即可。 func hello() {fmt.Pri…

USB一線連多屏?Display Link技術深度解析

DisplayLink 技術是一種基于USB接口的顯示輸出解決方案&#xff0c;通常用于通過USB端口連接多個顯示器&#xff0c;尤其在筆記本電腦、平板電腦和臺式機上&#xff0c;能夠顯著擴展顯示屏的數量和分辨率。它的核心技術原理是通過壓縮和傳輸圖形數據&#xff0c;將視頻信號通過…

AI 臨床醫學課題【總結】

最近參與了幾個臨床醫學課題,總結一下如何跨界結合 1: 確定研究的方向: 這個是決定文章的核心 研究方向的時候,就要確定要投的期刊,平時看論文的時候要把一些常用的術語記錄下來, 投的期刊,研究內容,方法記錄一下。 2: 研究團隊團隊搭建(負責人:負責讀論文,研究點…

PostgreSQL HOT (Heap Only Tuple) 更新機制詳解

PostgreSQL HOT (Heap Only Tuple) 更新機制詳解在PostgreSQL中&#xff0c;為了提高更新操作的性能并減少存儲空間的浪費&#xff0c;引入了一種稱為HOT (Heap Only Tuple) 的優化技術。HOT更新允許在相同的數據頁內進行行的更新操作&#xff0c;而不需要創建一個新的物理行版…

macos安裝iper3

brew install iperf3Running brew update --auto-update...安裝homebrew&#xff0c;長久沒用使用更新失效了。只好重新安裝 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"破案了 原來是需要海外網了。。。。 b…

【設計模式】策略模式(政策(Policy)模式)

策略模式&#xff08;Strategy Pattern&#xff09;詳解一、策略模式簡介 策略模式&#xff08;Strategy Pattern&#xff09; 是一種 行為型設計模式&#xff08;對象行為型模式&#xff09;&#xff0c;它定義了一系列算法&#xff0c;并將每一個算法封裝起來&#xff0c;使它…

用TensorFlow進行邏輯回歸(二)

邏輯回歸的例子 邏輯回歸是經典的分類算法。為了簡單&#xff0c;我們考慮二分類。這意味著&#xff0c;我們要處理識別二個分類的問題&#xff0c;我們的標簽為 0 或 1。 我們要一個與線性回歸不同的激活函數&#xff0c;不同的損失函數&#xff0c;神經元的輸出略有不同。我們…

Java設計模式之行為型模式(命令模式)介紹與說明

一、核心定義與目標 命令模式通過對象化請求&#xff0c;將操作的具體實現細節隱藏在命令對象中&#xff0c;使得調用者&#xff08;Invoker&#xff09;無需直接與接收者&#xff08;Receiver&#xff09;交互&#xff0c;僅需通過命令對象間接調用。這種解耦設計支持以下功能…

【深度學習新浪潮】xAI新發布的Grok4有什么看點?

Grok4作為馬斯克旗下xAI公司最新發布的旗艦AI模型,其核心看點和評測要點可總結如下: 一、Grok4的核心看點 學術推理能力全面超越人類博士水平 在「人類終極考試」(HLE)中,Grok4基礎版正確率達25.4%,啟用工具后飆升至44.4%,遠超Gemini 2.5 Pro(21.6%)和OpenAI o3(20.…

觀成科技:基于自監督學習技術的惡意加密流量檢測方案

1.前言當前&#xff0c;隨著加密協議技術的廣泛應用&#xff0c;互聯網用戶的個人流量隱私得到了有效保護&#xff0c;但與此同時也衍生出一系列安全問題。由于加密流量在傳輸過程中無法被解密&#xff0c;導致傳輸信息呈現“黑盒化”特征&#xff0c;這為惡意攻擊者利用加密流…

通用定時器GPT

目錄 GPT核心特性 GPT 計數器操作模式 重啟模式 自由運行模式 GPT時鐘源 GPT框圖 輸入捕獲&#xff1a;測量外部信號的高電平脈沖寬度 輸出比較&#xff1a;生成 1kHz PWM 波 GPT模塊外部引腳復用與功能映射表 GPT使用注意事項 GPT Memory Map GPT寄存器 GPTx_CR寄存…

#oda0095. 字符串通配符【B卷 100分】-字符串

題目描述問題描述&#xff1a;在計算機中&#xff0c;通配符一種特殊語法&#xff0c;廣泛應用于文件搜索、數據庫、正則表達式等領域。現要求各位實現字符串通配符的算法。要求&#xff1a;實現如下2個通配符&#xff1a;* &#xff1a;匹配0個或以上的字符&#xff08;注&…

面向對象設計原則

面向對象&#xff1a;是一種編程思想&#xff0c;面向過程是關注實現的步驟&#xff0c;每個步驟定義一個函數&#xff0c;調用函數執行即可。面向對象關注的是誰來執行&#xff0c;把具有相同屬性和行為的一類事物進行抽象成類&#xff0c;然后再通過實例化出一個個具體的對象…

Hyperledger Fabric深入解讀:企業級區塊鏈的架構、應用與未來

一、引言&#xff1a;企業級區塊鏈的標桿Hyperledger Fabric是Linux基金會主導的開源項目&#xff0c;專為企業級應用設計&#xff0c;以模塊化架構、許可鏈機制和隱私保護為核心&#xff0c;廣泛應用于金融、供應鏈、醫療等領域。相較于公有鏈&#xff08;如以太坊&#xff09…

從0開始學習R語言--Day45--Hausman檢驗

當我們在探究數據本身是否和變量相關時&#xff0c;往往都會對這兩者進行回歸分析&#xff0c;控制一下變量來看看趨勢走向。但其實在分析前&#xff0c;我們可以先嘗試做Hausman檢驗&#xff0c;這可以幫助我們判斷數據的變化到底是因為變量不一樣了還是因為自己的個體效應所以…

閑庭信步使用圖像驗證平臺加速FPGA的開發:第九課——圖像插值的FPGA實現

&#xff08;本系列只需要modelsim即可完成數字圖像的處理&#xff0c;每個工程都搭建了全自動化的仿真環境&#xff0c;只需要雙擊top_tb.bat文件就可以完成整個的仿真&#xff0c;大大降低了初學者的門檻&#xff01;&#xff01;&#xff01;&#xff01;如需要該系列的工程…

Android事件分發機制完整總結

一、核心概念事件分發的本質Android事件分發采用責任鏈模式&#xff0c;事件從Activity開始&#xff0c;依次經過ViewGroup和View。整個機制只有一個入口&#xff1a;dispatchTouchEvent方法。onInterceptTouchEvent和onTouchEvent都不是獨立的事件入口&#xff0c;而是被dispa…