1. 程式人生 > >最近遇到的部分知識點總結

最近遇到的部分知識點總結

一.多路複用

迴圈UDP伺服器:

socket(...);
bind(...);
while(1)
{
    recvfrom(...);
    process(...);
    sendto(...);
}

併發TCP伺服器:

socket(...);
bind(...);
listen(...);
while(1)
{
    accept(...);
    //if(fork(...)==0){
    while(1)
    {
        read(...);
        process(...);
        write(...);
    }
    close(...);
    exit(...);
    //}
    close(...);
}

多路複用I/O的TCP伺服器:

int use_select(int *readfd,int n)
{
   fd_set my_readfd;
   int maxfd;
   int i;
   
   maxfd=readfd[0];
   for(i=1;i<n;i++)
    if(readfd>;maxfd) maxfd=readfd;
   while(1)
   {
        FD_ZERO(&my_readfd);
        for(i=0;i<n;i++)
            FD_SET(readfd,*my_readfd);
        select(maxfd+1,&my_readfd,NULL,NULL,NULL); 
        for(i=0;i<n;i++)
        {  if(FD_ISSET(readfd,&my_readfd))
              {
                  /* 原來是我可以讀了  */ 
                        we_read(readfd);
              }
    }
   }
}

poll\select\epoll都是同步I\O多路複用的機制,epoll和poll最大的區別在於mmap和epoll分解了poll函式。
1.epoll也是同步阻塞的。阻塞就是一個程序從使用者空間向核心空間發起了I\O後進入休眠狀態,之後一直等待結果產生再被喚醒。而非阻塞是一個程序在發起了I\O後不休眠,而是可以不斷的進行其他操作然後定時輪詢即可。輪詢其實是很浪費CPU資源的。
等待佇列就是因為同樣等待一個訊號的程序都存放進的那個佇列。
2.非同步通知:也就是fasync,就是讓驅動程式去告訴應用底層硬體發生了什麼事,而不是應用主動的去查詢驅動。在驅動中需要定義一個fasync_struct來存放對應裝置檔案的資訊,如fd和filp並交給核心處理,再用fasync_helper將fd、filp和定義的結構體傳給核心。在非同步佇列裡註冊過的程序當kill_fasync訊號出現時就會去找到對應的程序。
fasync_helper(fd,filp,mode,&dev->async_queue)
3.

a.用fcntl(fd,F_SETOWN,getpid());設定接收SIGIO訊號的程序組
b.Oflags=fcntl(fd,F_GETFL);得到檔案的標誌位
c.fcntl(fd,F_SETFL,Oflags|FASYNC);當標誌位改變驅動程式中的fasync()函式得以執行
d.signal(SIGIO,my_signal_func);
if(dev->async_queue){
    kill_fasync(&dev->async_queue,SIGIO,POLL_IN);
}


POLL_IN指裝置可讀,POLL_OUT指裝置可寫,可讀或可寫時將SIGIO訊號傳送給核心。test_fasync(-1,filp,0);當裝置關閉時使用test_fasync將裝置刪除。

原子上下文:不允許核心訪問使用者空間、不允許核心睡眠、不允許呼叫任何可能引起睡眠的函式。

#define in_irq()     (hardirq_count()) //在處理硬中斷中
#define in_softirq()     (softirq_count()) //在處理軟中斷中
#define in_interrupt()   (irq_count()) //在處理硬中斷或軟中斷中
#define in_atomic()     ((preempt_count() & ~PREEMPT_ACTIVE) != 0) //包含以上所有情況,在禁用搶佔時會返回錯誤結果。

臨界區是輕量級的鎖,通常不會產生核心物件,內部實現是基於自旋鎖與事件物件等待來實現的,互斥體和訊號量都是核心物件,效率不如臨界區。互斥體是基於上鎖機制而訊號量是基於訊號機制。互斥體和訊號量可以跨執行緒,且都是基於引用計數,訊號量可以用於同步。
互斥體和臨界區是可重入的,大概是說一個執行緒在執行一個帶鎖的方法,該方法中又呼叫了另一個需要相同鎖的方法,則該執行緒可以直接執行而無需重新獲得鎖。
自旋鎖最多隻能被一個可執行執行緒持有,如果一個執行執行緒試圖獲得一個已經被持有的自旋鎖,那麼該執行緒就會一直進行忙迴圈-旋轉-等待鎖。自旋鎖可以防止多於一個的執行執行緒同時進入臨界區。適合短時間內的輕量級加鎖。長時間或可被搶佔時最好使用互斥體。自旋鎖可在中斷上下文中加鎖。
seqlock用於讀多寫少的情況,常用於時鐘的獲取,使用sequence++的奇偶方式判斷讀寫狀態。
RCU鎖,一種改進的讀寫鎖,使用read-copy-update,適用於網路路由表的查詢更新、裝置狀態表的維護、資料結構的延遲釋放以及多徑I/O裝置的維護。讀隨意,不過寫的開銷較大。

