linux基础知识
2022/2/28 7:21:33
本文主要是介绍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++)
这篇关于linux基础知识的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-12如何创建可引导的 ESXi USB 安装介质 (macOS, Linux, Windows)
- 2024-11-08linux的 vi编辑器中搜索关键字有哪些常用的命令和技巧?-icode9专业技术文章分享
- 2024-11-08在 Linux 的 vi 或 vim 编辑器中什么命令可以直接跳到文件的结尾?-icode9专业技术文章分享
- 2024-10-22原生鸿蒙操作系统HarmonyOS NEXT(HarmonyOS 5)正式发布
- 2024-10-18操作系统入门教程:新手必看的基本操作指南
- 2024-10-18初学者必看:操作系统入门全攻略
- 2024-10-17操作系统入门教程:轻松掌握操作系统基础知识
- 2024-09-11Linux部署Scrapy学习:入门级指南
- 2024-09-11Linux部署Scrapy:入门级指南
- 2024-08-21【Linux】分区向左扩容的方法