Linux驅動學習day24(UART子系統)

一、UART硬件理論

1.1 作用及功能

UART:通用異步收發傳輸器,簡稱串口。

功能:移植u-boot、內核時,主要使用串口查看打印信息。外接各種模塊,比如藍牙GPS模塊。

使用UART的時候,要注意1. 波特率 2. 格式:數據位、停止位、校驗位、流控。

1.2 發送接收數據流程

ARM發送數據以及PC接收數據流程,假設發送A 0x41 0b01000001。

ARM串口:原本是高電平 ,ARM拉低,保持1bit的時間(也就是波特率)。PC在低電平的時候開始計時,ARM根據數據驅動TxD電平,TxD = Data[0]?,TxD = Data[1]?...TxD = Data[7]。

PC讀取數據:在數據位的中間讀取引腳狀態,Data = RxD[t0]。

校驗位:奇/偶 “數據位+校驗位”中為1的位的個數是 奇/偶(如果數據位中 1 的個數是奇數,則校驗位為 0,否則為 1,使總的 1 個數為奇數)(現在一般不使用)。

TTL/CMOS這種方式電平不高,不適合遠距離傳輸

?目前開發板基本都會將電平轉換芯片做到板子里面。

?目前大部分電腦都沒有串口,所以現在使用的是下面這個圖的方式。

1.3 波特率

當波特率為115200時,且格式為115200,8n1。每秒能發送多少字節數據?

8代表數據位,n代表沒有校驗位,1代表停止位,傳輸一個字節需要10bit(含一個起始位),所以發送一個字節需要1/115200 * 10 = 1/11520,1秒能傳輸11520個字節數據。

二、TTY體系中設備節點的差別

TTY:teletype (teleprinter遠程打印機)。(輸入輸出設備)

/dev/tty0,/dev/tty1,/dev/tty2 都是虛擬終端,/dev/tty0指的是位于前臺的那個虛擬終端。

而/dev/ttyS0是真實的串口終端。

/dev/tty是當前程序本身所使用的終端。

console控制臺,可以理解為權限更大,能查看更多信息。比如我們可以在Console上看到內核的打印信息,從這個角度上看:·Console是某一個Terminal , Terminal并不都是Console。我們可以從多個Terminal中選擇某一個作為Console。很多時候,兩個概念混用,并無明確的、官方的定義?。

三、TTY驅動程序框架

在超級終端打出一個字符“l”,涉及發送和接收?

?如果寫錯字符刪去字符,其實是發送了退格鍵。

上圖是當輸入ls之后按下回車的傳輸情況,當PC機端按下回車,會將enter這個字節通過串口發送給ARM的串口,通過UART驅動程序發送給行規程(主要做字符的緩沖和處理(如回顯、編輯、終結符識別等,輸入被緩存在行緩沖區,直到遇到“行結束符”(如 \n)才傳給用戶空間。)),行規程判斷這是一個回車,并將其發送給APP(通常是一個shell),APP查找當前目錄下的內容之后將內容發送給行規程,行規程發送給驅動,由UART再將數據發給PC端,得以顯示。

四、Linux串口應用編程

4.1 應用基礎及串口常用API函數

在Linux系統中,操作設備的統一接口就是:open/iodtl/read/write。對于UART,又在ioctI之上封裝了很多函數,主要是用來設置行規程。所以對于UART,編程的套路就是: a. open,b. 設置行規程,比如波特率、數據位、停止位、檢驗位、RAW模式、一有數據就返回 c. read/write。

如何設置行規程中的參數便成為應用編程的重點。?在Linux中,行規程的參數用結構體termios來表示。

常用函數如下:?