非同步的應用程式和驅動程式:
https://blog.csdn.net/psvoldemort/article/details/21184525

wait_event_iterruptable()使程序休眠
wake_up_interruptable()喚醒休眠的程序
request_irq()註冊中斷處理函式
free_irq()登出中斷處理函式
local_irq_disable()
local_irq_enable()本地CPU的中斷情況
disable_irq()人為關閉某個中斷
enable_irq()人為開啟某個中斷

strace -p 2624檢視程序的系統呼叫

redis埠號。。。這個面試被問到賊拉拉難受。埠號6379
https埠號443
FTP埠號21
SSH、SCP、埠重定向埠號22
Telnet埠號23

 

二.筆試問題總結雜記

資料庫(主從資料庫一讀一寫)+伺服器+訊息佇列+分散式服務複用業務+(HTTPS+json+web(cookie+session)+App後臺(token))
訊息佇列可以將並行請求變為序列情求,常見的訊息佇列軟體有redis、RabbitMQ、ActiveMQ、ZeroMQ
token情況:收到使用者的URL請求和表單body資訊,後臺驗證正確後隨機生成一個token,該token與資料庫資訊相對應,資料庫將token返回給App,為了不在網路上傳輸token我們使用了MD5演算法將token和資訊轉化成一個sign,時間戳也ok如果資料庫token值與MD5的sign結果相同則繼續下一步操作。
URL上會包含uid和sign。
會使用推模式以及稍長時間的輪詢。

原始套接字的使用:
setuid(getpid());//只有管理員能用原始套接字,原始套接字可以自定義TCP內容。

10ms比較接近windows的排程時間片。
慢查詢:慢查詢是指當SQL語句超過long_query_time閾值時,將這些語句記錄在查詢日誌中。
索引
拆分表:縱向與橫向

B樹中每一個節點都儲存了資訊,而B+樹只有葉子節點儲存了資訊。B樹每一個節點都包含key和value,經常訪問的元素可能離根節點更近,訪問也更迅速。B+樹的內部結點不包含資料資訊,資料存放更加緊密,B+樹遍歷只需遍歷葉子結點,由於資料順序排列並且相連便於區間查詢和搜尋。

資料庫的物件有表,索引,檢視(看上去與表一樣但是上面限制了使用者訪問的資訊),圖表,預設值,規則,觸發器,使用者,函式等。

java調優命令:
1.jps用於檢視有權訪問的hotspot虛擬機器的程序。jps -v可以檢視虛擬機器啟動時顯示指定的引數列表。
2.jstat測量Java hotspot虛擬機器的效能統計資訊,包括類裝載、記憶體、垃圾收集、JIT編譯等執行資料。
3.jmap檢視程序記憶體資訊。
4.jhat檢視堆複製資訊,是平臺獨立的。
5.jstack用於打印出給定的java程序id或core file或遠端除錯服務的Java堆疊訊息。是執行緒快照。
6.jinfo用於檢視正在執行的java應用程式的擴充套件引數(JVM中-X標示的引數),甚至支援在執行時修改部分引數。

jmap將資訊dump到檔案中之後使用jhat進行堆記憶體分析。
java程式崩潰形成core檔案,如果執行的java程式hung死則可以用-F強制打出stack。

dump檔案中有:
死鎖 DeadLock !
執行中Runnable
等待資源Waiting on condition !
等待獲取監視器Waiting on monitor entry !
暫停Suspended
物件等待中Object.wait()
阻塞Blocked !
停止Parked

sleep來自Thread類,wait來自Object類。Thread.Sleep(0)的作用是觸發作業系統立刻重新進行一次CPU競爭。wait、notify、notifyAll只能在同步塊中使用,sleep必須捕獲異常。synchronized關鍵字用於保護共享資料,避免其他執行緒對共享資料進行存取。
sleep不出讓系統資源,沒有釋放鎖。而wait出讓了系統資源,釋放了鎖,但沒有釋放同步資源,使得其他執行緒可以同步控制塊或方法。

