主頁 > 作業系統 > 面試官讓你講講Linux內核的競爭與并發,你該如何回答?

面試官讓你講講Linux內核的競爭與并發,你該如何回答?

2020-12-27 06:09:13 作業系統

@

目錄
  • 內核中的并發和競爭簡介
  • 原子操作
    • 原子操作簡介
    • 整型原子操作函式
    • 位原子操作函式
    • 原子操作例程
  • 自旋鎖
    • 自旋鎖簡介
    • 自旋鎖操作函式
    • 自旋鎖例程
    • 讀寫自旋鎖
    • 讀寫鎖例程
    • 順序鎖
    • 順序鎖操作函式
    • 自旋鎖使用注意事項
  • 信號量
    • 信號量簡介
    • 信號量操作函式
    • 信號量例程
  • 互斥體
    • 互斥體簡介
    • 互斥體操作函式
    • 互斥體例程
    • 互斥體與自旋鎖
    • 互斥體使用注意事項

內核中的并發和競爭簡介

??在早期的 Linux內核中,并發的來源相對較少,早期內核不支持對稱多處理( symmetric multi processing,SMP),因此,導致并發執行的唯一原因是對硬體中斷的服務,這種情況處理起來較為簡單,但并不適用于為獲得更好的性能而使用更多處理器且強調快速回應事件的系統,

??為了回應現代硬體和應用程式的需求, Linux內核已經發展到同時處理更多事情的時代,Linux系統是個多任務作業系統,會存在多個任務同時訪問同一片記憶體區域的情況,這些任務可能會相互覆寫這段記憶體中的資料,造成記憶體資料混亂,針對這個問題必須要做處理,嚴重的話可能會導致系統崩潰,現在的 Linux系統并發產生的原因很復雜,總結一下有下面幾個主要原因:

  1. 多執行緒并發訪問, Linux是多任務(執行緒)的系統,所以多執行緒訪問是最基本的原因,
  2. 搶占式并發訪問,內核代碼是可搶占的,因此,我們的驅動程式代碼可能在任何時候丟失對處理器的獨占
  3. 中斷程式并發訪問,設備中斷是異步事件,也會導致代碼的并發執行,
  4. SMP(多核)核間并發訪問,現在ARM架構的多核SOC很常見,多核CPU存在核間并發訪問,正在運行的多個用戶空間行程可能以一種令人驚訝的組合方式訪問我們的代碼,SMP系統甚至可在不同的處理器上同時執行我們的代碼,

??只要我們的程式在運轉當中,就有可能發生并發和競爭,比如,當兩個執行執行緒需要訪問相同的資料結構(或硬體資源)時,混合的可能性就永遠存在,因此在設計自己的驅動程式時,就應該避免資源的共享,如果沒有并發的訪問,也就不會有競態的產生,因此,仔細撰寫的內核代碼應該具有最少的共享,這種思想的最明顯應用就是避免使用全域變數,如果我們將資源放在多個執行執行緒都會找到的地方(臨界區),則必須有足夠的理由,

??如何防止我們的資料被并發訪問呢?這個時候就要建立一種保護機制,下面介紹幾種內核提供的幾種并發和競爭的處理方法,

原子操作

原子操作簡介

??原子,在早接觸到是在化學概念中,原子指化學反應不可再分的基本微粒,同樣的,在內核中所說的原子操作表示這一個訪問是一個步驟,必須一次性執行完,不能被打斷,不能再進行拆分,
??例如,在多執行緒訪問中,我們的執行緒一對a進行賦值操作,a=1,執行緒二也對a進行賦值操作a=2,我們理想的執行順序是執行緒一先執行,執行緒二再執行,但是很有可能在執行緒一執行的時候被其他操作打斷,使得執行緒一最后的執行結果變為a=2,要解決這個問題,必須保證我們的執行緒一在對資料訪問的程序中不能被其他的操作打斷,一次性執行完成,

整型原子操作函式

