1. 程式人生 > 其它 >202202-linux基礎知識點

202202-linux基礎知識點

1.訊號

訊號是軟體中斷,提供了一種處理非同步事件的方法

unix訊號機制最簡單的介面是signal函式

/*
 * sign  訊號整型
 * func  函式指標
 * return :函式指標(一個函式地址,函式有一個整型引數,無返回值)
 */
void (* signal(int sign,void (*func)(int))) (int)
  
// 其他的表達方式
typedef void signfunc(int);
signfunc *signal(int ,signfunc);

typedef void (*sighandler_t)(int);
sighandler_t signal(int signum,sighandler_t handler);
  • 訊號:更高層的軟體形式的異常

知識拓展:

異常控制流

現代控制系統通過使控制流發生突變來對情況做出反應,我們把這些突變稱為異常控制流ECF

(Exceptional Control Flow)。

  • 硬體層,硬體檢測到的時間會觸發控制突然轉移到異常處理程式
  • 作業系統層,核心通過上下文切換將控制從一個程序轉移到另一個程序
  • 應用層,一個程序可以傳送訊號到另一個程序,接受者會將控制突然轉移到它的一個訊號處理程式

異常(exception)就是控制流的突變

硬體觸發異常,通過異常處理表轉移到異常處理程式,剩下的工作就是異常處理程式在軟體中完成

異常的類別:

  • 中斷(interrupt)
  • 陷阱(trap)
  • 故障(fault)
  • 終止(abort)

中斷

中斷是來自io裝置的訊號,是非同步發生的

I/O裝置(網路介面卡,磁碟控制器,定時器晶片)通過相處理器晶片的一個引腳發訊號,並將訊號發到系統總線上,來觸發中斷,這個異常號標識了引起中斷的裝置

陷阱

陷阱是有意的異常

陷阱處理程式將控制返回到下一條指令

可以在使用者程式和核心之間提供一個像過程一樣的介面(system call)系統呼叫

故障

故障是由錯誤情況引起的,可能能夠被故障處理程式修正

缺頁異常

終止

終止時不可恢復的致命錯誤造成的結果

併發流

一個邏輯流的執行在時間上與另一個流重疊,稱為併發流(concurrent flow)

多個流併發地執行的一般現象稱為併發(concurrency)

一個程序和其他程序輪流執行的概念稱為多工(multitasking)

並行流是併發流的一個真子集,如果兩個流併發地執行在不同的處理器核心或者計算機上,那我們稱他們為並行流(parallel flow)

2.strstr()函式

 #include <string.h>

char *
strstr(const char *haystack, const char *needle);
// locate a subtring in string
//比喻大海撈針

3.Redis 編碼格式

redis外部資料結構

redis的物件系統

  • 字串 string
  • 集合 set
  • 有序集合 zset
  • 雜湊 hash
  • 列表 list

redis內部實現資料結構

// 物件編碼
#define REDIS_ENCODING_RAW 0     /* Raw representation */
#define REDIS_ENCODING_INT 1     /* Encoded as integer */
#define REDIS_ENCODING_HT 2      /* Encoded as hash table */
#define REDIS_ENCODING_ZIPMAP 3  /* Encoded as zipmap */
#define REDIS_ENCODING_LINKEDLIST 4 /* Encoded as regular linked list */
#define REDIS_ENCODING_ZIPLIST 5 /* Encoded as ziplist */
#define REDIS_ENCODING_INTSET 6  /* Encoded as intset */
#define REDIS_ENCODING_SKIPLIST 7  /* Encoded as skiplist */
#define REDIS_ENCODING_EMBSTR 8  /* Embedded sds string encoding */

1.t_string

REDIS_STRING編碼型別

  • REDIS_ENCODING_INT ,整數值實現的字串物件
  • REDIS_ENCODING_RAW,簡單動態字串實現的字串物件
  • REDIS_ENCODING_EMBSTR,embstr編碼的簡單動態字串實現的字串物件

embstr編碼的字串物件所有資料都儲存在一塊連續的記憶體中

object.c中實現

