Linux INotif機(jī)制詳解及實(shí)例代碼
Linux INotif機(jī)制
一、 前言:
眾所周知,Linux 桌面系統(tǒng)與 MAC 或 Windows 相比有許多不如人意的地方,為了改善這種狀況,開源社區(qū)提出用戶態(tài)需要內(nèi)核提供一些機(jī)制,以便用戶態(tài)能夠及時(shí)地得知內(nèi)核或底層硬件設(shè)備發(fā)生了什么,從而能夠更好地管理設(shè)備,給用戶提供更好的服務(wù),如 hotplug、udev 和 inotify 就是這種需求催生的。Hotplug 是一種內(nèi)核向用戶態(tài)應(yīng)用通報(bào)關(guān)于熱插拔設(shè)備一些事件發(fā)生的機(jī)制,桌面系統(tǒng)能夠利用它對設(shè)備進(jìn)行有效的管理,udev 動(dòng)態(tài)地維護(hù) /dev 下的設(shè)備文件,inotify 是一種文件系統(tǒng)的變化通知機(jī)制,如文件增加、刪除等事件可以立刻讓用戶態(tài)得知,該機(jī)制是著名的桌面搜索引擎項(xiàng)目 beagle 引入的,并在 Gamin 等項(xiàng)目中被應(yīng)用。
事實(shí)上,在 inotify 之前已經(jīng)存在一種類似的機(jī)制叫 dnotify,但是它存在許多缺陷:
1. 對于想監(jiān)視的每一個(gè)目錄,用戶都需要打開一個(gè)文件描述符,因此如果需要監(jiān)視的目錄較多,將導(dǎo)致打開許多文件描述符,特別是,如果被監(jiān)視目錄在移動(dòng)介質(zhì)上(如光盤和 USB 盤),將導(dǎo)致無法 umount 這些文件系統(tǒng),因?yàn)槭褂?dnotify 的應(yīng)用打開的文件描述符在使用該文件系統(tǒng)。
2. dnotify 是基于目錄的,它只能得到目錄變化事件,當(dāng)然在目錄內(nèi)的文件的變化會(huì)影響到其所在目錄從而引發(fā)目錄變化事件,但是要想通過目錄事件來得知哪個(gè)文件變化,需要緩存許多 stat 結(jié)構(gòu)的數(shù)據(jù)。
3. Dnotify 的接口非常不友好,它使用 signal。
Inotify 是為替代 dnotify 而設(shè)計(jì)的,它克服了 dnotify 的缺陷,提供了更好用的,簡潔而強(qiáng)大的文件變化通知機(jī)制:
1. Inotify 不需要對被監(jiān)視的目標(biāo)打開文件描述符,而且如果被監(jiān)視目標(biāo)在可移動(dòng)介質(zhì)上,那么在 umount 該介質(zhì)上的文件系統(tǒng)后,被監(jiān)視目標(biāo)對應(yīng)的 watch 將被自動(dòng)刪除,并且會(huì)產(chǎn)生一個(gè) umount 事件。
2. Inotify 既可以監(jiān)視文件,也可以監(jiān)視目錄。
3. Inotify 使用系統(tǒng)調(diào)用而非 SIGIO 來通知文件系統(tǒng)事件。
4. Inotify 使用文件描述符作為接口,因而可以使用通常的文件 I/O 操作select 和 poll 來監(jiān)視文件系統(tǒng)的變化。
Inotify 可以監(jiān)視的文件系統(tǒng)事件包括:
IN_ACCESS,即文件被訪問
IN_MODIFY,文件被 write
IN_ATTRIB,文件屬性被修改,如 chmod、chown、touch 等
IN_CLOSE_WRITE,可寫文件被 close
IN_CLOSE_NOWRITE,不可寫文件被 close
IN_OPEN,文件被 open
IN_MOVED_FROM,文件被移走,如 mv
IN_MOVED_TO,文件被移來,如 mv、cp
IN_CREATE,創(chuàng)建新文件
IN_DELETE,文件被刪除,如 rm
IN_DELETE_SELF,自刪除,即一個(gè)可執(zhí)行文件在執(zhí)行時(shí)刪除自己
IN_MOVE_SELF,自移動(dòng),即一個(gè)可執(zhí)行文件在執(zhí)行時(shí)移動(dòng)自己
IN_UNMOUNT,宿主文件系統(tǒng)被 umount
IN_CLOSE,文件被關(guān)閉,等同于(IN_CLOSE_WRITE | IN_CLOSE_NOWRITE)
IN_MOVE,文件被移動(dòng),等同于(IN_MOVED_FROM | IN_MOVED_TO)
注:上面所說的文件也包括目錄。
二、用戶接口
在用戶態(tài),inotify 通過三個(gè)系統(tǒng)調(diào)用和在返回的文件描述符上的文件 I/ 操作來使用,使用 inotify 的第一步是創(chuàng)建 inotify 實(shí)例:
int fd = inotify_init ();
每一個(gè) inotify 實(shí)例對應(yīng)一個(gè)獨(dú)立的排序的隊(duì)列。
文件系統(tǒng)的變化事件被稱做 watches 的一個(gè)對象管理,每一個(gè) watch 是一個(gè)二元組(目標(biāo),事件掩碼),目標(biāo)可以是文件或目錄,事件掩碼表示應(yīng)用希望關(guān)注的 inotify 事件,每一個(gè)位對應(yīng)一個(gè) inotify 事件。Watch 對象通過 watch描述符引用,watches 通過文件或目錄的路徑名來添加。目錄 watches 將返回在該目錄下的所有文件上面發(fā)生的事件。
下面函數(shù)用于添加一個(gè) watch:
int wd = inotify_add_watch (fd, path, mask);
fd 是 inotify_init() 返回的文件描述符,path 是被監(jiān)視的目標(biāo)的路徑名(即文件名或目錄名),mask 是事件掩碼, 在頭文件 linux/inotify.h 中定義了每一位代表的事件??梢允褂猛瑯拥姆绞絹硇薷氖录诖a,即改變希望被通知的inotify 事件。Wd 是 watch 描述符。
下面的函數(shù)用于刪除一個(gè) watch:
int ret = inotify_rm_watch (fd, wd);
fd 是 inotify_init() 返回的文件描述符,wd 是 inotify_add_watch() 返回的 watch 描述符。Ret 是函數(shù)的返回值。
文件事件用一個(gè) inotify_event 結(jié)構(gòu)表示,它通過由 inotify_init() 返回的文件描述符使用通常文件讀取函數(shù) read 來獲得
struct inotify_event { __s32 wd; /* watch descriptor */ __u32 mask; /* watch mask */ __u32 cookie; /* cookie to synchronize two events */ __u32 len; /* length (including nulls) of name */ char name[0]; /* stub for possible name */ };
結(jié)構(gòu)中的 wd 為被監(jiān)視目標(biāo)的 watch 描述符,mask 為事件掩碼,len 為 name字符串的長度,name 為被監(jiān)視目標(biāo)的路徑名,該結(jié)構(gòu)的 name 字段為一個(gè)樁,它只是為了用戶方面引用文件名,文件名是變長的,它實(shí)際緊跟在該結(jié)構(gòu)的后面,文件名將被 0 填充以使下一個(gè)事件結(jié)構(gòu)能夠 4 字節(jié)對齊。注意,len 也把填充字節(jié)數(shù)統(tǒng)計(jì)在內(nèi)。
通過 read 調(diào)用可以一次獲得多個(gè)事件,只要提供的 buf 足夠大。
size_t len = read (fd, buf, BUF_LEN);
buf 是一個(gè) inotify_event 結(jié)構(gòu)的數(shù)組指針,BUF_LEN 指定要讀取的總長度,buf 大小至少要不小于 BUF_LEN,該調(diào)用返回的事件數(shù)取決于 BUF_LEN 以及事件中文件名的長度。Len 為實(shí)際讀去的字節(jié)數(shù),即獲得的事件的總長度。
可以在函數(shù) inotify_init() 返回的文件描述符 fd 上使用 select() 或poll(), 也可以在 fd 上使用 ioctl 命令 FIONREAD 來得到當(dāng)前隊(duì)列的長度。close(fd)將刪除所有添加到 fd 中的 watch 并做必要的清理。
int inotify_init (void); int inotify_add_watch (int fd, const char *path, __u32 mask); int inotify_rm_watch (int fd, __u32 mask);
三、內(nèi)核實(shí)現(xiàn)原理
在內(nèi)核中,每一個(gè) inotify 實(shí)例對應(yīng)一個(gè) inotify_device 結(jié)構(gòu):
struct inotify_device { wait_queue_head_t wq; /* wait queue for i/o */ struct idr idr; /* idr mapping wd -> watch */ struct semaphore sem; /* protects this bad boy */ struct list_head events; /* list of queued events */ struct list_head watches; /* list of watches */ atomic_t count; /* reference count */ struct user_struct *user; /* user who opened this dev */ unsigned int queue_size; /* size of the queue (bytes) */ unsigned int event_count; /* number of pending events */ unsigned int max_events; /* maximum number of events */ u32 last_wd; /* the last wd allocated */ };
wq 是等待隊(duì)列,被 read 調(diào)用阻塞的進(jìn)程將掛在該等待隊(duì)列上,idr 用于把 watch 描述符映射到對應(yīng)的 inotify_watch,sem 用于同步對該結(jié)構(gòu)的訪問,events 為該 inotify 實(shí)例上發(fā)生的事件的列表,被該 inotify 實(shí)例監(jiān)視的所有事件在發(fā)生后都將插入到這個(gè)列表,watches 是給 inotify 實(shí)例監(jiān)視的 watch 列表,inotify_add_watch 將把新添加的 watch 插入到該列表,count 是引用計(jì)數(shù),user 用于描述創(chuàng)建該 inotify 實(shí)例的用戶,queue_size 表示該 inotify 實(shí)例的事件隊(duì)列的字節(jié)數(shù),event_count 是 events 列表的事件數(shù),max_events 為最大允許的事件數(shù),last_wd 是上次分配的 watch 描述符。
每一個(gè) watch 對應(yīng)一個(gè) inotify_watch 結(jié)構(gòu):
struct inotify_watch { struct list_head d_list; /* entry in inotify_device's list */ struct list_head i_list; /* entry in inode's list */ atomic_t count; /* reference count */ struct inotify_device *dev; /* associated device */ struct inode *inode; /* associated inode */ s32 wd; /* watch descriptor */ u32 mask; /* event mask for this watch */ };
d_list 指向所有 inotify_device 組成的列表的,i_list 指向所有被監(jiān)視 inode 組成的列表,count 是引用計(jì)數(shù),dev 指向該 watch 所在的 inotify 實(shí)例對應(yīng)的 inotify_device 結(jié)構(gòu),inode 指向該 watch 要監(jiān)視的 inode,wd 是分配給該 watch 的描述符,mask 是該 watch 的事件掩碼,表示它對哪些文件系統(tǒng)事件感興趣。
結(jié)構(gòu) inotify_device 在用戶態(tài)調(diào)用 inotify_init() 時(shí)創(chuàng)建,當(dāng)關(guān)閉 inotify_init()返回的文件描述符時(shí)將被釋放。結(jié)構(gòu) inotify_watch 在用戶態(tài)調(diào)用 inotify_add_watch()時(shí)創(chuàng)建,在用戶態(tài)調(diào)用 inotify_rm_watch() 或 close(fd) 時(shí)被釋放。
無論是目錄還是文件,在內(nèi)核中都對應(yīng)一個(gè) inode 結(jié)構(gòu),inotify 系統(tǒng)在 inode 結(jié)構(gòu)中增加了兩個(gè)字段:
#ifdef CONFIG_INOTIFY struct list_head inotify_watches; /* watches on this inode */ struct semaphore inotify_sem; /* protects the watches list */ #endif
inotify_watches 是在被監(jiān)視目標(biāo)上的 watch 列表,每當(dāng)用戶調(diào)用 inotify_add_watch()時(shí),內(nèi)核就為添加的 watch 創(chuàng)建一個(gè) inotify_watch 結(jié)構(gòu),并把它插入到被監(jiān)視目標(biāo)對應(yīng)的 inode 的 inotify_watches 列表。inotify_sem 用于同步對 inotify_watches 列表的訪問。當(dāng)文件系統(tǒng)發(fā)生第一部分提到的事件之一時(shí),相應(yīng)的文件系統(tǒng)代碼將顯示調(diào)用fsnotify_* 來把相應(yīng)的事件報(bào)告給
inotify 系統(tǒng),其中*號就是相應(yīng)的事件名,目前實(shí)現(xiàn)包括:
fsnotify_move,文件從一個(gè)目錄移動(dòng)到另一個(gè)目錄
fsnotify_nameremove,文件從目錄中刪除
fsnotify_inoderemove,自刪除
fsnotify_create,創(chuàng)建新文件
fsnotify_mkdir,創(chuàng)建新目錄
fsnotify_access,文件被讀
fsnotify_modify,文件被寫
fsnotify_open,文件被打開
fsnotify_close,文件被關(guān)閉
fsnotify_xattr,文件的擴(kuò)展屬性被修改
fsnotify_change,文件被修改或原數(shù)據(jù)被修改
有一個(gè)例外情況,就是 inotify_unmount_inodes,它會(huì)在文件系統(tǒng)被 umount 時(shí)調(diào)用來通知 umount 事件給 inotify 系統(tǒng)。
以上提到的通知函數(shù)最后都調(diào)用 inotify_inode_queue_event(inotify_unmount_inodes直接調(diào)用 inotify_dev_queue_event ),該函數(shù)首先判斷對應(yīng)的inode是否被監(jiān)視,這通過查看 inotify_watches 列表是否為空來實(shí)現(xiàn),如果發(fā)現(xiàn) inode 沒有被監(jiān)視,什么也不做,立刻返回,反之,遍歷 inotify_watches 列表,看是否當(dāng)前的文件操作事件被某個(gè) watch 監(jiān)視,如果是,調(diào)用 inotify_dev_queue_event,否則,返回。函數(shù)inotify_dev_queue_event 首先判斷該事件是否是上一個(gè)事件的重復(fù),如果是就丟棄該事件并返回,否則,它判斷是否 inotify 實(shí)例即 inotify_device 的事件隊(duì)列是否溢出,如果溢出,產(chǎn)生一個(gè)溢出事件,否則產(chǎn)生一個(gè)當(dāng)前的文件操作事件,這些事件通過kernel_event 構(gòu)建,kernel_event 將創(chuàng)建一個(gè) inotify_kernel_event 結(jié)構(gòu),然后把該結(jié)構(gòu)插入到對應(yīng)的 inotify_device 的 events 事件列表,然后喚醒等待在inotify_device 結(jié)構(gòu)中的 wq 指向的等待隊(duì)列。想監(jiān)視文件系統(tǒng)事件的用戶態(tài)進(jìn)程在inotify 實(shí)例(即 inotify_init() 返回的文件描述符)上調(diào)用 read 時(shí)但沒有事件時(shí)就掛在等待隊(duì)列 wq 上。
四、使用示例
下面是一個(gè)使用 inotify 來監(jiān)視文件系統(tǒng)事件的例子:
#include <linux/unistd.h> #include <linux/inotify.h> #include <errno.h> _syscall0(int, inotify_init) _syscall3(int, inotify_add_watch, int, fd, const char *, path, __u32, mask) _syscall2(int, inotify_rm_watch, int, fd, __u32, mask) char * monitored_files[] = { "./tmp_file", "./tmp_dir", "/mnt/sda3/windows_file" }; struct wd_name { int wd; char * name; }; #define WD_NUM 3 struct wd_name wd_array[WD_NUM]; char * event_array[] = { "File was accessed", "File was modified", "File attributes were changed", "writtable file closed", "Unwrittable file closed", "File was opened", "File was moved from X", "File was moved to Y", "Subfile was created", "Subfile was deleted", "Self was deleted", "Self was moved", "", "Backing fs was unmounted", "Event queued overflowed", "File was ignored" }; #define EVENT_NUM 16 #define MAX_BUF_SIZE 1024 int main(void) { int fd; int wd; char buffer[1024]; char * offset = NULL; struct inotify_event * event; int len, tmp_len; char strbuf[16]; int i = 0; fd = inotify_init(); if (fd < 0) { printf("Fail to initialize inotify.\n"); exit(-1); } for (i=0; i<WD_NUM; i++) { wd_array[i].name = monitored_files[i]; wd = inotify_add_watch(fd, wd_array[i].name, IN_ALL_EVENTS); if (wd < 0) { printf("Can't add watch for %s.\n", wd_array[i].name); exit(-1); } wd_array[i].wd = wd; } while(len = read(fd, buffer, MAX_BUF_SIZE)) { offset = buffer; printf("Some event happens, len = %d.\n", len); event = (struct inotify_event *)buffer; while (((char *)event - buffer) < len) { if (event->mask & IN_ISDIR) { memcpy(strbuf, "Direcotory", 11); } else { memcpy(strbuf, "File", 5); } printf("Object type: %s\n", strbuf); for (i=0; i<WD_NUM; i++) { if (event->wd != wd_array[i].wd) continue; printf("Object name: %s\n", wd_array[i].name); break; } printf("Event mask: %08X\n", event->mask); for (i=0; i<EVENT_NUM; i++) { if (event_array[i][0] == '\0') continue; if (event->mask & (1<<i)) { printf("Event: %s\n", event_array[i]); } } tmp_len = sizeof(struct inotify_event) + event->len; event = (struct inotify_event *)(offset + tmp_len); offset += tmp_len; } } }
該程序?qū)⒈O(jiān)視發(fā)生在當(dāng)前目錄下的文件 tmp_file 與當(dāng)前目錄下的目錄 tmp_dir 上的所有文件系統(tǒng)事件, 同時(shí)它也將監(jiān)視發(fā)生在文件 /mnt/sda3/windows_file 上的文件系統(tǒng)事件,注意,/mnt/sda3 是 SATA 硬盤分區(qū) 3 的掛接點(diǎn)。
細(xì)心的讀者可能注意到,該程序首部使用 _syscallN 來聲明 inotify 系統(tǒng)調(diào)用,原因是這些系統(tǒng)調(diào)用是在最新的穩(wěn)定內(nèi)核 2.6.13 中引入的,glibc 并沒有實(shí)現(xiàn)這些系統(tǒng)調(diào)用的庫函數(shù)版本,因此,為了能在程序中使用這些系統(tǒng)調(diào)用,必須通過 _syscallN 來聲明這些新的系統(tǒng),其中的 N 為要聲明的系統(tǒng)調(diào)用實(shí)際的參數(shù)數(shù)。還有需要注意的地方是系統(tǒng)的頭文件必須與被啟動(dòng)的內(nèi)核匹配,為了讓上面的程序能夠成功編譯,必須讓 2.6.13 的內(nèi)核頭文件(包括 include/linux/*, include/asm/* 和 include/asm-generic/*)在頭文件搜索路徑內(nèi),并且是第一優(yōu)先搜索的頭文件路徑,因?yàn)?_syscallN 需要用到這些頭文件中的 linux/unistd.h 和 asm/unistd.h,它們包含了 inotify 的三個(gè)系統(tǒng)調(diào)用的系統(tǒng)調(diào)用號 __NR_inotify_init、__NR_inotify_add_watch 和 __NR_inotify_rm_watch。
因此,要想成功編譯此程序,只要把用戶編譯好的內(nèi)核的頭文件拷貝到該程序所在的路徑,并使用如下命令編譯即可:
$gcc -o inotify_example -I. inotify_example.c
注意:當(dāng)前目錄下應(yīng)當(dāng)包含 linux、asm 和 asm-generic 三個(gè)已編譯好的 2.6.13 內(nèi)核的有文件目錄,asm 是一個(gè)鏈接,因此拷貝 asm 頭文件的時(shí)候需要拷貝 asm 與 asm-ARCH(對于 x86 平臺應(yīng)當(dāng)是 asm-i386)。 然后,為了運(yùn)行該程序,需要在當(dāng)前目錄下創(chuàng)建文件 tmp_file 和目錄 tmp_dir,對于/mnt/sda3/windows_file 文件,用戶需要依自己的實(shí)際情況而定,可能是/mnt/dosc/windows_file,即 /mnt/dosc 是一個(gè) FAT32 的 windows 硬盤,因此用戶在編譯該程序時(shí)需要根據(jù)自己的實(shí)際情況來修改 /mnt/sda3。Windows_file 是在被 mount 硬盤上創(chuàng)建的一個(gè)文件,為了運(yùn)行該程序,它必須被創(chuàng)建。
以下是作者在 redhat 9.0 上運(yùn)行此程序得到的一些結(jié)果:
當(dāng)運(yùn)行此程序的時(shí)候在另一個(gè)虛擬終端執(zhí)行 cat ./tmp_file,此程序的輸出為:
Some event happens, len = 48. Object type: File Object name: ./tmp_file Event mask: 00000020 Event: File was opened Object type: File Object name: ./tmp_file Event mask: 00000001 Event: File was accessed Object type: File Object name: ./tmp_file Event mask: 00000010 Event: Unwrittable file closed
以上事件清楚地說明了 cat 指令執(zhí)行了文件 open 和 close 操作,當(dāng)然 open 和 close操作都屬于 access 操作,任何對文件的操作都是 access 操作。
此外,運(yùn)行 vi ./tmp_file,發(fā)現(xiàn) vi實(shí)際在編輯文件時(shí)復(fù)制了一個(gè)副本,在未保存之前是對副本進(jìn)行操作。 運(yùn)行 vi ./tmp_file, 修改并保存退出時(shí),發(fā)現(xiàn) vi 實(shí)際在保存修改時(shí)刪除了最初的文件并把那個(gè)副本文件名更改為最初的文件的名稱。注意,事件"File was ignored"表示系統(tǒng)把該文件對應(yīng)的 watch 從 inotify 實(shí)例的 watch 列表中刪除,因?yàn)槲募呀?jīng)被刪除。 讀者可以自己分別執(zhí)行命令:echo "abc" > ./tmp_file 、rm -f tmp_file、 ls tmp_dir、 cd tmp_dir;touch c.txt、 rm c.txt 、 umount /mnt/sda3(實(shí)際用戶需要使用自己當(dāng)時(shí)的 mount 點(diǎn)路徑名),然后分析一下結(jié)果。Umount 觸發(fā)兩個(gè)事件,一個(gè)表示文件已經(jīng)被刪除或不在存在,另一個(gè)表示該文件的 watch被從 watch 列表中刪除。
感謝閱讀,希望能幫助到大家,謝謝大家對本站的支持!
- linux系統(tǒng)中rsync+inotify實(shí)現(xiàn)服務(wù)器之間文件實(shí)時(shí)同步
- linux系統(tǒng)中通過rsync+inotify實(shí)現(xiàn)網(wǎng)頁自動(dòng)同步
- linux下通過rsync+inotify 實(shí)現(xiàn)數(shù)據(jù)實(shí)時(shí)備份(遠(yuǎn)程容災(zāi)備份系統(tǒng))
- Linux下安裝配置nginx詳解
- linux平臺使用Python制作BT種子并獲取BT種子信息的方法
- linux配置jdk環(huán)境變量簡單教程
- Linux下壓縮與解壓命令詳解
- linux下source命令使用詳解
- linux中Jetty的安裝和配置方法
- Linux shell常用的73條命令總結(jié)
相關(guān)文章
Apache 內(nèi)容動(dòng)態(tài)緩沖模塊 mod_cache應(yīng)用
mod_cache是apache中基于URI鍵的內(nèi)容動(dòng)態(tài)緩沖(內(nèi)存或磁盤),從Apache2.2起,mod_cache和mod_file_cache將不再是試驗(yàn)?zāi)K,它們已經(jīng)足夠穩(wěn)定,可以用于實(shí)際生產(chǎn)中了。2013-10-102018即將推出的Apache Spark 2.4都有哪些新功能
即將發(fā)布的 Apache Spark 2.4 版本是 2.x 系列的第五個(gè)版本。 本文對Apache Spark 2.4 的主要功能和增強(qiáng)功能進(jìn)行了概述,需要的朋友可以參考下2018-09-09linux 服務(wù)器自動(dòng)備份腳本的方法(mysql、附件備份)
這篇文章主要介紹了linux 服務(wù)器自動(dòng)備份腳本(mysql、附件備份),本文通過實(shí)例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-01-01總結(jié)Centos7系統(tǒng)加固知識點(diǎn)
本篇文章給大家詳細(xì)介紹了LINUX中Centos7系統(tǒng)加固的相關(guān)知識點(diǎn),如果大家對此有需要跟著學(xué)習(xí)下吧。2018-02-02Linux中驗(yàn)證tar包在傳輸后的完整性的過程詳解
在日常的系統(tǒng)管理或軟件部署過程中,文件傳輸是一項(xiàng)常規(guī)操作,特別是當(dāng)處理大型文件(如tar包)時(shí),驗(yàn)證文件的完整性變得尤為重要,本文將介紹如何使用校驗(yàn)和來驗(yàn)證tar包在從一臺機(jī)器傳輸?shù)搅硪慌_機(jī)器后的完整性,需要的朋友可以參考下2023-12-12在 Linux 上鎖定虛擬控制臺會(huì)話的實(shí)現(xiàn)方法
這篇文章主要介紹了在 Linux 上鎖定虛擬控制臺會(huì)話的實(shí)現(xiàn)方法,Vlock 對于有多個(gè)用戶訪問控制臺的共享 Linux 系統(tǒng)特別有用,文中給大家提到了vlock的安裝方法,需要的朋友可以參考下2018-11-11