- 博客(104)
- 收藏
- 关注
原创 Linux网络:传输层协议 TCP协议的特性(2)
滑动窗口快速重传延迟应答捎带应答可靠性方面校验和序列号(按序到达)确认应答超时重发连接管理流量控制拥塞控制。
2024-09-09 20:53:14
1014
原创 Linux网络:传输层协议 TCP/UDP(1)
如果缓冲区的数据满了,再来的UDP数据会被直接丢弃。32位确认序号:发送报文的末端序列号+1,告知对方发送报文的末端序列号前的数据已经收到,下次发送从发送报文的末端序列号+1处开始发送。在接受到对端的报文时,根据确认应答机制将缓冲区中剩余空间的大小通过响应告知对端,当接近为0时对端就会停止发送报文,避免了上述问题。但TCP在接受报文时会根据报文号进行排序,所以最后在缓冲区的TCP报文最后是跟发送时的顺序是一致的。在TCP缓冲区中进行报文重排(数据有序性),上层来不及读取的报文暂时保存在接受缓冲区中等等。
2024-09-09 20:30:14
1031
原创 Linux网络:应用层协议http/https
服务器接受到请求报头时,识别http版本,用对应的http版本进行处理业务。URL会将这些关键字进行转义,将需要转码的字符转化为16进制,然后从右到左取4位,不足4位直接处理,再每2位做一位前加上%,编码成%XY的形式。200(OK) ,404(Not Found) ,403(Forbidden) ,302(重定向) ,504(Bad Gateway)等等。响应报头中的状态码:表示服务器处理请求的情况,常见的状态码有200(OK)、404(Not Found)等。后查询字符串,是这次http请求的参数。
2024-09-09 17:28:11
1451
原创 Linux网络:网络套接字-TCP回显服务器——多进程/线程池(生产者消费者模型)
注意:在类内部的成员函数,调用类内非静态成员函数时会自动带this指针。而线程执行的函数要求。或者可以忽略SIGCHID信号让子进程不需要等待。类型,所以线程执行函数要用static修饰。客户端代码不需要改变。客户端代码部分不变。
2024-09-09 16:32:45
743
原创 Linux网络:网络套接字-UDP服务器(单线程)
在网络中规定网络序列,在网络序列上数据都是大端形式。2.无符号32位整数[0-255].[0-255].[0-255].[0-255]。注意:udp服务器的端口需要绑定。一台机器上一个进程可以与端口号绑定,绑定后就在网络层面唯一标识一台主机上的一个进程。IP号用来保证数据可以机器到机器传递,进程还要绑定进程号来标定自身。但一台机器上存在大量进程,但不是所有进程都要网络通信。返回值:成功返回文件描述符,失败返回-1,设置错误码。返回值:绑定成功返回0,失败返回-1。进程PID标识系统所有进程,属于系统范畴。
2024-09-09 11:44:52
1411
原创 Linux网络:网络协议栈&协议
数据又通过路由器网络层的封包到路由器的数据链路层,此时添加的报头是令牌环报头。如果两个局域网都是通过以太网通信比较简单,但如果两个局域网一个用以太网,另一个用令牌环通信,就存在在数据链路层使用的协议不同,添加的网络报头不同,无法直接通信的现象。数据帧上有dst MAC地址,当MAC数据帧在局域网上传输时,每一台主机与dst MAC地址对比,如果匹配上,则处理数据。在主机A向主机B传递报文时,局域网上所有的主机底层都可以收到报文数据,只不过只有主机B对报文进行了解析,其他主机直接丢弃了报文。
2024-09-09 11:03:32
1265
转载 Linux系统:生产消费模型 & 读者写者模型
当不满足条件,或者线程被唤醒,此时就可以_blockQueue.push(in)来投放任务了,当投放完任务,此时通过pthread_cond_signal(&_consum_cond)唤醒一个消费者来执行任务。以上代码中, pthread_mutex_lock(&_mutex)先加锁,随后while语句判断当前阻塞队列是否满,如果满了就执行pthread_cond_wait(&_product_cond, &_mutex)去阻塞等待。是空闲的,它们却不能执行这个任务,这就出现了很明显的资源分配不合理问题。
2024-09-08 21:40:45
1183
转载 Linux系统:线程池(重要)
时,已经不算访问临界资源了,而调度函数的时间可能很长,此时先把锁释放掉,让其他线程拿任务,而不是自己执行完任务后才让别的线程拿任务,这样和单线程就没有区别了。目前为止我们还没有真正创建一个线程,而是通过类成员保存了线程的相关信息,那么我们就要通过这些线程的相关信息,来创建线程了。毫无疑问的是:只有任务队列里面有任务时,线程才能去任务队列中拿任务。线程池内部维护多个线程和一个任务队列,主线程往任务队列中放任务,线程池内部的线程则执行任务队列中的任务。我们的任务是:主线程往队列放任务,其它线程从队列拿任务。
2024-09-08 19:23:17
945
原创 Linux系统:线程同步
线程在占用锁的时候,是不受控制的,这就有可能导致一个竞争能力强的线程,从头到尾都占用一个锁。因为在进入等待队列前,线程是持有锁的状态,此处传入锁的指针,就是为了帮助这个线程释放该锁,从而让其他线程也可以申请锁,进入等待队列。此时所有被唤醒的线程再次竞争同一把锁,竞争到锁的线程才访问临界资源。当前一个线程访问完毕后,剩下的线程继续竞争,再访问临界资源。用于指明一个条件变量,说明要唤醒哪一个条件变量下等待的线程。用于指明一个条件变量,说明要唤醒哪一个条件变量下等待的线程。,这三个线程争夺一个临界资源。
2024-09-08 18:16:06
904
原创 Linux系统:线程互斥
如图所示,现在有两个线程thread-1和thread-2,它们共同征用内存中的锁mutex。指的是在多线程环境中,多个线程访问同一个共享资源时,只允许一个线程访问,其他线程必须等待,直到当前线程访问完成才能继续访问。死锁:指在一组进程中的各个进程均占有不会释放的资源,但因互相申请其它进程不会释放的资源而处于的一种。当线程没有申请到锁,一段时间后再次检测这个锁有没有被释放,一直反复申请这个锁,这个过程叫做。这一种锁,还有非常多的锁,接下来我们看看其它的锁。,谁就先抢到锁,而是谁先执行该函数内部的。
2024-09-08 17:07:18
1412
原创 Linux系统:线程概念和线程控制
而且从上方的struct page源码中可以发现,它是不存储页框的起始地址和终止地址的,因为可以通过下标计算出起始地址,起始地址 + 4kb就可以求出终止地址。可以看到,确实是有两个叫做test.exe的线程的,它们的PID都是141776,但是它们的LWP不同。另外的,你会发现第一个线程的PID = LWP = 141776,说明这个线程是主线程,其余的所有线程都是这个主线程创造的。在CPU调度进程的时候,本质是在调度进程的PCB,把进程的PCB放到运行队列中,然后CPU依次执行队列中的进程。
2024-09-08 16:06:12
1009
原创 Linux系统:信号的阻塞与捕捉
此处我们用(2) SIGINT做检测,先通过sigaddset(&set, 2)把set中的第二位变为1,随后通过sigprocmask(SIG_BLOCK, &set, nullptr)将set添加到block中,由于我们并不想知道旧的block是什么样,所以第三个参数设为nullptr。,获取当前的block,存到block_set中。此处要注意,第一次old_set提取到的,不是设置后的block而是设置前的block,也就是说现在old_set拿到的是block的默认值。从宏观上看,你可以理解为。
2024-07-15 19:38:49
889
原创 Linux系统:信号的概念与产生
CPU 接收这个信号后,会暂停当前执行的任务,转而去处理硬件设备的请求。由于现在的操作系统基本都是分时操作系统,因此实时信号其实是不符合设计理念的,几乎用不到实时信号,本博客只讲解。在handler中,会先输出get sig: 2,表示自己收到了信号,然后exit退出进程。进程中,而被终止的进程,是在前台运行的另外一个进程。来终止一个前台运行的进程,其实这就是一个发送信号的行为。信号,本质也是硬件中断,当我们从键盘输入了数据后,键盘向。通过键盘发送信号是最简单的信号发送方式,最常用的有。
2024-07-14 23:50:43
1163
原创 Linux系统:管道
进程间通信的目的,是为了让两个进程看到同一份资源,在Linux中,主要的进程间通信有管道system VPOSIX,本博客讲解管道。
2024-07-14 20:47:27
910
原创 C++:异常
一旦Division抛出异常,那么就是Func发生了异常,此时try就可以检测到,由于抛出的异常是const char*,我们要检测该类型的异常,所以catch的参数就是const char*了,catch (const char* errmsg)。4.抛出异常对象后,会生成一个异常对象的拷贝,因为抛出的异常对象可能是一个临时对象,所以会生成一个拷贝对象,这个拷贝的临时对象会在被。在实际开发中,由于异常抛出的类型是不确定的,公司内部一般会约定好如何抛异常,而异常继承体系是最优秀的一套体系。
2024-07-14 19:38:43
929
原创 C++:lambda表达式和包装器
在寄快递的时候,快递会进行一次包装,这样我们就可以统一的在上面贴上快递信息,随后以统一的形式管理所有快递。比如这个f1最后拿到了这个bind封装的函数,那么f1(3, 5)执行的并不是3 - 5,而是5 - 3。如果直接通过变量名捕获,此时是传值调用,修改函数体内部的变量,不会影响父作用域的变量。只要所有返回值和参数列表相同的可调用对象,经过这一层封装,都会变成相同的类型。是通过捕获列表捕获的变量,传入的参数带有const属性,不允许修改。的作用,其名称为捕获列表,可以捕获父作用域中所有变量。
2024-07-12 15:05:35
910
原创 C++:初始化&声明
以上代码中,最外层的{ }括起来的就是一个initializer_list,内部的三个{ }则是三个不同的pair,不过const char*可以转为string,因此最后pair会变成pair。保证了指针类型的稳定,空指针不会被传递到指针以外的类型。的适用范围,其可以用于所有的内置类型和自定义类型的初始化。可以检测一个变量的类型,并且拿这个类型去声明新的类型。
2024-07-12 14:11:11
436
原创 C++:右值引用
以上代码中,函数say_hello生成了一个string,并把它返回给外部,如果我们直接返回,那么str1接收参数时,就会先拷贝构造出一个临时变量,然后临时变量再拷贝构造str1。虽然说我们的左值引用,也可以达到这样的移动构造,但是有一个问题,并不是所有的对象,资源都是可以被转移走的。就是这样的一个逻辑闭环,右值引用以一个既安全,又高效的方式,完成了局部变量的资源拷贝问题。确实是这样的,当右值引用了。当右值引用了常量,引用会把常量区中的数据拷贝一份到栈区,然后该引用指向栈区中拷贝后的数据。
2024-07-12 13:39:46
1388
原创 C++:哈希表
如果我们想要搜索这个数据,直接计算出这个数据的下标,然后就可以直接访问数组对应的位置,所以可以用O(1)的复杂度直接找到数据。当这个哈希表越满,我们查找数据的效率就越低,甚至说:如果查找一个不存在的数据,我们可能要用O(N)的复杂度遍历整个哈希表.因此我们因该把哈希表的负载率控制在一定值,当超过一定值,我们就要进行扩容操作。闭散列,也叫做开放定址法,当发生哈希冲突时,如果哈希表没有被装满,说明哈希表中还有空位置,那么我们可以把发生冲突的数据放到下一个空位置去。转化的函数,被写为了一个模板,而这个。
2024-07-12 10:56:01
1215
原创 C++:对set和map的封装
2.cur->_kv.first这个过程是有问题的,其意图通过找到key值,然后比较当前节点与查找节点的大小,来决定下一步去左子树还是右子树找节点。但是我们对RBTreeNode改造后,其内部存储的已经不是_kv了,而是_data,对于set而言,这个_data是没有first这个成员的,因此我们不能直接通过_kv.first这种方式来访问key。有一个注意点,在typedef的时候,由于我们的RBTree是一个模板,我们到模板的域中访问了变量。,此时我们存储的数据就不一定是键值对的格式了。
2024-07-12 09:45:39
577
原创 C++:AVL树
其不会影响自身的AVL结构,所以不用对自生做操作,但是由于其高度增加了,所以有可能会影响其父节点,那么此时我们就要去看看父节点是否平衡。一般的二叉搜索树在插入新节点以及删除节点时,都有可能会破坏树的平衡,所以AVL树需要对插入以及删除接口做修改,每次插入删除时,都要检测一下当前的树时候符合AVL树,如果不符合,要做出相应的调整措施。,称为平衡因子(balance factor),其值为左右子树高度的差值,当这个差值的绝对值>=2,就说明这个节点的左右子树不平衡,那么我们就要使用相应的手段调整这棵树。
2024-07-11 19:32:06
1000
原创 C++:set和map
map的插入后,不论成功与否,都会返回一个pair<iterator, bool>,iterator用于指向key的迭代器,bool用于标识插入是否成功。而map的节点是pair<key, value>,所以我们解引用得到了一个pair,随后通过( ).second访问pair<key, value>的second,也就是value。它是基于红黑树实现的,它可以存储不重复的元素,并且会自动按照元素的大小进行排序。中的每个元素都由一个键和一个与之关联的值组成,键和值可以是任意类型。的类型)来调用默认构造。
2024-07-11 18:40:12
1099
原创 C++: 二叉搜索树
这是一个比较现代的赋值重载,注意我们在传参时BSTree t不是一个引用,而是一个不同的参数,此时参数t是实参的一份拷贝,是通过拷贝构造创建的,然后我们把这个形参拷贝构造出来的树直接交换给自己: swap(_root, t._root);首先,既然要删除特定的节点,那么我们就要先查找到该节点。既然要修改该节点,那么也要查找到其父节点,这个思路与前面的插入接口非常相似。想要拷贝一棵树出来,我们也需要进行递归式的深拷贝,不过由于要先有父节点,再有子节点,所以要用前序遍历。这个调用是存在问题的,那就是。
2024-07-11 18:07:08
709
原创 Linux系统:进程控制
WNOHANG: 若 pid 指定的子进程没有结束,则 waitpid() 函数返回 0 ,不予以等待。然后 shell 读取新的一行输入,建立一个新的进程,在这个进程中运行程序 并等待这个进程束。options==WNOHANG,非阻塞等待:对子进程进行检测,子进程没退出,直接返回0。参数: status 定义了进程的终止状态,父进程通过 wait 来获取该值。参数:status 定义了进程的终止状态,父进程通过wait来获取该值。当正常返回的时候 waitpid 返回收集到的子进程的进程 ID;
2024-04-20 20:19:46
2344
原创 C++:多态
先看到多态的定义:C++的多态是指在中,允许使用基类的指针或引用来调用派生类的虚函数的特性。这样的调用将根据对象的实际类型来动态绑定到适当的函数实现,实现了不同对象调用相同函数的不同行为。
2024-04-20 18:32:53
946
原创 C++:继承
继承是面向对象编程中的一个重要概念。它的由来可以追溯到软件开发中的模块化设计和代码复用的需求。在软件开发过程中,我们经常会遇到需要为多个类添加相同的行为或属性的场景,这样就产生了代码重复的问题。为了解决这个问题,工程师们开始寻找一种方法来实现代码的复用。继承就是一种解决代码复用问题的方式。它允许我们创建一个新的类,继承自一个已经存在的类,从而继承和复用父类的属性和方法。通过继承,我们可以在不改变父类的前提下,为子类添加额外的属性和方法,实现功能的扩展。
2024-04-20 17:22:03
731
原创 C++:stack&queue&priority_queue容器适配器介绍
栈(stack)是一种先进后出(Last-In-First-Out,LIFO)的数据结构,它的特点是只能在栈的一端进行插入和删除操作。由于大堆要保证每隔父亲节点大于两个子节点,而除去最后一个节点,其它的节点已经满足堆结构了,所以此处需要将最后一个节点不断地与其父亲节点比较,如果其比父亲节点大,就交换位置,然后继续和新的父亲节点比较,直到比当前的父亲节点小,或者到达堆顶为止。但是至此我们的优先级队列还不是完全体,因为其只能固定是大堆/小堆,或者说优先级的比较方式是固定的,想要解决这个问题,我们就需要仿函数。
2024-04-20 16:39:07
1149
原创 C++:vector的介绍
C++的vector是一种可变长度的,被广泛用于C++编程中。它是标准模板库(STL)中的容器之一,提供了比原始数组更灵活和方便的操作。vector可以存储任意类型的元素,包括基本类型(如整数和浮点数)和自定义类型(如类和结构体)。它的大小可以根据需要动态调整,而不需要手动管理内存。vector支持随机访问,即可以通过下标直接访问容器中的元素。它还提供了一系列的成员函数和操作符,如插入、删除和查找等,使得对元素的操作变得更加方便和高效。vector。
2024-04-20 10:16:27
953
原创 C++:模板初阶
当我们希望把一些类中的成员定义在类的外部时,那就需要声明和定义分离。假设我们希望分离析构函数~stack。//声明private://函数体所以我们的类模板也要类型::函数名来限定作用域。类模板的类型刚刚介绍过,就是stack//函数体对于类模板,当在类外定义函数时,要添加模板参数列表。//函数体。
2024-04-20 09:54:50
1091
原创 DFS深度优先搜索刷题(二)
算法思想:dfs按列扫描,在每一列具体扫描中向下扫描行,设置行的状态,当某一行已经放过棋子,跳过此行,继续扫描下一行,直到找到没有放过棋子的一行,就将此行状态设置为真。算法思想:设置瓷砖状态st,这里瓷砖状态是否走过决定计数与否,因为可以重复走过但只记一次,所以可以不用回溯。每一次dfs都记录此时的坐标与进入可能的新坐标。算法思想:组合型枚举,带一个start变量保证以后的不小于start即可,在带一个即时记录所用数字和变量判断剪枝。类似上题,记录水格状态即可。ps:注意一列不放棋子的情况。
2024-03-26 23:32:46
409
原创 Linux系统:进程概念认识
进程的信息可以通过 /proc 系统文件夹查看进程id(PID) 父进程id(PPID) 通过系统调用创建进程 -fork 初识进程状态cpu资源分配的先后顺序,就是指进程的优先权(priority)。优先权高的进程有优先执行权利.配置进程优先权对多任务环境的linux很有用,可以改善系统性能。还可以把进程运行到指定的CPU上,这样一来,把不重要的进程安排到某个CPU,可以大大改善系统整体性能查看系统进程用 top 命令更改已存在进程的 nice :
2024-03-25 11:19:20
1000
原创 DFS深度优先搜索刷题(一)
第x位选择状态为未选择的数并给该数设置已选择状态,然后枚举x+1位直至枚举完,判断是否符合,并根据具体情况进行剪枝。,一共三个位置,先构造一个火柴用量数字表,根据表依次枚举每个位置的火柴表示数,最后符合。,在通过剪枝去除错误的方案来减少时间开销。两个条件后记录数+1,注意当火柴用量超出时剪枝。用dfs暴力枚举出所有组合情况,主要是通过。依旧dfs暴力枚举出每种排序,主要。x位以后可选的数是否最终能达到要求。,实现组合不重复的实现。,可以通过dfs深度优先搜索。,注意回溯时当前位置的置空。
2024-03-23 22:56:05
427
原创 滑动窗口(二)
算法思想:可以用哈希表记录是否已经采摘过扫描指针指向的水果(入窗口),如果没有,则令type++,然后判断当type>2时,则将left++(出窗口),令哈希表中left指向的水果种类-1直到为0时,type--,保证type
2024-03-23 20:15:13
415
原创 双指针(滑动窗口)-算法刷题
cur为扫描指针,dest为移动指针,初始cur为0,dest为-1;当dest移动到最后一个元素时,cur指向最后一个要复写的元素。但这里dest可能会超出(说明cur此时指向0),所以要先判断边界位置。
2024-03-22 19:17:25
1309
1
原创 C++:string的介绍
C语言中,字符串是以'\0'结尾的一些字符的集合,为了操作方便,中提供了一些str系列的库函数,但是这些库函数与字符串是分离开的,不太符合面向对象的思想,而且底层空间需要用户自己管理,稍不留神可能还会越界访问。所以在C++的STL库中提供了更加方便的string类。
2024-03-08 16:41:35
1166
1
原创 二分查找算法--刷题
二分查找也常被称为二分法或者折半查找,每次查找时通过将待查找区间分成两部分并只取一部分继续查找,将查找的复杂度大大减少。对于一个长度为 O(n) 的数组,二分查找的时间复杂度为 O(log n)。实质是验证位置是否符合要求,将数组分为符合要求的区间与不符的区间,找到临界位置。但值得注意的是:二分查找主要针对的是有序数组。
2024-03-02 09:46:58
726
空空如也
空空如也
TA创建的收藏夹 TA关注的收藏夹
TA关注的人
RSS订阅