多執行緒共享堆記憶體,效率比多程序要好。
Wait\Notify\NotifyAll可用於執行緒間通訊關於資源鎖的狀態。
Java的每個物件都有一個鎖(monitor,也被稱為監視器)。
sleep()和yield()都是靜態方法,可以在當前正在執行的執行緒中工作。
保證執行緒同步的方法有:同步,原子類,併發鎖,volatile,使用不變類和執行緒安全類。
阻塞佇列是java collections框架的一部分,用於實現生產者消費者問題。


索引:
1.有索引的好處是搜尋比較快但是在有索引的前提下進行插入更新操作會很慢。因此當查詢操作比更新頻繁時需要建立索引。
2.不要在值較少的欄位上建立索引比如性別。
3.表的主鍵和外來鍵必須有索引。
4.經常出現在where子句中的子段,特別是大表的欄位應該建立索引。
5.索引應該建立在小欄位上,不要在大的文字欄位甚至超長字元上建立索引。
6.優先考慮where和order by涉及的列上建立索引。
7.避免對where子句中的欄位進行null判斷,否則會放棄索引而進行全表掃描。where numis null//應該給num賦0值。
8.避免在where子句中使用!=或<>操作符。
9.避免在where子句中使用or來連線條件。
如:select id from t where num=10 or num=20可以這樣查詢:select id from t where num=10 union all
select id from t where num=20
10.in和not in慎用
select id from t where num in(1,2,3) 對於連續的數值,能用 between 就不要用 in 了:select id from t where num between 1 and 3
11.避免like '李%',這個也是走全表掃描。
12.避免在where子句上對欄位進行表示式操作和函式操作。
13.用where子句替換having子句,用exists代替in。
14.當索引列有大量資料重複時SQL查詢不會去使用索引。
15.一個表上的索引最好不要超過6個。只能有一個聚簇索引,不能有多個非聚簇索引。
create (clustered) index mycolumn_index on mytable(mycolumn) (with allow_dup_row) drop index mytable.mycolumn_index

%yue%,由於在yue前加上了%號,因此這是一個全表查詢。


快速排序每輪都有一個partition歸位
希爾排序每輪都排好了步長的大小
選擇、希爾、堆排、快排都是不穩定的

ip link指令用於第二層鏈路層設定網絡卡。
ip address指令用於第三層也就是網路層的設定。
ip route指令用於路由器的設定。


判斷兩個IP是否在同一個網段:
A類地址:0.0.0.0--127.255.255.255
B類地址:128.0.0.0--191.255.255.255   2^7開始
C類地址:192.0.0.0--223.255.255.255   2^7+2^6開始
D類地址:224.0.0.0--239.255.255.255   2^7+2^6+2^5開始
E類地址:240.0.0.0--247.255.255.255   2^7+2^6+2^5+2^4開始


連結串列去重:

list<int> ll;
ll.push_back(xx);
ll.sort();
ll.erase(unique(ll.begin(),ll.end()),ll.end());

list的erase使用注意:

list<int>::iterator itList=ll.begin();
for(;itList!=ll.end;)
{
    list<int>::iterator it=itList++;//先賦值再遞增
    ll.erase(it);
}


檢視陣列資訊:設定斷點,點選陣列(連結串列)名稱然後右鍵選快速監視(quickwatch),新增需要檢視的個數然後點選陣列前的+號,就可以看到了。


Hash是vector上接連結串列。unordered_set和unordered_map是基於hash的set和map實現,而set和map是基於RBTree實現的。如果沒有排序需求只是為了快速的查詢和刪除那麼使用unordered即可。主要在於定義

hash<value>和equal_to<value>
template<class key,
    class Hash=hash<key>,
    class Pred=equal_to<key>,
    class Alloc=allocator<key>
>class unordered_set;
struct myHash
{
    size_t operator()(pair<int,int> __val)const
    {
        return static_cast<size_t>(__val.first*101+val.second);
    }
};
int main()
{
    unordered_set<pair<int,int>,myHash> S;
    S.insert(make_pair(x,y));
}