?4.2 UART回環實驗代碼

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <errno.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <termios.h>
#include <stdlib.h>/* set_opt(fd,115200,8,'N',1) */
int set_opt(int fd,int nSpeed, int nBits, char nEvent, int nStop)
{struct termios newtio,oldtio;if ( tcgetattr( fd,&oldtio) != 0) { perror("SetupSerial 1");return -1;}bzero( &newtio, sizeof( newtio ) );newtio.c_cflag |= CLOCAL | CREAD; newtio.c_cflag &= ~CSIZE; newtio.c_lflag  &= ~(ICANON | ECHO | ECHOE | ISIG);  /*Input*/newtio.c_oflag  &= ~OPOST;   /*Output*/switch( nBits ){case 7:newtio.c_cflag |= CS7;break;case 8:newtio.c_cflag |= CS8;break;}switch( nEvent ){case 'O':newtio.c_cflag |= PARENB;newtio.c_cflag |= PARODD;newtio.c_iflag |= (INPCK | ISTRIP);break;case 'E': newtio.c_iflag |= (INPCK | ISTRIP);newtio.c_cflag |= PARENB;newtio.c_cflag &= ~PARODD;break;case 'N': newtio.c_cflag &= ~PARENB;break;}switch( nSpeed ){case 2400:cfsetispeed(&newtio, B2400);cfsetospeed(&newtio, B2400);break;case 4800:cfsetispeed(&newtio, B4800);cfsetospeed(&newtio, B4800);break;case 9600:cfsetispeed(&newtio, B9600);cfsetospeed(&newtio, B9600);break;case 115200:cfsetispeed(&newtio, B115200);cfsetospeed(&newtio, B115200);break;default:cfsetispeed(&newtio, B9600);cfsetospeed(&newtio, B9600);break;}if( nStop == 1 )newtio.c_cflag &= ~CSTOPB;else if ( nStop == 2 )newtio.c_cflag |= CSTOPB;newtio.c_cc[VMIN]  = 1;  /* 讀數據時的最小字節數: 沒讀到這些數據我就不返回! */newtio.c_cc[VTIME] = 0; /* 等待第1個數據的時間: * 比如VMIN設為10表示至少讀到10個數據才返回,* 但是沒有數據總不能一直等吧? 可以設置VTIME(單位是10秒)* 假設VTIME=1,表示: *    10秒內一個數據都沒有的話就返回*    如果10秒內至少讀到了1個字節,那就繼續等待,完全讀到VMIN個數據再返回*/tcflush(fd,TCIFLUSH);if((tcsetattr(fd,TCSANOW,&newtio))!=0){perror("com set error");return -1;}//printf("set done!\n");return 0;
}int open_port(char *com)
{int fd;//fd = open(com, O_RDWR|O_NOCTTY|O_NDELAY);fd = open(com, O_RDWR|O_NOCTTY);if (-1 == fd){return(-1);}if(fcntl(fd, F_SETFL, 0)<0) /* 設置串口為阻塞狀態*/{printf("fcntl failed!\n");return -1;}return fd;
}/** ./serial_send_recv <dev>*/
int main(int argc, char **argv)
{int fd;int iRet;char c;/* 1. open *//* 2. setup * 115200,8N1* RAW mode* return data immediately*//* 3. write and read */if (argc != 2){printf("Usage: \n");printf("%s </dev/ttySAC1 or other>\n", argv[0]);return -1;}fd = open_port(argv[1]);if (fd < 0){printf("open %s err!\n", argv[1]);return -1;}iRet = set_opt(fd, 115200, 8, 'N', 1);if (iRet){printf("set port err!\n");return -1;}printf("Enter a char: ");while (1){scanf("%c", &c);iRet = write(fd, &c, 1);iRet = read(fd, &c, 1);if (iRet == 1)printf("get: %02x %c\n", c, c);elseprintf("can not get data\n");}return 0;
}

上述代碼做實驗的時候會發現,第一次輸入a和enter鍵,會返回兩次can not get data,輸入b和enter鍵會返回a和enter鍵,也就是返回上一次輸入的數據,這是因為newtio.c_cc[VMIN] ?= 0;??newtio.c_cc[VTIME] = 0;這樣的設置是立即返回,不管是否有數據,由于數據傳輸慢,所以當數據還沒到達UART串口的時候,read函數立即返回。下一次讀到的數據就是之前寫的數據。

當設置newtio.c_cc[VMIN] ?= 1;??newtio.c_cc[VTIME] = 0;的時候,read函數會阻塞讀,讀到1個字節就返回,所以這樣只要你發送的字符到達了串口硬件緩沖區,內核檢測到數據立即得到數據,而不會返回can not get data。

