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