序列式容器:Vector、List(基於BiDirectional Iterator)->sList(基於Forward Iterator)->ArrayList\LinkedList、Deque->queue\stack、max-heap->priority_queue。heap可基於array|vector|list來實現。
vector便於read、list便於write。
關聯式容器:map、set、multimap、multiset
set和map鍵值都不能隨意更改,但map可以更改key對應的value。
set和multiset:
set只允許插入key,不允許鍵值重複。比較器預設是less,分配器預設是alloc。
multiset允許鍵值重複,底層呼叫的是RBTree的insert_equal函式,而set呼叫的是RBTree的insert_unique函式。
set可以進行交集、差集、並集運算

Set<String> result=new HashSet<String>();
Set<String> set1=new HashSet<String>(){
    {
        add("王者榮耀");
        add("英雄聯盟");
        add("穿越火線");
        add("地下城與勇士");
    }
};
Set<String> set2=new HashSet<String>(){
    {
        add("王者榮耀");
        add("英雄聯盟");
        add("魔獸世界");
    }
};
result.clear());
result.addAll(set1);
result.retainAll(set2);
system.out.println("交集:"+result);

result.clear());
result.addAll(set1);
result.addAll(set2);
system.out.println("並集:"+result);

result.clear());
result.addAll(set1);
result.removeAll(set2);
system.out.println("差集:"+result);

ConcurrentHashMap是分段的,相比於HashTable實現了鎖分離技術。使用多個子雜湊表,允許多個讀操作並行,讀操作不需要加鎖,保證HashEntry幾乎是不可變的,底層將key-value當成一個整體進行處理,底層採用一個Entry[]陣列,當需要取出一Entry時需要根據key的hash演算法找到其在陣列中的儲存位置,再根據equals方法從該位置上取出該Entry。

ArrayList是List介面的可變陣列非同步實現,採用fail-fast機制,面對併發的修改時迭代器會完全失敗,陣列擴容時將舊元素拷貝到一份新的陣列中,新陣列容量增長是舊陣列的1.5倍,remove方法會讓下標到陣列末尾的元素向前移動一個單位,將最後一位置空,方便GC。

LinkedList是List介面的雙向連結串列非同步實現的。雙向連結串列的結點對應類Node的例項,查詢是折半查詢。

HashMap是基於雜湊表的Map介面的非同步實現,底層使用陣列實現,陣列中每一項是一個單向連結串列,當連結串列長度大於一個閾值時轉化為紅黑樹,這樣可以減少連結串列查詢時間。採用fail-fast機制有一個modCount來記錄修改情況,迭代器初始化是有一個expectedModCount,兩者不對則說明有外部執行緒進行了修改,立馬丟擲異常。

typedef multiset<int,greater<int>> intSet;
typedef multiset<int,greater<int>>::iterator itSet;
int FindLeastKth(const vector<int>& data,intSet& LeastNumber,int k){}

priority_queue<int> Q;//預設為最大值優先
priority_queue<int,vector<int>,greater<int>> Q1;//最小值優先

穩定性的好處:第一次排序的結果可以被第二次排序所使用
哪些排序是穩定的:
插入排序中:直接插入和折半插入
交換排序中:氣泡排序
歸併排序和基數排序

哪些是不穩定的:
插入排序中:希爾排序
選擇排序中:直接選擇和堆排序
交換排序中:快速排序

快速排序每一輪都有一個數字歸位。
希爾排序每一輪都是隻與之後的步長數比較。
歸併排序(二路)第一輪兩兩排序,第二輪四個排,第三輪八個排,如果有九個必須用到十六路歸併,也就是四輪。
基數排序個位數先排序,再是十位數排序

FIFO對於已存在的頁面不改變結構,而LRU會對已存在的介面移至佇列頭。


突破反爬蟲代理限制的方法:
正常的時間訪問路徑:time.sleep(random.choice(range(1,3)))
使用多個代理IP
合法cookie
url headers request response content pattern ip_page ip_array.extend(ip_page) time.sleep

管道:無名管道用於具有親緣關係的程序之間,有名管道叫named pipe或FIFO,可以用函式mkfifo()建立。管道通過VFS索引節點指向file結構,是一次性讀操作,通過鎖,等待佇列和訊號來控制。

1.匈牙利演算法:給每個男生找出最適合的匹配妹子的演算法。求最大匹配問題。