robj *tryObjectEncoding(robj *o) {
    long value;
    sds s = o->ptr;
    size_t len;
    // 只在字串的編碼為 RAW 或者 EMBSTR 時嘗試進行編碼
    if (!sdsEncodedObject(o)) return o;
    // 不對共享物件進行編碼
    if (o->refcount > 1) return o;
    // 對字串進行檢查
    // 只對長度小於或等於 21 位元組,並且可以被解釋為整數的字串進行編碼
    len = sdslen(s);
    if (len <= 21 && string2l(s,len,&value)) {
        /* This object is encodable as a long. Try to use a shared object.
         * Note that we avoid using shared integers when maxmemory is used
         * because every object needs to have a private LRU field for the LRU
         * algorithm to work well. */
        if (server.maxmemory == 0 &&
            value >= 0 &&
            value < REDIS_SHARED_INTEGERS)
        {
            decrRefCount(o);
            incrRefCount(shared.integers[value]);
            return shared.integers[value];
        } else {
            if (o->encoding == REDIS_ENCODING_RAW) sdsfree(o->ptr);
            o->encoding = REDIS_ENCODING_INT;
            o->ptr = (void*) value;
            return o;
        }
    }
    // 嘗試將 RAW 編碼的字串編碼為 EMBSTR 編碼
    if (len <= REDIS_ENCODING_EMBSTR_SIZE_LIMIT) {
        robj *emb;
        if (o->encoding == REDIS_ENCODING_EMBSTR) return o;
        emb = createEmbeddedStringObject(s,sdslen(s));
        decrRefCount(o);
        return emb;
    }
    // 這個物件沒辦法進行編碼,嘗試從 SDS 中移除所有空餘空間
    if (o->encoding == REDIS_ENCODING_RAW &&
        sdsavail(s) > len/10)
    {
        o->ptr = sdsRemoveFreeSpace(o->ptr);
    }
    /* Return the original object. */
    return o;
}

t_list

編碼方式

  • REDIS_ENCODING_ZIPLIST
  • REDIS_ENCODING_LINKEDLIST
/* Check if the length exceeds the ziplist length threshold. */
// 檢視插入之後是否需要將編碼轉換為雙端連結串列
if (subject->encoding == REDIS_ENCODING_ZIPLIST &&
    ziplistLen(subject->ptr) > server.list_max_ziplist_entries)
      listTypeConvert(subject,REDIS_ENCODING_LINKEDLIST);

t_hash

編碼方式

  • REDIS_ENCODING_ZIPLIST
  • REDIS_ENCODING_HT
void hashTypeTryConversion(robj *o, robj **argv, int start, int end) {
    int i;
    // 如果物件不是 ziplist 編碼,那麼直接返回
    if (o->encoding != REDIS_ENCODING_ZIPLIST) return;

    // 檢查所有輸入物件,看它們的字串值是否超過了指定長度
    for (i = start; i <= end; i++) {
        if (sdsEncodedObject(argv[i]) &&
            sdslen(argv[i]->ptr) > server.hash_max_ziplist_value)
        {
            // 將物件的編碼轉換成 REDIS_ENCODING_HT
            hashTypeConvert(o, REDIS_ENCODING_HT);
            break;
        }
    }
}

t_set

編碼方式

  • REDIS_ENCODING_INSET
  • REDIS_ENCODING_HT
void setTypeConvert(robj *setobj, int enc) {

    setTypeIterator *si;

    // 確認型別和編碼正確
    redisAssertWithInfo(NULL,setobj,setobj->type == REDIS_SET &&
                             setobj->encoding == REDIS_ENCODING_INTSET);

    if (enc == REDIS_ENCODING_HT) {
        int64_t intele;
        // 建立新字典
        dict *d = dictCreate(&setDictType,NULL);
        robj *element;

        /* Presize the dict to avoid rehashing */
        // 預先擴充套件空間
        dictExpand(d,intsetLen(setobj->ptr));

        /* To add the elements we extract integers and create redis objects */
        // 遍歷集合,並將元素新增到字典中
        si = setTypeInitIterator(setobj);
        while (setTypeNext(si,NULL,&intele) != -1) {
            element = createStringObjectFromLongLong(intele);
            redisAssertWithInfo(NULL,element,dictAdd(d,element,NULL) == DICT_OK);
        }
        setTypeReleaseIterator(si);

        // 更新集合的編碼
        setobj->encoding = REDIS_ENCODING_HT;
        zfree(setobj->ptr);
        // 更新集合的值物件
        setobj->ptr = d;
    } else {
        redisPanic("Unsupported set conversion");
    }
}

