Ssystem V 共享内存
共享内存区是最快的IPC形式。一旦这样的内存映射到共享它的进程的地址空间,这些进程间数据传递不再涉及到内核,换句话说是进程不再通过执行进入内核的系统调用来传递彼此的数据,而是直接使用内存中的共享区。 我们接下来认识一下常用的接口 接口shmget 创建共享内存需要同时引入<sys/ipc.h> <sys/shm.h> int shmget(key_t key, size_t size, int shmflg); key 是生成共享内存标示符的 关键字,唯一的key值能返回唯一的共享内存标示符,这是获得同一个共享内存的关键参数 size是指共享内存的大小,按字节算 shmflg是一个位图,控制创建时的行为和 共享内存文件的权限(缺省时为0),常见选项如下 IPC_CREAT:单独一个时,如果申请的共享内存不存在,就创建,然后返回;若存在,则获取并返回 IPC_CREAT | IPC_EXCL: 如果申请的共享内存不存在,则创建;若存在,则出错并返回-1 IPC_EXCL:不能单独使用 IPC_CREAT | 0666:创建一个权限为0666的共享内存文件,注: ...
Linux日志系统
引入首先看看AI对日志系统的重要性是怎么解释的 日志系统在软件开发和运维中扮演着至关重要的角色,主要体现在以下几个方面: 问题排查和调试:日志记录了系统运行过程中的重要信息,包括错误信息、异常堆栈、接口调用信息等。开发人员可以通过分析日志快速定位问题,减少排查时间,提高开发效率。 性能监控:通过记录应用程序的性能指标(如响应时间、请求吞吐量等),开发团队可以监控系统的健康状态,发现性能瓶颈并进行优化。 安全审计:日志可以记录用户的操作和系统事件,为后期的安全审计提供依据。一旦发生安全事件,通过分析日志可以追踪到攻击来源和攻击方式。 用户行为分析:通过对日志进行分析,开发团队可以了解用户的使用习惯和需求,这有助于改进产品功能和用户体验。 合规要求:在许多行业中,日志记录是合规的要求之一。维护完整的日志记录可以帮助企业满足法律法规和行业标准。 系统健康监控:通过实时监控日志,可以及时发现系统异常,进行预警和自动化处理,从而提高系统的可用性和稳定性。 故障恢复:在系统出现故障时,日志记录可以帮助开发和运维人员还原问题发生前的状态,从而有助于快速恢复系统。 总之,良好的日志系统能够帮助开发 ...
手撕AVL树
AVL树的概念二叉搜索树的不足二叉搜索树虽可以缩短查找的效率,但如果数据有序或接近有序二叉搜索树将退化为单支树,查找元素相当于在顺序表中搜索元素,效率低下。 AVL树的提出两位俄罗斯的数学家G.M.Adelson-Velskii和E.M.Landis在1962年发明了一种解决上述问题的方法:当向二叉搜索树中插入新结点后,如果能保证每个结点的左右子树高度之差的绝对值不超过1(需要对树中的结点进行调整),即构建一颗绝对的平衡搜索二叉树,即可降低树的高度,从而减少平均搜索长度。 定义: 一棵AVL树或者是空树,或者是具有以下性质的二叉搜索树: 它的左右子树都是AVL树 左右子树高度之差(简称平衡因子)的绝对值不超过1(-1/0/1) 如果一棵二叉搜索树是高度平衡的,它就是AVL树。如果它有n个结点,其高度可保持在O(log_2 n),搜索的时间复杂度O(log_2 n) 封装AVL树头文件12#include <utility>#include <cassert> 封装AVL树的节点 这里采用键值(KV)类型的二叉树节点,使其泛用性更高 ...
C++特殊类的设计
接下来我们设计一些C++的常用特殊类,同时加深对C++类和对象的理解 设计一个不能被拷贝的类拷贝只会放生在两个场景中:拷贝构造函数以及赋值运算符重载,因此想要让一个类禁止拷贝,只需让该类不能调用拷贝构造函数以及赋值运算符重载即可。 C++98 将拷贝构造和赋值重载只私有声明且不定义即可 分析: 设为私有:防止类外调用,还能防止类外定义重新实现拷贝功能 只声明不定义:防止类内成员函数对其调用1234567//C++98class NoCopy{private://设置成私有 NoCopy(const NoCopy&); NoCopy& operator=(const NoCopy&);//只声明不定义}; C++11 C++11扩展了delete关键字的用法,可用于删除成员函数,尤其是删除默认成员函数 123456//C++11class CopyBan{ CopyBan(const CopyBan&) = delete; CopyBan& operator=(const CopyBan&) = delet ...
进程间通信
本篇博客更偏向于总括和导航,部分概念更细致的介绍将内嵌链接在文章中 重点内容 初识进程间通信 管道 消息队列 共享内存 信号量 进程间通信的目的 数据传输: 一个进程需要将它的数据发送给另一个进程 资源共享: 多个进程之间共享同样的资源 通知事件:一个进程需要向另一个或一组进程发送消息,通知它(它们)发生了某种事件(如子进程终止时要通知父进程) 进程控制: 有些进程希望完全控制另一个进程的执行(如Debug进程),此时控制进程希望能够拦截另一个进程的所有陷入和异常,并能够及时知道它的状态改变 进程间通信的主要方式 管道 System V进程间通信 POSIX进程间通信 进程间通信的分类管道 匿名管道 命名管道 System V IPC System V 消息队列 SysTem V 共享内存 System V 信号量 POSIX IPC 消息队列 共享内存 信号量 互斥量 条件变量 读写锁 管道怎么使用?戳我去管道博客🔗 首先,管道是Unix中最古老的进程间通信的形式。它用于进程间的单向通信 那么具体是怎样实现的呢?从标题里就可以发现,是基于文件 既然一个文件可以被多个进 ...
进程间通信--匿名管道与命名管道
什么是管道文件首先,管道是Unix中最古老的进程间通信的形式。它用于进程间的单向通信 那么具体是怎样实现的呢?从标题里就可以发现,是基于文件 既然一个文件可以被多个进程打开,那么不妨将文件作为两个进程通信的媒介。但是一般位于磁盘上的文件,IO效率相比于CPU,内存之类的读写速度慢了几个数量级,但文件是可以被加载到内存中的,而专门建立在内存中,而没有磁盘文件,专门用于进程间通信的内存级文件,我们就叫它管道文件 管道文件由内核维护 管道文件是单向的,可以是父进程->子进程,也可以子进程->父进程 管道读写规则 写端未关闭,但读端无数据可读时 (默认)O_NONBLOCK disable:read调用阻塞,即进程暂停执行,一直等到有数据来到为止。 O_NONBLOCK enable:read调用返回-1,errno值为EAGAIN。 读端未关闭,但写端写入管道已经写满时 (默认) O_NONBLOCK disable: write调用阻塞,直到有进程读走数据 O_NONBLOCK enable:wrtie调用返回-1,errno值为EAGAIN 若写端关闭,则read返 ...
【Linux】简易进程池
原理匿名管道可用于父子进程间的通讯,于是可以有父进程创建多个子进程形成进程池,并通过匿名管道文件向各个子进程派发任务 戳我去管道原理🔗 这里使用C++编写进程池代码,在程序中创建多个进程,并在父进程中使用自定义类channel用于描述和管理子进程,然后在task.hpp中模拟一些任务给主程序随机派发 代码代码规范这次对形参有了新的规范,这里用variable代指形参名 输入: const &variable 输出: *variable 输入输出: &variable 准备makefile文件这里使用g++编译,规定语法标准为C++11 123456processPool:processPool.cc g++ -o $@ $^ -std=c++11.PHONY:cleanclean: rm -rf processPool 准备任务文件首先要准备前后需要用到的头文件,然后构建模拟任务,并提供加载任务列表的函数 task.hpp 12345678910111213141516171819202122232425262728293031323334353637383 ...
【数据结构】一步到胃,键值对版二叉搜索树
什么是二叉搜索树二叉搜索树的定义是一颗二叉树的所有节点满足:根的左右孩子存在时,满足 左孩子 < 根 < 右孩子 递归定义则是: 左子树的根 < 根 < 右子树的根 左子树是二叉搜索树,右子树是二叉搜索树 写一个验证二叉搜索树的函数Leetecode题目链接🔗 封装一个二叉树类文件布置 BSTree.h用于声明和实现BSTree类 test.cpp用于测试 头文件BSTree.h 123#include <iostream>#include <utility> test.cpp 1#include "BSTree.h" 命名空间1namespace key_value 这里使用key_value作为命名空间,表示这是键值表示的搜索二叉树 定义节点二叉树的节点用于储存键值对和左右指针,并提供默认构造函数,使用初始化列表初始化成员变量 123456789101112131415template<class K,class V>class BSTNode{public: BSTNode(c ...
文件缓冲区
前置博客 基础IO 为什么有缓冲因为磁盘的读写与内存的读写操作速度相比,磁盘的读写是相差数量级的慢,所以为了提高内存多次,频繁读写磁盘文件的效率,缓冲区被投入使用。尤其是内存内容写入磁盘时,常常先写入内存级缓冲区,再在特定规则下一次性将缓冲区的内容写入磁盘 **本文以C语言提供的用户级缓冲区为例介绍缓冲区 缓冲区的刷新规则首先当一个进程正常退出时,会先刷新缓冲区再关闭文件,此时必定有一次刷新 而当进程运行时缓冲区的刷新策略主要有以下三种 无缓冲 内容直接写入文件 行缓冲 输入一般内容不刷新,遇到\n时刷新一次缓冲区 全缓冲 缓冲区有容量限制,满了之后就刷新 认识一下C语言的缓冲区这里的系统环境是Linux 刷新规则运行如下代码 12345678910111213#include <stdio.h>#include <unistd.h>int main(){ FILE* pfile = fopen("file.txt","w");//打开空文件 fprintf(stdout,"stdout ...
Linux文件系统
文件系统的组织方式 这里介绍一下Block Group里的分区内容 超级块(Super Block): 存放文件系统本身的结构信息。记录的信息主要有:bolck(数据块) 和 inode(属性块)的总量,未使用的block和inode的数量,一个block和inode的大小,最近一次挂载的时间,最近一次写入数据的时间,最近一次检验磁盘的时间等其他文件系统的相关信息。Super Block的信息被破坏,可以说整个文件系统结构就被破坏了 GDT(Group Descriptor Table): 块组描述符,描述块组属性信息。这里不作详细介绍 块位图(Block Bitmap):位图中的比特位记录着Data Block中哪个数据块已经被占用,哪个数据块没有被占用 inode位图(inode Bitmap):每个比特位表示一个inode是否空闲可用。 inode表(inode Table):每一个inode存放了各自文件的属性,如文件大小,所有者,最近修改时间等 数据区:存放文件内容 Linux系统中文件的增删查改Linux中,每一个文件都有自己的inode,而每一个inode有自己的in ...
C语言文件操作
用户级文件操作C语言的文件操作也是用户级的文件操作,通过FILE对象来管理每一个被打开的文件,以及提供了用户级文件缓冲区,因此还涉及到冲刷缓冲区等问题 FILE 类FILE类描述了一个文件流。里面存储了文件控制所需的信息: 指向自身缓冲区的的指针 位置指示器 状态指示器 所以C语言中对文件的管理就是对FILE对象的管理 基础操作 - 针对一般文件基础示例1234567891011#include <stdio.h>int main(){ FILE* pfile = fopen("file.txt", "w");//"w"模式打开文件file.txt int code = 1; const char* msg = "this is a msg"; fprintf(pfile, "get msg : %s code:%d", msg, code);//格式化输出字符串 fclose(pfile);//冲刷缓冲区并关闭文件 return 0;} 以上代码创建 ...
=C++= sizeof关键字详解
简介sizeof作为C/C++关键字,基本用法是求字节大小,但仅仅这一项用法在细节上就有很多说法了 求内置类型变量的大小有两种写法,以int a = 0的变量a为例 sizeof a sizeof(a) 都可以求变量a的大小,但注意,该变量的大小仅与变量类型有关,而与值无关 求内置类型的大小求类型大小时必须加上括号 例如sizeof(int) 求数组的大小 当数组声明在全局或sizeof处于数组声明语句的局部作用范围时,能够用sizeof(<数组名>)求数组大小 当数组名经过函数传参或加减常量运算后,退化为指针变量,类型大小在32位机器中为4,64位机器中为8 12345678910111213141516171819#include <iostream>using namespace std;int g_arr[10] = { 0 };void func(int st_arr[]){ cout << sizeof(st_arr) << endl;//此处退化为指针变量,输出4或8} ...
基础IO
共识原理 文件 = 内容 + 属性 被打开的文件需要加载到内存中 内存中的文件需要被操作系统管理 用户级文件接口详见C++文件操作 详见C语言文件操作 Linux系统调用接口fd 文件描述符 与访问文件的本质 fd(file descriptor),即文件描述符,下文的系统调用接口经常以fd命名变量,fd是整形变量,作为数组下标,用于管理打开的文件 可以看到,一个进程通过struct files _struct里的指针数组,管理多个同时打开的文件 且每个进程启动时,会默认打开三个文件,且默认fd固定 stdout read所需头文件#include <unistd.h>声明ssize_t read(int fd, void *buf, size_t count); 参数 fd即为目标文件的文件描述符 buf为要从文件读取字节到的内存地址 count为最大读取字节数 返回值 若成功,返回读取文件的字节数,类型为ssize_t,是层层封装的long int 若失败,返回-1,并设置errno的值 write所需头文件#include <unistd ...
初识动静态库
静态库(.a)程序在编译链接的时候把库的代码链接到可执行文件中。编译出的程序运行时不再需要静态库 生成和使用静态库这里使用gcc编译获得.o链接文件,再用gnu归档工具中的ar指令配合-rc选项(replace and create)封装库文件,最后借助makefile简化文件目录的封装操作 使用第三方库时,还可以使用ar -tv查看库中的目录列表 t:列出静态库中的文件 v:verbose 详细信息 使用静态库: gcc编译时使用第三方静态库必须包含以下选项: -I指定头文件目录 -L指定库路径 -l指定库名(不加空格),库名要把文件名前面去掉lib,后面去掉.a才是真正的库名 否则编译器不知道头文件在哪,或者不知道链接哪个库 库搜索路径库的搜索会按照一定的顺序 从左到右搜索-L指定的目录。 由环境变量指定的目录 (LIBRARY_PATH) 由系统指定的目录 /usr/lib /usr/local/lib 样例测试程序(共5个文件) 12345678910111213141516171819202122232425262728293031323334353637// ...
fork子进程,进程退出与等待
认识fork()头文件<unistd.h>提供的fork()函数用于从已有的原进程创建一个新的子进程,而原进程在关系式称为父进程 fork()的返回值12#include <unistd.h>pid_t id = fork(); 父子进程中fork()函数的返回值(此处用变量id储存)是不同的: 父进程里id的值为子进程的PID,其值>0;子进程里id值固定为0 id > 0 父进程 id == 0 子进程 id < 0 fork()失败 分流 利用父子进程中fork()返回值的不同,可以用if...else...进行分流,让父子进程执行不同的代码 fork()的过程进程调用fork,当控制转移到内核中的fork代码后,内核做 分配新的内存块和内核数据结构给子进程 将父进程部分数据结构内容拷贝至子进程 添加子进程到系统进程列表当中 fork返回,开始调度器调度 当一个进程调用fork之后,就有一对二进制代码相同的父子进程。而且它们都运行到相同的地方。但每个进程都可以独立地继续运行代码,并按代码分流至不同的代码段 但这两个进程谁先执行 ...
环境变量
基本概念 环境变量(environment variables)一般是指在操作系统中用来指定操作系统运行环境的一些参数 环境变量通常具有某些特殊用途,还有在系统当中通常具有全局特性 构成:环境变量是一系列字符串的统称,所以一个环境变量由变量名和值构成 这么说还是太抽象了,我们接下来会举几个具体样例,体会环境变量在获取系统全局的变量,系统指令路径等方面的作用 常见环境变量 变量名 功能 PATH 指定命令的搜索路径 HOME 指定用户的主工作目录(即用户登陆到Linux系统中时,默认的目录) USER 当前用户名 SHELL 当前Shell,它的值通常是/bin/bash PWD 当前工作目录 操作系统变量查看环境变量echo $NAME NAME为变量名 以PATH为例,查看PATH的值的指令为 1echo $PATH https://picbed0521.oss-cn-shanghai.aliyuncs.com/blogpic/PixPin_2024-07-08_12-46-35.png 可以看到PATH的内容为多个文件路径,互相以:分隔 而若要查看 ...
初识进程
操作系统平台:Linux服务器系统: CentOS 7 概念抽象程序程序 = 代码 + 数据 程序是储存在硬盘上的可执行文件 进程将程序加载到内存后,就在内存中程序的就是进程。也就是说一个正在运行的程序就能叫做进程 结构关系如下 如图,操作系统为了管理内存中的进程,使用了PCB结构体来描述进程,通过管理PCB来管理进程,依然是先描述再组织 PCB:进程控制块的数据结构(process control block) 所以实际上:进程=PCB+代码和数据 对于代码和数据没什么好说的,接下来主要讨论PCB task_structLinux平台下的PCB叫做task_struct task_struct内容分类: 标示符: 描述本进程的唯一标示符,用来区别其他进程。 状态: 任务状态,退出代码,退出信号等。 优先级: 相对于其他进程的优先级。 程序计数器: 程序中即将被执行的下一条指令的地址。 内存指针: 包括程序代码和进程相关数据的指针,还有和其他进程共享的内存块的指针 上下文数据: 进程执行时处理器的寄存器中的数据。 I/O状态信息: 包括显示的I/ ...
C/C++ static关键字的使用
因为static的用法又多又杂,值得单出一篇博客用以汇总 C/C++ 通用用法局部变量->全局属性当对原本声明在函数栈帧里的变量使用static修饰时,该变量的存储空间会改变到静态区,不会随着函数栈帧的销毁而销毁。 初始化:初次调用声明语句时会执行声明操作,而之后再执行到该语句处时会自动跳过。 作用范围:与不加static时的作用范围相同,还是局部可用 销毁:和全局变量一样在main函数的栈帧销毁时一并销毁 全局变量->限制访问原本同级文件夹下的源文件可以用extern关键字互相获取全局变量,但如果用static修饰本地全局变量,那么这个全局变量只能在本文件调用,而其它文件看不到它 全局函数->限制访问原本同级文件夹下的源文件可以用extern关键字声明函数,然后去其它源文件的全局函数中寻找实现方式,但如果用static修饰本地全局函数,那么这个全局函数的实现只能在本文件调用,而其它文件看不到它 C++类和对象成员变量->静态成员 (全局变量)原本声明的成员变量在实例化后,属于由类实例化出来的对象,生命周期与所属对象相同,但在加了static后 ...
归并排序
时间复杂度: O(nlogn)空间复杂度: O(n)稳定性: 稳定实现语言: C/C++ 原理思想这里采用的是分治的思想,但与快速排序相反的是,归并排序采用的是先分治再合并。 已知在有额外空间的情况下,合并两个有序数组得到一个新的较长有序数组是很高效的。 所以能不能把一个任意数组分成由左右两个有序数组组成然后合并成有序数组呢? 显然不能,大部分情况并不能分成两个有序数组,但如果在此之前用同样的方法(这里采用递归)事先排序左右两部分呢?大部分情况依然不能,因此这个递归会一直递推下去,最终待排序区间不断缩小,到只剩一个或零个元素,此时就可以将其看为有序数组了,也就是说递归在这里停止,可以一路合并有序数组一路回归上去了 分治这里使用左右指针控制待排序区间(迭代器也行),并采用递归的方式形象地完成分治操作 123456789101112131415161718void _MergeSort(vector<int>& arr,int left,int right,vector<int>&tmp){ //分治 if(le ...
=Linux=一步步自己写一个shell程序
系统:阿里云服务器Linux CentOs 7 编辑器: vim 编译器: gcc (支持C99) 文件本次写的程序较为简单,所以只使用一个源文件 所以在shell中touch一个makefile和一个myshell.c shell 12touch makefiletouch myshell.c 然后编辑makefile文件 makefile 1234561 myshell:myshell.c gcc -o $@ $^ -std=c99.PHONY:cleanclean: rm -f myshell 头文件本程序因函数较杂,会include较多头文件 myshell.c 12345#include <stdio.h>#include <stdlib.h>#include <string.h>#include <unistd.h>#include <assert.h> 宏定义为了统一修改部分参数,以及使参数更易读,这里使用部分宏定义 myshell. ...
堆排序
背景知识 知道什么是大堆/小堆 掌握如何将数组与完全二叉树的映射关系 掌握向上调整法和向下调整法 大堆/小堆大堆的特性:每一个节点的值都比左右孩子都大,根的值是整个大堆中最大的小堆的特性:每一个节点的值都比左右孩子都小,根的值是整个大堆中最小的 后面以大堆为例 数组映射成完全二叉树任何一个数组可以看成一个完全二叉树,下标0为二叉树的根 而非常方便的是,已知一个节点的下标,可以利用数学关系求出根或孩子的下标 下标关系如下(变量均为下标) parent = (child-1)/2 left_child = parent*2+1 right_child = parent*2+2 建堆方法向上调整法在已有一个大堆的前提下,把一个新的数据插入到堆的最后一个节点(此时破坏大堆的结构),再一路向上调整,可以重新建堆 123456789101112131415161718template<class T>void adjust_up(vector<T>& arr, int child){ int parent = (child - ...
C++文件操作
注:追求代码简洁,有一致的C++风格,可参阅本篇博客,若追求更高的读写效率,建议参阅C语言篇 但文章还没写 本篇文章主要研究头文件fstream中的函数和类 目前C++文件操作主要有两种流派,一种是声明fstream对象,另一种是分开声明ifstream和ofstream 注意,本文代码为了简洁,都是在展开std命名空间的前提下书写 fstream的使用先写一段示例代码 12345678910111213141516171819202122#include <iostream>#include <fstream>using namespace std;int main(){ fstream f("file.txt", ios::out);//调用构造函数以out模式打开文件file.txt 注:out模式下file.txt 会自动创建 string str = "This is a sentence";//在内存中准备一段字符串 f << str;//将字符串从内存写入文件 f.close();//关 ...
通过设计list类深入理解iterator迭代器
前置博客:从构建一个Date类入门C++类与对象设计模式介绍:TODO Iterator迭代器实际上也是一种设计模式,它提供了一种方法顺序访问一个聚合对象中各个元素 下面先迅速地搓一个list类 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859template <class T>//先用模板创建一个节点类struct ListNode{ T _val; ListNode<T>* _next; ListNode<T>* _prev; //提供全缺省的默认构造函数 ListNode(const T& val = T()) :_val(val), _next(nullptr), _prev(nullptr) {}};//用ListNode构造list类template <class T>class list{ typ ...
vim基础指令集
Vim是一款文本编辑器,下面介绍在vim界面中的常用指令 三种模式:命令模式(Command Mode) 插入模式(Insert Mode 命令行模式(Command-Line Mode)(这里称命令行模式为底行模式) 三者关系如下图 命令模式vim界面中多摁几次ESC就能退出其它模式回到命令模式,在这个模式下可以使用一系列vim快捷键 底行模式tips:不管目前是什么模式,先狂按ESC,回到命令模式,然后输入:进入底行模式,准备开始输命令 命令组成 保存:w->强制保存!w 退出:q->强制退出:!q 保存并退出:wq-.强制保存并退出:!wq 对比:vs +(源文件路径) 插入模式在命令模式下按键盘i进入插入模式,执行正常的文本编辑功能
avatar
副驾supdriver
动物界 脊索动物门 哺乳纲 灵长目 人科 人属 智人种
我github还蛮大的
公告
主域名:
supdriver.top
网站资讯
文章数目 :
59
已运行时间 :
本站总字数 :
154.8k
本站总访问量 :
最后更新时间 :