bool find(int x){  
    int i,j;  
    for (j=1;j<=m;j++){    //掃描每個妹子  
        if (line[x][j]==true && used[j]==false)        
        //如果有曖昧並且還沒有標記過(這裡標記的意思是這次查詢曾試圖改變過該妹子的歸屬問題,但是
沒有成功,所以就不用瞎費工夫了)  
        {  
            used[j]=1;  
            if (girl[j]==0 || find(girl[j])) {   
                //名花無主或者能騰出個位置來,這裡使用遞迴。
                girl[j]=x;  
                return true;  
            }  
        }  
    }  
    return false;  
} 
for (i=1;i<=n;i++)  
{  
    memset(used,0,sizeof(used));    //這個在每一步中清空  
    if find(i) all+=1;  
}  


2.著色問題:衝突時間情況下的分配,使用DFS遞迴。

#define MEETING 5
#define NO_COLOR 0
#define MAX_COLOR 6

// 'N' = NONE 'R' = Red 'G' = Green 'B' = Blue 'Y' = Yellow 'W' = White 'M' = MAX
char colors[MAX_COLOR + 1] = {'N', 'G', 'R', 'B', 'Y', 'W', 'M' };

// A : { 1 - 2 - 3 }
// B : { 1 - 3 - 4 }
// C : { 2 - 3 }
// D : { 3 - 4 - 5 }
// E : { 1 - 4 - 5 }
int meetings[MEETING][MEETING] = { { 0, 1, 1, 1, 1 },
{ 1, 0,1, 0, 0 },
{ 1, 1, 0, 1, 1 },
{ 1, 0, 1, 0, 1 },
{ 1, 0, 1, 1, 0 } };

int result[MEETING] = {NO_COLOR, NO_COLOR, NO_COLOR, NO_COLOR, NO_COLOR };
bool check(int meeting, int color)//壞檢測,迴圈continue
{
    int i = 0;
    for (i = 0; i < MEETING; i++)
    {
        if ((meetings[i][meeting] == 1) &&(color == result[i]))//當值為1時顏色而顏色相等
,則說明檢測到不對應的顏色。
        {
            return true;
        }
    }
    return false;
}
void compute()
{
    int i = 0, j = 0;
    for (i = 0; i < MEETING; i++)
    {
        if (result[i] == NO_COLOR)
        {
            for (j = NO_COLOR + 1; j < MAX_COLOR; j++)
            {
                if (check(i, j))//如果檢測到他們之間相連並且顏色值相同,則使用
continue讓顏色值向前加一。如果相連且顏色不相同,則賦上顏色的值並跳出這個MEETING。
                    continue;
                else
                {
                    result[i] = j;
                    break;
                }
            }
        }
    }
}

void print()
{
    int i = 0;
    for (i = 0; i < MEETING; i++)
    {
        cout << i << ":" << colors[result[i]] << "   ";
    }
    cout << endl;
}

 

三.生產者消費者鎖的問題&MESI

使用上condition_variable標頭檔案,常與unique_lock一併使用。
如果是mutex則兩個執行緒不能同時工作,因此semaphor有時更好。

std::mutex mut;
std::condition_variable empty,full;
std::queue<int> Q;
生產者:
std::unique_lock<std::mutex> lk(mut);
empty.wait(lk);

full.notify_all();
lk.unlock();

消費者:
std::unique_lock<std::mutex> lk(mut);
full.wait(lk,[&](){return flag==id;});

empty.notify_one();
full.notify_all();

以下內容取自:https://www.cnblogs.com/aiheyiwen/p/4925810.html
P(S)意為佔用則訊號量減一,如果為0則說明剛好分配完全,如果為負數則說明當前有多少人在等待使用。
V(S)意為釋放,S一樣的意思。
也就是進入臨界區之前要對互斥程序使用P操作,以免搶佔資源。

//a.多個生產者,多個消費者,一個公共緩衝區(生產者先生產,消費者後消費,兩者互斥使用緩衝區即可)
//生產者
{
    生產產品
    P(m)//保證消費者無法再生產
    產品送往buffer
    V(n)//喚醒消費者
}
//消費者
{
    P(n)//保證有產品可消費
    從buffer取走產品----與buffer相關操作一定要有P(m)阻止生產者生產
    V(m)//喚醒生產者繼續生產
    消費產品
}