4.3 GPS實驗代碼

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <errno.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <termios.h>
#include <stdlib.h>/* set_opt(fd,115200,8,'N',1) */
int set_opt(int fd,int nSpeed, int nBits, char nEvent, int nStop)
{struct termios newtio,oldtio;if ( tcgetattr( fd,&oldtio) != 0) { perror("SetupSerial 1");return -1;}bzero( &newtio, sizeof( newtio ) );newtio.c_cflag |= CLOCAL | CREAD; newtio.c_cflag &= ~CSIZE; newtio.c_lflag  &= ~(ICANON | ECHO | ECHOE | ISIG);  /*Input*/newtio.c_oflag  &= ~OPOST;   /*Output*/switch( nBits ){case 7:newtio.c_cflag |= CS7;break;case 8:newtio.c_cflag |= CS8;break;}switch( nEvent ){case 'O':newtio.c_cflag |= PARENB;newtio.c_cflag |= PARODD;newtio.c_iflag |= (INPCK | ISTRIP);break;case 'E': newtio.c_iflag |= (INPCK | ISTRIP);newtio.c_cflag |= PARENB;newtio.c_cflag &= ~PARODD;break;case 'N': newtio.c_cflag &= ~PARENB;break;}switch( nSpeed ){case 2400:cfsetispeed(&newtio, B2400);cfsetospeed(&newtio, B2400);break;case 4800:cfsetispeed(&newtio, B4800);cfsetospeed(&newtio, B4800);break;case 9600:cfsetispeed(&newtio, B9600);cfsetospeed(&newtio, B9600);break;case 115200:cfsetispeed(&newtio, B115200);cfsetospeed(&newtio, B115200);break;default:cfsetispeed(&newtio, B9600);cfsetospeed(&newtio, B9600);break;}if( nStop == 1 )newtio.c_cflag &= ~CSTOPB;else if ( nStop == 2 )newtio.c_cflag |= CSTOPB;newtio.c_cc[VMIN]  = 1;  /* 讀數據時的最小字節數: 沒讀到這些數據我就不返回! */newtio.c_cc[VTIME] = 0; /* 等待第1個數據的時間: * 比如VMIN設為10表示至少讀到10個數據才返回,* 但是沒有數據總不能一直等吧? 可以設置VTIME(單位是10秒)* 假設VTIME=1,表示: *    10秒內一個數據都沒有的話就返回*    如果10秒內至少讀到了1個字節,那就繼續等待,完全讀到VMIN個數據再返回*/tcflush(fd,TCIFLUSH);if((tcsetattr(fd,TCSANOW,&newtio))!=0){perror("com set error");return -1;}//printf("set done!\n");return 0;
}int open_port(char *com)
{int fd;//fd = open(com, O_RDWR|O_NOCTTY|O_NDELAY);fd = open(com, O_RDWR|O_NOCTTY);if (-1 == fd){return(-1);}if(fcntl(fd, F_SETFL, 0)<0) /* 設置串口為阻塞狀態*/{printf("fcntl failed!\n");return -1;}return fd;
}/* eg. $GPGGA,082559.00,4005.22599,N,11632.58234,E,1,04,3.08,14.6,M,-5.6,M,,*76"<CR><LF> */
int read_gps_raw_data(int fd, char *buf)
{int i = 0;int iRet;char c;int start = 0;while (1){iRet = read(fd, &c, 1);if (iRet == 1){if (c == '$')start = 1;if (start){buf[i++] = c;}if (c == '\n' || c == '\r')return 0;}else{return -1;}}
}/* eg. $GPGGA,082559.00,4005.22599,N,11632.58234,E,1,04,3.08,14.6,M,-5.6,M,,*76"<CR><LF> */
int parse_gps_raw_data(char *buf, char *time, char *lat, char *ns, char *lng, char *ew)
{char tmp[10];if (buf[0] != '$')return -1;else if (strncmp(buf+3, "GGA", 3) != 0)return -1;else if (strstr(buf, ",,,,,")){printf("Place the GPS to open area\n");return -1;}else {//printf("raw data: %s\n", buf);sscanf(buf, "%[^,],%[^,],%[^,],%[^,],%[^,],%[^,]", tmp, time, lat, ns, lng, ew);return 0;}
}/** ./serial_send_recv <dev>*/
int main(int argc, char **argv)
{int fd;int iRet;char c;char buf[1000];char time[100];char Lat[100]; char ns[100]; char Lng[100]; char ew[100];float fLat, fLng;/* 1. open *//* 2. setup * 115200,8N1* RAW mode* return data immediately*//* 3. write and read */if (argc != 2){printf("Usage: \n");printf("%s </dev/ttySAC1 or other>\n", argv[0]);return -1;}fd = open_port(argv[1]);if (fd < 0){printf("open %s err!\n", argv[1]);return -1;}iRet = set_opt(fd, 9600, 8, 'N', 1);if (iRet){printf("set port err!\n");return -1;}while (1){/* eg. $GPGGA,082559.00,4005.22599,N,11632.58234,E,1,04,3.08,14.6,M,-5.6,M,,*76"<CR><LF>*//* read line */iRet = read_gps_raw_data(fd, buf);/* parse line */if (iRet == 0){iRet = parse_gps_raw_data(buf, time, Lat, ns, Lng, ew);}/* printf */if (iRet == 0){printf("Time : %s\n", time);printf("ns   : %s\n", ns);printf("ew   : %s\n", ew);printf("Lat  : %s\n", Lat);printf("Lng  : %s\n", Lng);/* 緯度格式: ddmm.mmmm */sscanf(Lat+2, "%f", &fLat);fLat = fLat / 60;fLat += (Lat[0] - '0')*10 + (Lat[1] - '0');/* 經度格式: dddmm.mmmm */sscanf(Lng+3, "%f", &fLng);fLng = fLng / 60;fLng += (Lng[0] - '0')*100 + (Lng[1] - '0')*10 + (Lng[2] - '0');printf("Lng,Lat: %.06f,%.06f\n", fLng, fLat);}}return 0;
}

五、字符設備驅動的另外一種注冊方法

由于register_chrdev無法指定次設備號,它一般直接將(major, xxx)下的次設備號都占用,這就導致了系統資源的浪費。

注冊字符設備區域

有主設備號:register_chrdev_region()

無主設備號:alloc_chrdev_region()

分配設置注冊cdev:cdev_alloc,cdev_init,cdev_add,最主要的是設置cdev中的fops結構體。

六、UART驅動

6.1 注冊過程

在uart中,設備節點可以說是一個port,一個UART就是一個port。

對于這個驅動模型,主要分為上下三層,最底層的左邊是注冊一個uart_driver,最底層的右邊是注冊一個uart_port(對應真實硬件相關信息),下面也是一個platform模型,當platform_driver和DTS中的節點的compatible屬性匹配,會調用驅動程序中的probe函數,在probe函數中會獲得硬件資源,并且設置uart_port,使用uart_add_one_port函數,該函數最終會去使用cdev_add函數,設置重要的cdev_ops。?

6.2 open過程

從open過程可以看出來tty_operations 和 tty_port_operations是核心層(serial_core.c)提供,所以之后寫驅動程序,我們要提供硬件相關的struct uart_ops結構體,要提供里面的startup函數。?

open設備時確定行規程的代碼流程

6.3 read過程(tty-io.c)

流程為:

a. APP讀,使用行規程來讀,無數據則休眠

b. UART接收到數據,產生中斷

c. 中斷程序從硬件上讀入數據發給行規程

d. 行規程處理后存入buffer

e. 行規程喚醒APP,APP被喚醒后,從行規程buffer中讀入數據,返回。

6.4 write過程

流程為:a. APP寫。使用行規程來寫,數據最終存入uart_state->xmit的buffer里。
b. 硬件發送:怎么發送數據?使用硬件驅動中uart_ops->start_tx開始發送具體的發送方法有2種:通過DMA,或通過中斷。
c.中斷方式 方法1:直接使能txempty中斷,一開始tx buffer為空,在中斷里填入數據。方法2:寫部分數據到txfifo,使能中斷,剩下的數據再中斷里繼續發送。

?

這部分的函數特別多還特別亂,存在不同的文件中,發送流程:APP發送數據==>tty-io.c中調用tty_write==>do_tty_write,在該函數中通過copy_from_user和write寫到行規程的write_buf中==>進入行規程使用n_tty.c中n_tty_write==>n_tty_write函數調用tty_struct中的tty_operations的write函數進入核心層serial_core.c==>struct?tty_operations uart_ops.uart_write==>將數據拷貝到statas中的環形緩沖區==>_uart_start開始發送==>使用8250_port.c(硬件相關的驅動程序中)uart_ops.start_tx函數。

七、UART驅動調試方法、

7.1 /proc/interrupt

查看中斷次數

7.2 /proc/tty/drivers

7.3?/proc/tty/driver()

表示這個串口驅動支持三個串口 ,發送統計信息,含接收發送

7.4?/proc/tty/ldiscs

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

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

相關文章

NFS共享服務器

目錄 任務要求 思路總結 1.NFS共享服務 服務端 (ip 192.168.48.128) 客戶端 (ip 192.168.48.130) 2.配置autofs自動掛載 任務要求 1.NFS服務器,可以讓PC將網絡中的NFS服務器共享的目錄掛載到本地端的文件系統中,而在本地端的系統中看來&#xff0c;那個遠程主機的目…

FreeRTOS學習筆記之隊列

小編正在學習嵌入式軟件&#xff0c;目前建立了一個交流群&#xff0c;可以留下你的評論&#xff0c;我拉你進群一、簡介隊列是為了任務與任務、任務與中斷之間的通信而準備的&#xff0c;可以在任務與任務、任務與中斷之間消息傳遞&#xff0c;隊列中可以存儲有限的、大小固定…

垃圾收集器-ZGC

前言在Java開發中&#xff0c;垃圾收集器的選擇對系統性能有著致命的影響。Java 8后&#xff0c;雖然G1 GC成為默認&#xff0c;但是它在延遲性控制上仍有限。ZGC作為最新一代高性能低延遲垃圾收集器&#xff0c;解決了CMS和G1在延遲、垃圾堆容量和吞吐量方面的重大突破。本文將…

計算機“十萬個為什么”之跨域

計算機“十萬個為什么”之跨域 本文是計算機“十萬個為什么”系列的第五篇&#xff0c;主要是介紹跨域的相關知識。 作者&#xff1a;無限大 推薦閱讀時間&#xff1a;10 分鐘 一、引言&#xff1a;為什么會有跨域這個“攔路虎”&#xff1f; 想象你正在參觀一座戒備森嚴的城堡…

C語言:20250719筆記

字符數組在C語言中&#xff0c;支持字符串常量&#xff0c;不支持字符串變量。如果想要實現類似的字符串變量&#xff0c;C語言提供了兩種實現方式&#xff1a;字符數組&#xff1a;char name[] “哪吒”&#xff1b;字符指針&#xff1a;char *name "娜吒"&#x…

decltype是什么,什么作用?

基本概念decltype 是 C11 引入的關鍵字&#xff0c;用于推導表達式的類型&#xff0c;且會完整保留類型的細節&#xff08;包括 const、引用 &、指針 * 等&#xff09;。語法:decltype(表達式) 變量名核心特點1.推導依據是表達式本身&#xff0c;而非表達式的結果&#xff…

RPC 與 Feign 的區別筆記

一、基本概念 1.1 RPC&#xff08;Remote Procedure Call&#xff09; 定義&#xff1a;遠程過程調用&#xff0c;允許像調用本地方法一樣調用遠程服務的方法。 本質&#xff1a;跨進程通信&#xff0c;隱藏了底層網絡通信的復雜性。 常見實現&#xff1a; Java 原生 RMIDub…

高防IP能夠防御CC攻擊嗎?它具備哪些顯著優勢?

摘要&#xff1a; 面對日益復雜的網絡攻擊&#xff0c;高防IP作為重要的安全工具&#xff0c;不僅能防御常見的DDoS攻擊&#xff0c;還能有效應對CC攻擊。本文將解析高防IP防御CC攻擊的原理及其核心優勢&#xff0c;幫助讀者了解其在網絡安全中的關鍵作用。一、高防IP能否防御C…

TypeScript 類型注解(一)

一、TypeScript 類型注解1、什么是TpyeScript類型注解- 是否還記得TypeScript的兩個重要特性&#xff1f;- 類型系統、適用于任何規模- 可以說&#xff0c;TS的類型系統是TS最重要的功能&#xff1b;那么什么是類型注解呢&#xff1f;其實就是在聲明變量時&#xff0c;將變量的…

弗蘭肯斯坦式的人工智能與GTM策略的崩潰

2025 年上半年已經明確了一件事&#xff1a;B2B 市場營銷團隊被工具淹沒&#xff0c;但缺乏策略。人工智能無處不在。收入領導者在進行無休止的試點。營銷團隊拼湊各種點解決方案&#xff0c;希望能實現規模擴張。然而&#xff0c;銷售線索的增長停滯不前。信譽正在受損。曾經承…

NAND閃存(NAND Flash)是什么?

NAND閃存(NAND Flash)是什么? NAND閃存(NAND Flash)詳解 NAND閃存是一種非易失性存儲介質(斷電不丟失數據),廣泛應用于SSD、U盤、手機存儲等設備中。NAND Flash 的全稱是 “Negative-AND Flash”(與非型閃存),其名稱源自其底層存儲單元的電路結構——基于**“與非門…

Android性能優化之UI渲染優化

一、UI渲染核心瓶頸深度解析 1. 渲染管線關鍵階段階段CPU工作GPU工作潛在卡頓點Measure計算View尺寸-嵌套布局多次測量Layout計算View位置-頻繁重排(Relayout)Draw構建DisplayList指令集-復雜自定義View.onDraw()Sync & Upload資源上傳到GPU內存紋理上傳大圖/未壓縮資源Ras…

基于Spring AI Alibaba的智能知識助手系統:從零到一的RAG實戰開發

&#x1f4d6; 項目概述 在人工智能快速發展的今天&#xff0c;RAG&#xff08;Retrieval-Augmented Generation&#xff09;技術已成為構建智能問答系統的核心技術。本文將詳細介紹一個基于Spring AI Alibaba DashScope深度集成的智能知識助手系統的完整開發過程&#xff0c;…

VirtualBox + CentOS:啟用 DHCP 獲取 IPv4 地址

標題&#xff1a; VirtualBox CentOS&#xff1a;啟用 DHCP 獲取 IPv4 地址 日期&#xff1a; 2025-07-18 一、問題現象 最小化安裝的 CentOS 7 虛擬機里敲&#xff1a; ip addr輸出只有 lo 的 127.0.0.1 以及 enp0s3 的 IPv6 鏈路本地地址&#xff0c;沒有 IPv4&#xff0…

Git

Git簡介Git 是一個分布式版本控制工具&#xff0c;通常用來對軟件開發過程中的源代碼文件進行管理。通過Git 倉庫來存儲和管理這些文件&#xff0c;Git 倉庫分為兩種:本地倉庫:開發人員自己電腦上的 Git倉庫。遠程倉庫:遠程服務器上的 Git 倉庫。commit: 提交, 將本地文件和版本…

通信算法之294:LTE系統中的整數倍頻偏估計

在LTE系統中&#xff0c;整數倍頻偏估計主要通過以下方法實現&#xff1a;一、最大似然估計法&#xff08;ML&#xff09;通過遍歷預設的整數倍頻偏范圍&#xff08;如30kHz&#xff09;&#xff0c;將接收信號與本地的PSS序列在不同頻偏點上進行相關運算&#xff0c;選擇相關峰…

數字人直播:開啟直播行業新紀元?

?原始尺寸更換圖片p9-flow-imagex-sign.byteimg.com??在科技日新月異的當下&#xff0c;直播行業正經歷著一場深刻變革&#xff0c;數字人直播的興起&#xff0c;宛如一顆璀璨新星&#xff0c;照亮了直播領域的新征程。數字人直播&#xff0c;是利用先進的人工智能技術&…

朝鮮升級供應鏈惡意軟件XORIndex,再次瞄準npm生態系統

Socket威脅研究團隊最新披露&#xff0c;朝鮮國家支持的黑客組織在"傳染性面試"攻擊活動中采用了新型惡意軟件加載器XORIndex&#xff0c;該惡意程序專門通過npm軟件包注冊表滲透軟件供應鏈。攻擊規模與持續性此次攻擊并非孤立事件&#xff0c;而是針對開發者、求職者…

Windows 下 VS2019 編譯 libevent-2.1.10 庫

1. 你需要VS2019 編譯好openssl-1.1.1 &#xff0c;這個具體編譯或者下載可以參考我的博客openssl生成的庫是這兩個文件接下來&#xff0c;打開CMake &#xff0c;主要是下面的需要設置好最后Config Generate即可&#xff1b;全部成功生成 22個然后INSTALL右鍵生成 最后看下生…

Vim多列操作指南

我們在使用 Vim 時&#xff0c;經常需要同時編輯多個文件&#xff0c;或者同一個文件的不同部分。Vim 提供了分割窗口&#xff08;split&#xff09;和垂直分割窗口&#xff08;vsplit&#xff09;的功能&#xff0c;允許我們在同一個 Vim 會話中查看多個緩沖區&#xff08;buf…