信号是软件中断,提供了一种处理异步事件的方法
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)就是控制流的突变
硬件触发异常,通过异常处理表转移到异常处理程序,剩下的工作就是异常处理程序在软件中完成
异常的类别:
中断
中断是来自io设备的信号,是异步发生的
I/O设备(网络适配器,磁盘控制器,定时器芯片)通过相处理器芯片的一个引脚发信号,并将信号发到系统总线上,来触发中断,这个异常号标识了引起中断的设备
陷阱
陷阱是有意的异常
陷阱处理程序将控制返回到下一条指令
可以在用户程序和内核之间提供一个像过程一样的接口(system call)系统调用
故障
故障是由错误情况引起的,可能能够被故障处理程序修正
缺页异常
终止
终止时不可恢复的致命错误造成的结果
并发流
一个逻辑流的执行在时间上与另一个流重叠,称为并发流(concurrent flow)
多个流并发地执行的一般现象称为并发(concurrency)
一个进程和其他进程轮流运行的概念称为多任务(multitasking)
并行流是并发流的一个真子集,如果两个流并发地运行在不同的处理器内核或者计算机上,那我们称他们为并行流(parallel flow)
#include <string.h> char * strstr(const char *haystack, const char *needle); // locate a subtring in string //比喻大海捞针
redis外部数据结构
redis的对象系统
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编码类型
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
编码方式
/* 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
编码方式
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
编码方式
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
编码方式
欧几里得算法:
应用:
// 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
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"); }
受限指针,一般出现在指针的声明中
int * restrict p;
如果指针p指向的对象在之后需要修改,那么该对象不会允许通过除指针p之外的任何方式访问
对程序有高性能要求才会添加
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
线程通过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);
#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)
当需要重用缓冲区来存放其他磁盘数据,需要执行2步骤(flush)
Sync,fsync主要是用来保证磁盘上实际文件系统与缓冲区内容一致性
unix系统支持在不同进程间共享打开文件
内核用于所有I/O的数据结构
使用是3种数据结构表示打开的文件
注意区分 文件描述符 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 指向 终端 的文件表项
io函数都是围绕文件描述符
标准io库都是围绕流(stream)
标准io函数fopen,返回一个指向FILE对象的指针
// .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()函数
如果目标文件是由c代码编译生成的,用gcc做链接就可以,整个程序的入口点是ctr1.o中提供的 _start; 它首先做一些初始化工作(以下成为启动例程 startup routine), 然后调用c代码中提供的main函数; 真正正确的描述_start才函数真正的入口点,main函数是_start调用的 /usr/lib/gcc/i486-linux-gun/4.3.2/../../../../lib/ctr1.0a
原型prototype,对象创建型设计模式
在spring框架的bean工厂 beanfactory有较多的应用
动态语言:动态语言也叫弱类型语言,运行时才确定数据类型语言(js,python)
静态语言:静态语言也叫强类型语言,编译时变量数据类型就可以确定(java,c/c++)