//b.一個生產者,一個消費者,無數個公共緩衝區
//生產者
{
    生產產品
    P(m)//阻止生產者消費
    產品送至buffer
    V(m)//繼續生產產品
    V(n)//喚醒消費者
}
//消費者
{
    P(n)//保證有產品可消費
    P(m)//阻止生產者消費
    從buffer取出產品
    V(m)//喚醒生產者繼續生產
    消費產品
}

環形緩衝區也就是多新增一個P(count)

以下內容取自:https://blog.csdn.net/liuxuejiang158blog/article/details/17301739
queue的push和pop介面中的pop改為try_pop()和wait_and_pop()。
private中的資料結構需要增加一個

mutable std::mutex mut;
std::condition_variable data_cond;//使用和上面一樣,lock_guard<mutex> lk(mut);
push裡是data_cond.notify_one();
pop裡是data_cond.wait()
//這裡使用到shared_ptr主要問題在於此時的佇列首元素可能是不確定的,
std::shared_ptr<T> res(std::make_shared<T>(data_queue.front()));
data_queue.pop();
return res;

//push、pop、pop_front時都需要獨佔鎖
以下內容取自:https://blog.csdn.net/ljh081231/article/details/19342963
關於CAS、ABA、無鎖佇列,這裡用的是CAS中使用的匯流排鎖。

生產者先申請一個slot,用

CAS(&claimSequence,slot-1,slot);

執行成功的這個生產者可以獲得這一slot。
生產者佔好位後進行釋出。

CAS(&cursor,slot,slot-1)

消費者可以消費這一區段的物品,並且需要跟蹤避免一次性生產的值超過了環形佇列的大小。

std::mutex可以實現跨平臺的互斥鎖,std::atomic_flag類才是真正的無鎖實現,此類沒有lock、unlock、try_lock等介面,只有test_and_set和clear,使用automic_flag可以方便的實現跨平臺的旋轉鎖,效率比mutex高出很多,但同時CPU的使用率也高出很多,我們可以在加鎖時按旋轉鎖的方式嘗試一定次數,如果指定次數加鎖失敗則轉為呼叫等待。

MESI協議:
M是被修改,與主存不一致
E是獨享的,單一CPU,未被修改,可轉為M
S是共享的,多CPU,未被修改,可轉為M
I是無效的,有其他CPU對它進行修改則無效

 

四.網路部分

top檢視系統資源情況
netstat檢視網路相關情況

ICMP、ARP、RARP是屬於網路層的協議,網路層還有IP、IGMP、RIP、OSPF、BGP、IS-IS、IPsec、NAT(NAPT由於涉及到埠號按理是屬於網路+傳輸層)
DHCP是應用層協議,動態主機配置協議保證任何IP同一時刻只能有一臺DHCP客戶機所使用,可以給使用者分配永久固定的IP地址。(應用層的協議有DNS、FTP、Gopher、POP3、SIP、SMTP、SNMP、SSH、TELNET、TLS、RPC、SOAP、GTP、NTP、STUN、SDP、RTSP、XMPP(基於XML)、NNTP、IRC等等)
組播是組播路由器藉助組播路由協議為組播資料包建立樹形路由,組播源只發送一次。

IGP內部閘道器協議:
RIP是距離向量路由選擇協議,度量是跳數
OSPF是鏈路狀態路由選擇協議,度量是頻寬和延遲,優勢在於無環,收斂速度快,hello報文的鄰居發現協議,支援基於介面的明文和MD5驗證。缺點在於負載均衡能力較差,配置複雜。
EIGRP則是解決了負載均衡的問題。
EGP外部閘道器協議:
BGP

隧道技術實際上是用一種網路層的協議來傳輸另一種網路層協議,基本功能是加密和封裝,VPN:封裝是指用來建立、維持和撤銷一個通道,加密是指對流經通道的資料進行加密,來實現VPN的安全性和私有性。

SNMP是簡單網路管理協議,他有一個MIB管理資訊庫,每個產品可以給自己的品牌申請一個MIB。
一個函式是一個執行緒,同一個執行緒保證原子執行順序。
網橋用於區域網之間的連線,有令牌環網段和區域網網段,透明網橋通過自學習(適合乙太網,也叫生成樹網橋,他們選舉ID最小的網橋為根網橋生成一顆生成樹使得每兩個點只有一條路徑可達且無環)而源路由網橋通過廣播(適合令牌環,),通過轉發、儲存、自學習等方式,有混雜介面可以處理不同的幀資訊。
虛擬機器中有bridge、nat、host-only等模式,其中nat+dhcp最為容易配置,但區域網內無法訪問,而bridge區域網內其他使用者可訪問。docker中一般開放前端埠,後端埠只暴露給前端埠。前端一個容器,後臺一個容器。
iptables命令:prerouting1->路由決策1->input1->forward2->output1->postrouting2