函式 描述
ATOMIC_INIT(int i) 定義原子變數的時候對其初始化,
int atomic_read(atomic_t*v) 讀取 v的值,并且回傳
void atomic_set(atomic_t *v, int i) 向 v寫入 i值,
void atomic_add(int i, atomic_t *v) 給 v加上 i值,
void atomic_sub(int i, atomic_t *v) 從 v減去 i值,
void atomic_inc(atomic_t *v) 給 v加 1,也就是自增,
void atomic_dec(atomic_t *v) 從 v減 1,也就是自減 ,
int atomic_dec_return(atomic_t *v) 從 v減 1,并且回傳v的值 ,
int atomic_inc_return(atomic_t *v) 給 v加 1,并且回傳 v的值,
int atomic_sub_and_test(int i, atomic_t *v) 從 v減 i,如果結果為0就回傳真,否則就回傳假
int atomic_dec_and_test(atomic_t *v) 從 v減 1,如果結果為0就回傳真,否則就回傳假
int atomic_inc_and_test(atomic_t *v) 給 v加 1,如果結果為0就回傳真,否則就回傳假
int atomic_add_negative(int i, atomic_t *v) 給 v加 i,如果結果為負就回傳真,否則回傳假

注:64位的整型原子操作只是將“atomic_”前綴換成“atomic64_”,將int換成long long,

位原子操作函式

函式 描述
void set_bit(int nr, void *p) 將p地址的nr位置1
void clear_bit(int nr,void *p) 將p地址的nr位清零
void change_bit(int nr, void *p) 將p地址的nr位反轉
int test_bit(int nr, void *p) 獲取p地址的nr位的值
int test_and_set_bit(int nr, void *p) 將p地址的nr位置1,并且回傳nr位原來的值
int test_and_clear_bit(int nr, void *p) 將p地址的nr位清0,并且回傳nr位原來的值
int test_and_change_bit(int nr, void *p) 將p地址的nr位翻轉,并且回傳nr位原來的值

原子操作例程