t_zset

編碼方式

  • REDIS_ENCODING_ZIPLIST
  • REDIS_ENCODING_SKIPLIST

4.歐幾里得演算法(輾轉相處法)

歐幾里得演算法:

  • 求最大公約數的演算法
  • 演算法的核心思想:兩個數的最大公約數等於較小的數和兩數相除餘數的最大公約數

應用:

  • 判斷兩個陣列成的分數是否最簡,即最大公約數是否為1
// pseudocode
function gcd(a,b):
	     while b!= 0:
            t = b
            b = a % b
            a = t
        return a

遞迴形式

public int gcd(a,b){
     return b != 0? gcd(b,a%b): a;
}
  • 拓展

計算兩個數的最大公約數的另一種方法

更相減損法

//pseudocode
function gcd(a,b):
		 while true:
          if a> b:  a-=b
          else if a < b: b -=a
          else  return a

5.語法問題

c++中全域性閾,只能宣告、初始化變數

不能用於賦值、運算和呼叫函式等

關於初始化及賦值

  • 程式中的變數可以宣告多次,但只能定義一次
  • 只有當宣告變數也是定義的時候,宣告才有初始化式(只有定義才分配儲存空間,初始化必須要有儲存空間來初始化)
  • 宣告+初始化式 就是 定義
  • 賦值是一種運算,賦值等號是從右到左的運算子,對於全域性變數,不能用於運算

多餘傳參編譯警告問題

函式傳入引數未使用,編譯會包報warning警告

參考redis程式碼中的解決方案

How to anti-warning

// define a marco
#define REDIS_NOTUSED(V)  ((void) V)

// function
int function(int arg){
  
  REDIS_NOTUSED(arg);
  
  printf("fucntion\n");
}

6. 受限指標

受限指標,一般出現在指標的宣告中

int * restrict p;

如果指標p指向的物件在之後需要修改,那麼該物件不會允許通過除指標p之外的任何方式訪問

對程式有高效能要求才會新增

7.Python(zip,yield)

zip( iterables)*

生成一個迭代器重新組合可迭代元素

matrix是一個二維矩陣

zip(*matrix) ,返回一個matrix矩陣每列的元素

# zip()
# Make an iterator that aggregates elements from each of the iterables.
def zip(*iterables):
    # zip('ABCD', 'xy') --> Ax By
    sentinel = object()
    iterators = [iter(it) for it in iterables]
    while iterators:
        result = []
        for it in iterators:
            elem = next(it, sentinel)
            if elem is sentinel:
                return
            result.append(elem)
        yield tuple(result)
>>> x = [1, 2, 3]
>>> y = [4, 5, 6]
>>> zipped = zip(x, y)
>>> list(zipped)
[(1, 4), (2, 5), (3, 6)]
>>> x2, y2 = zip(*zip(x, y))
>>> x == list(x2) and y == list(y2)
True

yield

Delegating to a subgenerator

Allowing a generator to delegate part of its operations to another genrator

8.執行緒

執行緒通過pthread_create函式來建立其他執行緒

#include <pthread.h>

int
pthread_create(pthread_t *thread, const pthread_attr_t *attr,
         void *(*start_routine)(void *), void *arg);

成功返回,新建立執行緒id會設定成thread指向的記憶體單元;

新建的執行緒會從start_routine(執行緒例程)函式地址開始執行;

互斥量 & 條件變數

  • 互斥量mutex,本質上來說就是一把鎖,在訪問共享資源critical seciton對互斥量進行設定(加鎖)

  • 條件變數,是執行緒可用的另一種同步機制,條件變數給多個執行緒提供了一個會和的場所

條件變數+互斥量 一起使用,允許執行緒以無競爭的方式等待特定條件的發生

條件變數是由互斥量保護,執行緒在改變條件狀態之前必須先鎖住互斥量

#include <pthread.h>
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
  • 1.傳遞給pthread_cond_wait的互斥量mutex對條件進行保護
  • 2.呼叫者把鎖住的互斥量傳遞給函式
  • 3.函式將呼叫者放到等待條件的的執行緒列表上,對互斥量進行解鎖
  • 4.函式返回後,互斥量再次被鎖住