iptables -A INPUT -p tcp --dport 22 -j ACCEPT等價於有一條資料流想進入,目的埠為22
iptables -A INPUT -p tcp --sport 22 -j ACCEPT等價於有一條資料流想進入,來源於對方的22埠
iptables -A INPUT -s 192.168.0.3 -p tcp --dport 22 -j ACCEPT//SSH功能
有napt:iptables -t nat -A PREROUTING  -p tcp --dport 21 -d 211.101.46.253 -j DROP
iptables -t nat -A PREROUTING -d 202.96.129.5 -j DNAT 192.168.1.2

netfilter架構就是在整個網路流程的若干位置防止了一些監測點hook,每個監測點上登記一些處理函式。這
些處理函式返回NF_ACCEPT\NF_DROP\NF_STOLEN\NF_QUEUE\NF_REPEAT
sk-buff處理:
alloc_skb:分配一塊sk-buff記憶體
skb_reserver:寫指標向後移動到一個位置p,確定為資料包尾部,自始,寫指標開始從該位置前移封裝資料包
skb_push:寫指標前移n,更新資料包長度,從它返回的位置可以寫n個位元組資料-即封裝n位元組的協議
skb_put:寫指標移動到資料包尾部,返回尾部指標,可以從此位置寫n位元組資料,同時更新尾指標和資料nf_ip_checksum
先router cache、再router table、再鄰居結構+路由結構、再ARP查詢。


圖片轉載自:https://blog.csdn.net/CODINGCS/article/details/77803983
這個部分有一個重點:prerouting->路由決策->forward->postrouting
或prerouting->路由決策->input->系統核心->output->postrouting
prerouting和postrouting用於處理nat的情況。
當有input進來時系統會產生硬體中斷。

 

五.redis專題:

1.expire key seconds設定一個鍵的生存時間,persist取消時間限制,set會重新整理生存時間
2.watch key觀察一個值如果該值被修改之後的對該key的單個或multi行為都不會執行
3.multi一次性執行一個事務transact

sort:sadd set 1 4 5 2 3 9 10 -1;sort set;
sort set2 alpha:排列數字+字典序
sort set2 alpha desc limit 開始位置 個數

4.雜湊儲存:

sadd set a b c d e 
hset a time 5 title hello
hset b time 1 title world
hset c time 7 title this
sort set by *->time get *->title  //by意為key->field

5.任務佇列:lpush和rpop(brpop阻塞佇列輸出,brpop list 0意為超時時間設定為0,不限制等待時間)
6.訊息佇列:

brpop list 0;
lpush list hehe;
brpop list 0;
//(結果為"list""hehe")


7.優先順序佇列:brpop接收多個鍵,只要有一個鍵有元素就會pop出來

brpop list1 list2 0;
lpush list2 this;
brpop list1 list2 0;
//(結果為"list2""this")
brpop list2 list1 0//設定了list2優先順序大於list1,只要list2存在值則優先輸出list2,否則輸出list1.

8.釋出/訂閱:

publish channel1 hi//(訊息不會持久化也就是訂閱讀不到訂閱之前的資料)
subscribe channel1//去讀訊息
publish channel1 mina

客戶端和redis之間使用TCP連線,如果上一條不被執行那麼無法執行下一條。redis底層通訊協議對管道提供了支援,通過管道可以一次性發送多條命令,並在執行完後將結果一次性返回來降低時延。

RDB持久化處理
AOF記錄每次的寫操作,不適用於備份,RDB是快照形式,適合快速還原到某一點。
 

Redis的五個資料結構的底層實現:
參考:https://www.cnblogs.com/jaycekon/archive/2017/01/02/6227442.html
資料庫的鍵有字串物件
資料庫的值可以是字串物件,列表物件,雜湊物件,集合物件,有序集合物件。

1.字串結構(字串)
SDS動態增長,不是像C那樣預先申請空間會出現溢位、覆蓋等現象,而是記錄了當前buffer的已用、未用的buffer長度,當free不夠預增長時並且len小於1MB時,free申請和len一樣的大小。當len>1MB時,free只申請1MB。也就是free最多申請不超過1MB。因為用len記錄長度因此可以儲存二進位制檔案而不像C那樣侷限在了以\0結尾的文字資料。