/* 定義原子變數,初值為1*/
static atomic_t xxx_available = ATOMIC_INIT(1); 
static int xxx_open(struct inode *inode, struct file *filp)
{
 ...
 /* 通過判斷原子變數的值來檢查LED有沒有被別的應用使用 */
 if (!atomic_dec_and_test(&xxx_available)) {
 /*小于0的話就加1,使其原子變數等于0*/
 atomic_inc(&xxx_available);
 /* LED被使用,回傳忙*/
 return - EBUSY; 
 }
...
/* 成功 */
 return 0;
static int xxx_release(struct inode *inode, struct file *filp)
{
 /* 關閉驅動檔案的時候釋放原子變數 */
 atomic_inc(&xxx_available); 
 return 0;
}

自旋鎖

??上面我們介紹了原子變數,從它的操作函式可以看出,原子操作只能針對整型變數或者位,假如我們有一個結構體變數需要被執行緒A所訪問,在執行緒A訪問期間不能被其他執行緒訪問,這怎么辦呢?自旋鎖就可以完成對結構體變數的保護,

自旋鎖簡介

??自旋鎖,顧名思義,我們可以把他理解成廁所門上的一把鎖,這個廁所門只有一把鑰匙,當我們進去時,把鑰匙取下來,進去后反鎖,那么當第二個人想進來,必須等我們出去后才可以,當第二個人在外面等待時,可能會一直等待在門口轉圈,

??我們的自旋鎖也是這樣,自旋鎖只有鎖定和解鎖兩個狀態,當我們進入拿上鑰匙進入廁所,這就相當于自旋鎖鎖定的狀態,期間誰也不可以進來,當第二個人想要進來,這相當于執行緒B想要訪問這個共享資源,但是目前不能訪問,所以執行緒B就一直在原地等待,一直查詢是否可以訪問這個共享資源,當我們從廁所出來后,這個時候就“解鎖”了,只有再這個時候執行緒B才能訪問,

??假如,在廁所的人待的時間太長怎么辦?外面的人一直等待嗎?如果換做是我們,肯定不會這樣,簡直浪費時間,可能我們會尋找其他方法解決問題,自旋鎖也是這樣的,如果執行緒A持有自旋鎖時間過長,顯然會浪費處理器的時間,降低了系統性能,我們知道CPU最偉大的發明就在于多執行緒操作,這個時候讓執行緒B在這里傻傻的不知道還要等待多久,顯然是不合理的,因此,如果自旋鎖只適合短期持有,如果遇到需要長時間持有的情況,我們就要換一種方式了(下文的互斥體),

自旋鎖操作函式

函式 描述
DEFINE_SPINLOCK(spinlock_t lock) 定義并初始化一個自旋變數
int spin_lock_init(spinlock_t *lock) 初始化自旋鎖
void spin_lock(spinlock_t *lock) 獲取指定的自旋鎖,也叫加鎖
void spin_unlock(spinlock_t *lock) 釋放指定的自旋鎖,
int spin_trylock(spinlock_t *lock) 嘗試獲取指定的鎖,如果沒有獲取到,回傳0
int spin_is_locked(spinlock_t *lock) 檢查指定的自旋鎖是否被獲取,如果沒有被獲取回傳非0,否則回傳0.

??自旋鎖是主要為了多處理器系統設計的,對于單處理器且內核不支持搶占的系統,一旦進入了自旋狀態,則會永遠自旋下去,因為,沒有任何執行緒可以獲取CPU來釋放這個鎖,因此,在單處理器且內核不支持搶占的系統中,自旋鎖會被設定為空操作

??以上串列中的函式適用于SMP或支持搶占的單CPU下執行緒之間的并發訪問,也就是用于執行緒與執行緒之間,被自旋鎖保護的臨界區一定不能呼叫任何能夠引起睡眠和阻塞(其實本質仍然是睡眠)的API函式,否則的話會可能會導致死鎖現象的發生,自旋鎖會自動禁止搶占,也就說當執行緒A得到鎖以后會暫時禁止內核搶占,如果執行緒A在持有鎖期間進入了休眠狀態,那么執行緒A會自動放棄CPU使用權,執行緒B開始運行,執行緒B也想要獲取鎖,但是此時鎖被A執行緒持有,而且內核搶占還被禁止了!執行緒B無法被調度岀去,那么執行緒A就無法運行,鎖也就無法釋放死鎖發生了!

??當執行緒之間發生并發訪問時,如果此時中斷也要插一腳,中斷也想訪問共享資源,那該怎么辦呢?首先可以肯定的是,中斷里面使用自旋鎖,但是在中斷里面使用自旋鎖的時候,在獲取鎖之前一定要先禁止本地中斷(也就是本CPU中斷,對于多核SOC來說會有多個CPU核),否則可能導致鎖死現象的發生,看下下面一個例子:

//執行緒A
spin_lock(&lock);
.......
functionA();
.......
spin_unlock(&lock);

//中斷發生,運行執行緒B
spin_lock(&lock);
.......
functionA();
.......
spin_unlock(&lock);

??執行緒A先運行,并且獲取到了lock這個鎖,當執行緒A運行 functionA函式的時候中斷發生了,中斷搶走了CPU使用權,下邊的中斷服務函式也要獲取lock這個鎖,但是這個鎖被執行緒A占有著,中斷就會一直自旋,等待鎖有效,但是在中斷服務函式執行完之前,執行緒A是不可能執行的,執行緒A說“你先放手”,中斷說“你先放手”,場面就這么僵持著死鎖發生!

??使用了自旋鎖之后可以保證臨界區不受別的CPU和本CPU內的搶占行程的打擾,但是得到鎖的代碼在執行臨界區的時候,還可能受到中斷和底半部的影響,為了防止這種影響,建議使用以下串列中的函式:

函式 描述
void spin_lock_irq(spinlock_t *lock) 禁止本地中斷,并獲取自旋鎖
void spin_unlock_irq(spinlock_t *lock) 激活本地中斷,并釋放自旋鎖
void spin_lock_irqsave(spinlock_t *lock, unsigned long flags) 保存中斷狀態,禁止本地中斷,并獲取自旋鎖
void spin_unlock_irqrestore(spinlock_t *lock, unsigned long flags)?????? 將中斷狀態恢復到以前的狀態,并且激活本地中斷,釋放自旋鎖

??在多核編程的時候,如果行程和中斷可能訪問同一片臨界資源,我們一般需要在行程背景關系中呼叫spin_ lock irqsave() spin_unlock_irqrestore(),在中斷背景關系中呼叫 spin_lock() spin _unlock(),這樣,在CPU上,無論是行程背景關系,還是中斷背景關系獲得了自旋鎖,此后,如果CPU1無論是行程背景關系,還是中斷背景關系,想獲得同一自旋鎖,都必須忙等待,這避免一切核間并發的可能性,同時,由于每個核的行程背景關系持有鎖的時候用的是 spin_lock_irgsave(),所以該核上的中斷是不可能進入的,這避免了核內并發的可能性,

DEFINE_SPINLOCK(lock) /* 定義并初始化一個鎖 */ 
/* 執行緒A */
void functionA (){ 
unsigned long flags; /* 中斷狀態 */
 spin_lock_irqsave(&lock, flags) /* 獲取鎖 */ 
  /* 臨界區 */ 
spin_unlock_irqrestore(&lock, flags) /* 釋放鎖 */ 
} 
 /* 中斷服務函式 */
 void irq() { 
 spin_lock(&lock) /* 獲取鎖 */ 
   /* 臨界區 */ 
 spin_unlock(&lock) /* 釋放鎖 */ 
}

自旋鎖例程

static int xxx_open(struct inode *inode, struct file *filp)
{
...
	spinlock(&xxx_lock);
	if (xxx_count) {/* 已經打開*/
	spin_unlock(&xxx_lock);
	return -EBUSY;
 }
	 xxx_count++;/* 增加使用計數*/
 	spin_unlock(&xxx_lock);
 ...
	 return 0;/* 成功 */
}

static int xxx_release(struct inode *inode, struct file *filp)
{
	 ...
	 spinlock(&xxx_lock);
	 xxx_count--;/* 減少使用計數*/
	 spin_unlock(&xxx_lock);
 	return 0;
}

讀寫自旋鎖

??當臨界區的一個檔案可以被同時讀取,但是并不能被同時讀和寫,如果一個執行緒在讀,另一個執行緒在寫,那么很可能會讀取到錯誤的不完整的資料,讀寫自旋鎖是可以允許對臨界區的共享資源進行并發讀操作的,但是并不允許多個執行緒并發讀寫操作,如果想要并發讀寫,就要用到了順序鎖,
??讀寫自旋鎖的讀操作函式如下所示:

函式 描述
DEFINE_RWLOCK(rwlock_t lock) 定義并初始化讀寫鎖
void rwlock_init(rwlock_t *lock) 初始化讀寫鎖
void read_lock(rwlock_t *lock) 獲取讀鎖
void read_unlock(rwlock_t *lock 釋放讀鎖
void read_unlock_irq(rwlock_t *lock) 打開本地中斷,并且釋放讀鎖
void read_lock_irqsave(rwlock_t *lock,unsigned long flags) 保存中斷狀態,禁止本地中斷,并獲取讀鎖
void read_unlock_irqrestore(rwlock_t *lock,unsigned long flags) 將中斷狀態恢復到以前的狀態,并且激活本地中斷,釋放讀鎖
void read_lock_bh(rwlock_t *lock) 關閉下半部,并獲取讀鎖
void read_unlock_bh(rwlock_t *lock) 打開下半部,并釋放讀鎖

??讀寫自旋鎖的寫操作函式如下所示:

函式 描述
void write_lock(rwlock_t *lock) 獲取寫鎖
void write_unlock(rwlock_t *lock) 釋放寫鎖
void write_lock_irq(rwlock_t *lock) 禁止本地中斷,并且獲取寫鎖,
void write_unlock_irq(rwlock_t *lock) 打開本地中斷,并且釋放寫鎖
void write_lock_irqsave(rwlock_t *lock,unsigned long flags) 保存中斷狀態,禁止本地中斷,并獲取寫鎖
void write_unlock_irqrestore(rwlock_t *lock,unsigned long flags) 將中斷狀態恢復到以前的狀態,并且激活本地中斷,釋放寫鎖
void write_lock_bh(rwlock_t *lock) 關閉下半部,并獲取寫鎖
void write_unlock_bh(rwlock_t *lock) 打開下半部,并釋放寫鎖

讀寫鎖例程

rwlock_t lock; /* 定義rwlock */
rwlock_init(&lock); /* 初始化rwlock */
/* 讀時獲取鎖*/
read_lock(&lock);
... /* 臨界資源 */
read_unlock(&lock);
/* 寫時獲取鎖*/
write_lock_irqsave(&lock, flags);
... /* 臨界資源 */
write_unlock_irqrestore(&lock, flags);

順序鎖

??順序鎖是讀寫鎖的優化版本,讀寫鎖不允許同時讀寫,而使用順序鎖可以完成同時進行讀和寫的操作但并不允許同時的寫,雖然順序鎖可以同時進行讀寫操作,但并不建議這樣,讀取的程序并不能保證資料的完整性,

順序鎖操作函式

??順序鎖的讀操作函式如下所示:

函式 描述
DEFINE_SEQLOCK(seqlock_t sl) 定義并初始化順序鎖
void seqlock_ini seqlock_t *sl) 初始化順序鎖
void write_seqlock(seqlock_t *sl) 順序鎖寫操作
void write_sequnlock(seqlock_t *sl) 獲取寫順序鎖
void write_seqlock_irq(seqlock_t *sl) 禁止本地中斷,并且獲取寫順序鎖
void write_sequnlock_irq(seqlock_t *sl) 打開本地中斷,并且釋放寫順序鎖
void write_seqlock_irqsave(seqlock_t *sl,unsigned long flags) 保存中斷狀態,禁止本地中斷,并獲取寫順序
void write_sequnlock_irqrestore(seqlock_t *sl,unsigned long flags) 將中斷狀態恢復到以前的狀態,并且激活本地中斷,釋放寫順序鎖
void write_seqlock_bh(seqlock_t *sl) 關閉下半部,并獲取寫讀鎖
void write_sequnlock_bh(seqlock_t *sl) 打開下半部,并釋放寫讀鎖

??順序鎖的寫操作函式如下所示:

函式 描述
DEFINE_RWLOCK(rwlock_t lock) 讀單元訪問共享資源的時候呼叫此函式,此函式會回傳順序鎖的順序號
unsigned read_seqretry(const seqlock_t *sl,unsigned start) 讀結束以后呼叫此函式檢查在讀的程序中有沒有對資源進行寫操作,如果有的話就要重讀

自旋鎖使用注意事項

  1. 因為在等待自旋鎖的時候處于“自旋”狀態,因此鎖的持有時間不能太長,一定要短,否則的話會降低系統性能,如果臨界區比較大,運行時間比較長的話要選擇其他的并發處理方式,比如稍后要講的信號量和互斥體,
  2. 自旋鎖保護的臨界區內不能呼叫任何可能導致執行緒休眠的API函式,比如copy_from_user()、copy_to_user()、kmalloc()和msleep()等函式,否則的話可能導致死鎖,
  3. 不能遞回申請自旋鎖,因為一旦通過遞回的方式申請一個你正在持有的鎖,那么你就必須“自旋”,等待鎖被釋放,然而你正處于“自旋”狀態,根本沒法釋放鎖,結果就是自己把自己鎖死了
  4. 在撰寫驅動程式的時候我們必須考慮到驅動的可移植性,因此不管你用的是單核的還是多核的SOC,都將其當做多核SOC來撰寫驅動程式,

copy_from_user的使用是結合行程背景關系的,因為他們要訪問“user”的記憶體空間,這個“user”必須是某個特定的行程,如果在驅動中使用這兩個函式,必須是在實作系統呼叫的函式中使用,不可在實作中斷處理的函式中使用,如果在中斷背景關系中使用了,那代碼就很可能操作了根本不相關的行程地址空間,其次由于操作的頁面可能被換出,這兩個函式可能會休眠,所以同樣不可在中斷背景關系中使用,

信號量

信號量簡介

??信號量和自旋鎖有些相似,不同的是信號量會發出一個信號告訴你還需要等多久,因此,不會出現傻傻等待的情況,比如,有100個停車位的停車場,門口電子顯示屏上實時更新的停車數量就是一個信號量,這個停車的數量就是一個信號量,他告訴我們是否可以停車進去,當有車開進去,信號量加一,當有車開出來,信號量減一,
??比如,廁所一次只能讓一個人進去,當A在里面的時候,B想進去,如果是自旋鎖,那么B就會一直在門口傻傻等待,如果是信號量,A就會給B一個信號,你先回去吧,我出來了叫你,這就是一個信號量的例子,B聽到A發出的信號后,可以先回去睡覺,等待A出來,
??因此,信號量顯然可以提高系統的執行效率,避免了許多無用功,信號量具有以下特點:

  1. 因為信號量可以使等待資源執行緒進入休眠狀態,因此適用于那些占用資源比較久的場合,
  2. 因此信號量不能用于中斷中,因為信號量會引起休眠,中斷不能休眠
  3. 如果共享資源的持有時間比較短,那就不適合使用信號量了,因為頻繁的休眠、切換執行緒引起的開銷要遠大于信號量帶來的那點優勢

信號量操作函式

函式 描述
DEFINE_SEAMPHORE(name) 定義一個信號量,并且設定信號量的值為1
void sema_init(struct semaphore *sem, int val) 初始化信號量sem,設定信號量值為val
void down(struct semaphore *sem) 獲取信號量,因為會導致休眠,因此不能在中斷中使用
int down_trylock(struct semaphore *sem); 嘗試獲取信號量,如果能獲取到信號量就獲取,并且回傳0.如果不能就回傳非0,并且不會進入休眠
int down_interruptible(struct semaphore ????????????????????? 獲取信號量,和down類似,只是使用dow進入休眠狀態的執行緒不能被信號打斷,而使用此函式進入休眠以后是可以被信號打斷的
void up(struct semaphore *sem) 釋放信號量

信號量例程

struct semaphore sem; /* 定義信號量 */ 
sema_init(&sem, 1); /* 初始化信號量 表示只能由一個執行緒同時訪問這塊資源 */
 down(&sem); /* 申請信號量 */
  /* 臨界區 */ 
 up(&sem); /* 釋放信號量 */

互斥體

互斥體簡介

??互斥體表示一次只有一個執行緒訪問共享資源,不可以遞回申請互斥體
??信號量也可以用于互斥體,當信號量用于互斥時(即避免多個行程同時在一個臨界區中運行),信號量的值應初始化為1.這種信號量在任何給定時刻只能由單個行程或執行緒擁有,在這種使用模式下,一個信號量有時也稱為一個“互斥體( mutex)”,它是互斥( mutual exclusion)的簡稱,Linux內核中幾平所有的信號量均用于互斥

互斥體操作函式

函式 描述
DEFINE_MUTEX(name) 定義并初始化一個 mutex變數
void mutex_init(mutex *lock) 初始化 mutex
void mutex_lock(struct mutex *lock) 獲取 mutex,也就是給 mutex上鎖,如果獲取不到就進休眠
void mutex_unlock(struct mutex *lock) 釋放 mutex,也就給 mutex解鎖
int mutex_trylock(struct mutex *lock) ????????????????????? 判斷 mutex是否被獲取,如果是的話就回傳,否則回傳0
int mutex_lock_interruptible(struct mutex *lock) 使用此函式獲取信號量失敗進入休眠以后可以被信號打斷

互斥體例程

struct mutex lock; /* 定義一個互斥體 */ 
mutex_init(&lock); /* 初始化互斥體 */ 
mutex_lock(&lock); /* 上鎖 */ 
/* 臨界區 */
mutex_unlock(&lock); /* 解鎖*/

互斥體與自旋鎖

??互斥體和自旋鎖都是解決互斥問題的一種手段,互斥體是行程級別的,互斥體在使用的時候會發生行程間的切換,因此,使用互斥體資源開銷比較大,自旋鎖可以節省背景關系切換的時間,如果持有鎖的時間不長,使用自旋鎖是比較好的選擇,如果持有鎖時間比較長,互斥體顯然是更好的選擇,

互斥體使用注意事項

  1. 當鎖不能被獲取到時,使用互斥體的開銷是行程背景關系切換時間,使用自旋鎖的開銷是等待獲取自旋鎖(由臨界區執行時間決定),若臨界區比較小,宜使用自旋鎖,若臨界區很大,應使用互斥體,
  2. 互斥體所保護的臨界區可包含可能引起阻塞的代碼,而自旋鎖則絕對要避免用來保護包含這樣代碼的臨界區,因為阻塞意味著要進行行程的切換,如果行程被切換岀去后,另一個行程企圖獲取本自旋鎖,死鎖就會發生,
  3. 互斥體存在于行程背景關系,因此,如果被保護的共享資源需要在中斷或軟中斷情況下使用,則在互斥體和自旋鎖之間只能選擇自旋鎖,當然,如果一定要使用互斥體,則只能通過mutex trylock()方式進行,不能獲取就立即回傳以避免阻塞,

??大家的鼓勵是我繼續創作的動力,如果覺得寫的不錯,歡迎關注,點贊,收藏,轉發,謝謝!

有任何問題,均可通過公告中的二維碼聯系我

轉載請註明出處,本文鏈接:https://www.uj5u.com/caozuo/240737.html

標籤:其他

上一篇:【原】無腦操作:虛擬機安裝Centos 7.6

下一篇:Virtual BOX 使用U盤或者現有硬碟啟動虛擬機;Virtual Box 使用物體硬碟啟動虛擬機;Virtual Box 使用物體U盤啟動虛擬機;

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • CA和證書

    1、在 CentOS7 中使用 gpg 創建 RSA 非對稱密鑰對 gpg --gen-key #Centos上生成公鑰/密鑰對(存放在家目錄.gnupg/) 2、將 CentOS7 匯出的公鑰,拷貝到 CentOS8 中,在 CentOS8 中使用 CentOS7 的公鑰加密一個檔案 gpg -a ......

    uj5u.com 2020-09-10 00:09:53 more
  • Kubernetes K8S之資源控制器Job和CronJob詳解

    Kubernetes的資源控制器Job和CronJob詳解與示例 ......

    uj5u.com 2020-09-10 00:10:45 more
  • VMware下安裝CentOS

    VMware下安裝CentOS 一、軟硬體準備 1 Centos鏡像準備 1.1 CentOS鏡像下載地址 下載地址 1.2 CentOS鏡像下載程序 點擊下載地址進入如下圖的網站,選擇需要下載的版本,這里選擇的是Centos8,點擊如圖所示。 決定選擇Centos8后,選擇想要的鏡像源進行下載,此 ......

    uj5u.com 2020-09-10 00:12:10 more
  • 如何使用Grep命令查找多個字串

    如何使用Grep 命令查找多個字串 大家好,我是良許! 今天向大家介紹一個非常有用的技巧,那就是使用 grep 命令查找多個字串。 簡單介紹一下,grep 命令可以理解為是一個功能強大的命令列工具,可以用它在一個或多個輸入檔案中搜索與正則運算式相匹配的文本,然后再將每個匹配的文本用標準輸出的格式 ......

    uj5u.com 2020-09-10 00:12:28 more
  • git配置http代理

    git配置http代理 經常遇到克隆 github 慢的問題,這里記錄一下幾種配置 git 代理的方法,解決 clone github 過慢。 目錄 git配置代理 git單獨配置github代理 git配置全域代理 配置終端環境變數 git配置代理 主要使用 git config 命令 git單獨 ......

    uj5u.com 2020-09-10 00:12:33 more
  • Linux npm install 裝包時提示Error EACCES permission denied解

    npm install 裝包時提示Error EACCES permission denied解決辦法 ......

    uj5u.com 2020-09-10 00:12:53 more
  • Centos 7下安裝nginx,使用yum install nginx,提示沒有可用的軟體包

    Centos 7下安裝nginx,使用yum install nginx,提示沒有可用的軟體包。 18 (flaskApi) [root@67 flaskDemo]# yum -y install nginx 19 已加載插件:fastestmirror, langpacks 20 Loading ......

    uj5u.com 2020-09-10 00:13:13 more
  • Linux查看服務器暴力破解ssh IP

    在公網的服務器上經常遇到別人爆破你服務器的22埠,用來挖礦或者干其他嘿嘿嘿的事情~ 這種情況下正確的做法是: 修改默認ssh的22埠 使用設定密鑰登錄或者白名單ip登錄 建議服務器密碼為復雜密碼 創建普通用戶登錄服務器(root權限過大) 建立堡壘機,實作統一管理服務器 統計爆破IP [root ......

    uj5u.com 2020-09-10 00:13:17 more
  • CentOS 7系統常見快捷鍵操作方式

    Linux系統中一些常見的快捷方式,可有效提高操作效率,在某些時刻也能避免操作失誤帶來的問題。 ......

    uj5u.com 2020-09-10 00:13:31 more
  • CentOS 7作業系統目錄結構介紹

    作業系統存在著大量的資料檔案資訊,相應檔案資訊會存在于系統相應目錄中,為了更好的管理資料資訊,會將系統進行一些目錄規劃,不同目錄存放不同的資源。 ......

    uj5u.com 2020-09-10 00:13:35 more
最新发布
  • vim的常用命令

    Vim的6種基本模式 1. 普通模式在普通模式中,用的編輯器命令,比如移動游標,洗掉文本等等。這也是Vim啟動后的默認模式。這正好和許多新用戶期待的操作方式相反(大多數編輯器默認模式為插入模式)。 2. 插入模式在這個模式中,大多數按鍵都會向文本緩沖中插入文本。大多數新用戶希望文本編輯器編輯程序中一 ......

    uj5u.com 2023-04-20 08:43:21 more
  • vim的常用命令

    Vim的6種基本模式 1. 普通模式在普通模式中,用的編輯器命令,比如移動游標,洗掉文本等等。這也是Vim啟動后的默認模式。這正好和許多新用戶期待的操作方式相反(大多數編輯器默認模式為插入模式)。 2. 插入模式在這個模式中,大多數按鍵都會向文本緩沖中插入文本。大多數新用戶希望文本編輯器編輯程序中一 ......

    uj5u.com 2023-04-20 08:42:36 more
  • docker學習

    ###Docker概述 真實專案部署環境可能非常復雜,傳統發布專案一個只需要一個jar包,運行環境需要單獨部署。而通過Docker可將jar包和相關環境(如jdk,redis,Hadoop...)等打包到docker鏡像里,將鏡像發布到Docker倉庫,部署時下載發布的鏡像,直接運行發布的鏡像即可。 ......

    uj5u.com 2023-04-19 09:26:53 more
  • 設定Windows主機的瀏覽器為wls2的默認瀏覽器

    這里以Chrome為例。 1. 準備作業 wsl是可以使用Windows主機上安裝的exe程式,出于安全考慮,默認情況下改功能是無法使用。要使用的話,終端需要以管理員權限啟動。 我這里以Windows Terminal為例,介紹如何默認使用管理員權限打開終端,具體操作如下圖所示: 2. 操作 wsl ......

    uj5u.com 2023-04-19 09:25:49 more
  • docker學習

    ###Docker概述 真實專案部署環境可能非常復雜,傳統發布專案一個只需要一個jar包,運行環境需要單獨部署。而通過Docker可將jar包和相關環境(如jdk,redis,Hadoop...)等打包到docker鏡像里,將鏡像發布到Docker倉庫,部署時下載發布的鏡像,直接運行發布的鏡像即可。 ......

    uj5u.com 2023-04-19 09:19:04 more
  • Linux學習筆記

    IP地址和主機名 IP地址 ifconfig可以用來查詢本機的IP地址,如果不能使用,可以通過install net-tools安裝。 Centos系統下ens33表示主網卡;inet后表示IP地址;lo表示本地回環網卡; 127.0.0.1表示代指本機;0.0.0.0可以用于代指本機,同時在放行設 ......

    uj5u.com 2023-04-18 06:52:01 more
  • 解決linux系統的kdump服務無法啟動的問題

    問題:專案麒麟系統服務器的kdump服務無法啟動,沒有相關日志無法定位問題。 1、查看服務狀態是關閉的,重啟系統也無法啟動 systemctl status kdump 2、修改grub引數,修改“crashkernel”為“512M(有的機器數值太大太小都會導致報錯,建議從128M開始試,或者加個 ......

    uj5u.com 2023-04-12 09:59:50 more
  • 解決linux系統的kdump服務無法啟動的問題

    問題:專案麒麟系統服務器的kdump服務無法啟動,沒有相關日志無法定位問題。 1、查看服務狀態是關閉的,重啟系統也無法啟動 systemctl status kdump 2、修改grub引數,修改“crashkernel”為“512M(有的機器數值太大太小都會導致報錯,建議從128M開始試,或者加個 ......

    uj5u.com 2023-04-12 09:59:01 more
  • 你是不是暴露了?

    作者:袁首京 原創文章,轉載時請保留此宣告,并給出原文連接。 如果您是計算機相關從業人員,那么應該經歷不止一次網路安全專項檢查了,你肯定是收到過資訊系統技術檢測報告,要求你加強風險監測,確保你提供的系統服務堅實可靠了。 沒檢測到問題還好,檢測到問題的話,有些處理起來還是挺麻煩的,尤其是線上正在運行的 ......

    uj5u.com 2023-04-05 16:52:56 more
  • 細節拉滿,80 張圖帶你一步一步推演 slab 記憶體池的設計與實作

    1. 前文回顧 在之前的幾篇記憶體管理系列文章中,筆者帶大家從宏觀角度完整地梳理了一遍 Linux 記憶體分配的整個鏈路,本文的主題依然是記憶體分配,這一次我們會從微觀的角度來探秘一下 Linux 內核中用于零散小記憶體塊分配的記憶體池 —— slab 分配器。 在本小節中,筆者還是按照以往的風格先帶大家簡單 ......

    uj5u.com 2023-04-05 16:44:11 more