9.fcntl sync

#include <fcntl.h>
// file control
int fcntl(int fildes, int cmd, ...);

改變已經開啟檔案的屬性

Manipulate file descriptor

NAME
     sync -- force completion of pending disk writes (flush cache)

unix 系統實現在核心中設有緩衝區快取記憶體頁快取記憶體

大多數磁碟io都通過緩衝區進行

向檔案中寫入資料(delayed write)

  • 1.核心copy到緩衝區(排隊佇列)
  • 2.緩衝區到磁碟

當需要重用緩衝區來存放其他磁碟資料,需要執行2步驟(flush)

Sync,fsync主要是用來保證磁碟上實際檔案系統與緩衝區內容一致性

10.檔案共享

unix系統支援在不同程序間共享開啟檔案

核心用於所有I/O的資料結構

使用是3種資料結構表示開啟的檔案

  • 程序表項
  • 檔案表項
  • v節點表項

注意區分 檔案描述符 FD

檔案狀態標誌 FL

兩個檔案描述符指向同一檔案表項,所以共享同一檔案狀態標誌

檔案重定向

digit1 > &digit2

表示要將描述符digit1 重定向到描述符digits2的同一檔案

./a.out > outfile  2 > &1
# 標準輸出到outfile
# 執行dup將標準輸出複製到描述符2(標準錯誤)上
# 1,2 指向同一檔案表項
./a.out  2 > &1  > outfile
# 描述符2 成為終端
# 標準輸出重定向到outfile
# 描述符1 指向 outifle 檔案表項
# 描述符2 指向 終端 的檔案表項

11. IO函式

io函式都是圍繞檔案描述符

標準io庫都是圍繞流(stream)

標準io函式fopen,返回一個指向FILE物件的指標

12.編譯與連結

// .o 可重定位目標檔案
gcc -v   // 可以顯示版本

// 標準指令
gcc - Og -o prog main.c sum.c

// 指令拆解
// 1.前處理器 cpp
cpp [other arguments] main.c /tmp/main.i
main.c   -> mian.i  // ascii碼的中間檔案
// 2.c編譯器  ccl
ccl /tmp/mian.i  -Og [other arguments] -o /tmp/main.s   // ascii碼的組合語言檔案
// 3.彙編器 as
as [other arguments] -o /tmp/main.o /tmp/mian.s
// main.o  可重定位目標檔案
  
// 4.連結器
ld -o prog [system objects files and args] /tmp/mian.o /tmp/sum.o

// 5.執行

>./prog
# shell呼叫os中一個叫做載入器loader的函式,將可執行檔案prog中程式碼和資料複製到記憶體
# 並將控制轉移到程式的開頭

程式執行的流程

核心執行c程式時,使用一個exec()函式

  • 1.c程式總是從main函式開始執行
  • 2.核心在呼叫main函式之前,先呼叫一個特殊的啟動例程
  • 3.可執行程式檔案將此啟動例程指定為程式的起始地址
  • 4.啟動例程從核心取得命令列引數和環境變數值
如果目標檔案是由c程式碼編譯生成的,用gcc做連結就可以,整個程式的入口點是ctr1.o中提供的 _start;
它首先做一些初始化工作(以下成為啟動例程 startup routine), 然後呼叫c程式碼中提供的main函式;
真正正確的描述_start才函式真正的入口點,main函式是_start呼叫的
/usr/lib/gcc/i486-linux-gun/4.3.2/../../../../lib/ctr1.0a

13.原型設計模式

原型prototype,物件建立型設計模式

  • abstract factory 抽象工廠模式,動態配置產生 原型模式
  • 抽象工廠模式,單個例項,產生單例模式

在spring框架的bean工廠 beanfactory有較多的應用

14.動態語言vs靜態語言

動態語言:動態語言也叫弱型別語言,執行時才確定資料型別語言(js,python)

靜態語言:靜態語言也叫強型別語言,編譯時變數資料型別就可以確定(java,c/c++)

不要用狹隘的眼光看待不瞭解的事物,自己沒有涉及到的領域不要急於否定. 每天學習一點,努力過好平凡的生活.