struct sdshdr {  
      
    // buf 中已佔用空間的長度  
    int len;  
  
    // buf 中剩餘可用空間的長度  
    int free;  
  
    // 資料空間  
    char buf[];  
};  

 

2.連結串列結構(列表)
這裡是用連結串列實現列表的功能,如LPUSH

typedef struct listNode{
      struct listNode *prev;
      struct listNode * next;
      void * value;  
}
typedef struct list{
    //表頭節點
    listNode  * head;
    //表尾節點
    listNode  * tail;
    //連結串列長度
    unsigned long len;
    //節點值複製函式
    void *(*dup) (void *ptr);
    //節點值釋放函式
    void (*free) (void *ptr);
    //節點值對比函式
    int (*match)(void *ptr, void *key);
}

 

3.字典(雜湊)
用雜湊來實現,字典主要用於關聯陣列或對映。SET msg "hello world"就是一個字典形式的資料。

typedef struct dictht {
   //雜湊表陣列
   dictEntry **table;
   //雜湊表大小
   unsigned long size;

   //雜湊表大小掩碼,用於計算索引值
   unsigned long sizemask;
   //該雜湊表已有節點的數量
   unsigned long used;
}

typedef struct dictEntry{
   //鍵
   void *key;
   //值
   union{
      void *val;
      uint64_tu64;
      int64_ts64;
   }
   struct dictEntry *next;
}

typedef struct dict {
    // 型別特定函式
    dictType *type;
    // 私有資料
    void *privedata;
    // 雜湊表
    dictht  ht[2];
    // rehash 索引
    in trehashidx;
}


redis在滿的時候採用鏈地址法,也會進行rehash讓雜湊結構在一個合理的負載因子上,雜湊表需要進行壓縮或者擴充套件操作。
漸進式rehash 的詳細步驟:
a、為ht[1] 分配空間,讓字典同時持有ht[0]和ht[1]兩個雜湊表
b、在幾點鐘維持一個索引計數器變數rehashidx,並將它的值設定為0,表示rehash 開始
c、在rehash 進行期間,每次對字典執行CRUD操作時,程式除了執行指定的操作以外,還會將ht[0]中的資料rehash 到ht[1]表中,並且將rehashidx加一
d、當ht[0]中所有資料轉移到ht[1]中時,將rehashidx 設定成-1,表示rehash 結束

4.跳躍表(有序集合)
跳躍表通過在每個結點維持多個指向其他節點的指標,從而達到快速訪問節點的目的。可以與平衡樹的效率媲美。兩個地方用到了跳躍表,一個是有續集合鍵,另一個是在叢集節點中用做內部資料結構。
unordered_map也就是字典,unordered_set就是集合。

typedef struct zskiplistNode{
   //層
     struct zskiplistLevel{
     //前進指標
        struct zskiplistNode *forward;
    //跨度
        unsigned int span;
    } level[];
  //後退指標
    struct zskiplistNode *backward;
  //分值
    double score;
  //成員物件
    robj *obj;
}

typedef struct zskiplist {
     //表頭節點和表尾節點
     structz skiplistNode *header,*tail;
     //表中節點數量
     unsigned long length;
     //表中層數最大的節點的層數
     int level;

}zskiplist;

5.整數集合(集合)
就是一個數組,表示可以根據資料型別進行升級操作。裡面只能存少量的資料。

6.壓縮列表(列表和雜湊)
相對應的是快速列表。可用於存資料以及短的字串。
deque是由一個map中控器和一個數組組成的資料結構,它既有連結串列頭尾插入便捷的優點,又有陣列連續記憶體儲存,支援下標訪問的優點。deque屬於分段連續,而不是記憶體連續,但它可以造成記憶體連續的假象。
redis是採用sdlist和ziplist一起實現的,與deque類似,因為雙向連結串列在插入節點上覆雜度很低,但它的記憶體開銷很大,每個節點的地址不連續容易產生記憶體碎片。ziplist儲存在連續記憶體上但不利於修改操作,插入刪除複雜且都需要頻繁的申請釋放記憶體。

CSRF跨站請求偽造
XSS指令碼攻擊

 

唯一索引在該列中如果有重複值會報錯