- 博客(112)
- 收藏
- 关注
原创 【Linux】网络基础_4
有了翻译服务就可以让服务器提供这个服务。为了让网络与服务之间解耦合,我们不能直接在服务器上直接调用函数,我们最好使用包装器包装一下函数,然后通过回调的方法来访问服务。这个词库代表当前我们所能翻译的能力范围,如果想要翻译更多单词,则需要往里面添加单词。接着我们要将词库里的单词一一存入一个键值对当中。当客户端访问时以便能够提供服务。客户端:./udpclient 127.0.0.1 8888。首先我们需要一个翻译服务。基于UDP,我们实现一个简单的翻译。成功实现了简陋的网络翻译服务。OK,我们来使用一下。
2024-08-04 09:58:25
449
原创 【Linux】网络基础_3
IPv4、IPv6地址类型分别定义为常数AF_INET、AF_INET6. 这样,只要取得某种sockaddr结构体的首地址,不需要知道具体是哪种类型的sockaddr结构体,就可以根据地址类型字段确定结构体中的内容。socket API是一层抽象的网络编程接口,适用于各种底层网络协议,如IPv4、IPv6,以及后面要讲的UNIX DomainSocket. 然而, 各种网络协议的地址格式并不相同。我们真正在基于IPv4编程时, 使用的数据结构是sockaddr_in;
2024-08-02 23:54:47
563
原创 【C++】学习笔记——C++的类型转换
C语言的强制类型转换关闭或挂起了正常的类型检查,每次使用强制类型转换前,我们应该仔细考虑是否还有其他不同的方法达到同一目的,如果非强制类型转换不可,则应限制强制转换值的作用域,以减少发生错误的机会。static_cast用于非多态类型的转换(静态转换),编译器隐式执行的任何类型转换都可用static_cast,但它不能用于两个不相关的类型进行转换。相当于C语言的隐式类型转换。在C语言中,如果赋值运算符左右两侧类型不同,或者形参与实参类型不匹配,或者返回值类型与接收返回值类型不一致时,就需要发生类型转化,
2024-08-02 15:57:53
487
原创 【C++】学习笔记——特殊类的设计
由于饿汉模式在程序进入主函数前就需要把对象创建出来,如果单例模式太多,或者这个对象太大,则会拖慢程序的启动,对性能要求较高。C++11扩展delete的用法,delete除了释放new申请的资源外,如果在默认成员函数后跟上=delete,表示让编译器删除掉该默认成员函数。一个类只能创建一个对象,即单例模式,该模式可以保证系统中该类只有一个实例,并提供一个访问它的全局访问点,该实例被所有程序模块共享。我们需要判断是否是第一次使用实例,如果是第一次使用实例才创建对象,否则返回原本就存在的对象。
2024-08-01 22:27:32
574
原创 【C++】学习笔记——智能指针
来释放的,与 delete 匹配的是 new,如果不是new出来的对象如何通过智能指针管理呢?并不是全部替换,节点本身都还是shread_ptr,但是节点的前驱指针和后继指针改成了weak_ptr。unique_ptr虽然解决了auto_ptr的问题,但是限制太大了,如果非要多个指针指向同一块资源的话就没办法,于是C++又提供了新的智能指针——shared_ptr。内存泄漏的危害:长期运行的程序出现内存泄漏,影响很大,如操作系统、后台服务等等,出现内存泄漏会导致响应越来越慢,最终卡死。如内存泄漏检测工具。
2024-08-01 22:27:09
820
原创 【C++】学习笔记——异常
实际使用中很多公司都会自定义自己的异常体系进行规范的异常管理,因为一个项目中如果大家随意抛异常,那么外层的调用者基本就没办法玩了,所以实际中都会定义一套继承的规范体系。这样大家抛出的都是继承的派生类对象,捕获一个基类就可以了。
2024-07-31 20:26:09
595
原创 【C++】学习笔记——C++11_4
C++中可调用的对象有很多种,比如函数,函数指针,仿函数,类的静态成员函数,类的非静态成员函数以及lambda表达式等。人们开始觉得上面的写法太复杂了,每次为了实现一个algorithm算法, 都要重新去写一个类,如果每次比较的逻辑不一样,还要去实现多个类,特别是相同类的命名,这些都给编程者带来了极大的不便。std::bind函数定义在头文件中,是一个函数模板,它就像一个函数包装器(适配器),接受一个可调用对象(callable object),生成一个新的可调用对象来“适应”原对象的参数列表。
2024-07-31 10:17:47
686
原创 【C++】学习笔记——C++11_3
默认生成的移动构造函数,对于内置类型成员会执行逐成员按字节拷贝(浅拷贝),自定义类型成员,则需要看这个成员是否实现移动构造,如果实现了就调用移动构造,没有实现就调用拷贝构造。push系列函数在对于深拷贝的对象进行插入时,会调用一次构造+一次移动构造,但是emplace系列仅仅只会调用一个构造函数,它是将参数一个一个传入对象的,省去了移动构造的消耗。模板的万能引用只是提供了能够接收同时接收左值引用和右值引用的能力,但是引用类型的唯一作用就是限制了接收的类型,后续使用中都退化成了左值。这种情况该怎么办呢?
2024-07-31 10:17:21
932
原创 【C++】学习笔记——C++11_2
当我们往list中插入时,由于每插入一个数据,list就需要构造一个节点,数据赋值的时候就要产生拷贝,但是我们要是写一个右值引用+移动语义的构造函数,就可以将资源直接与右值交换,避免了拷贝。右值引用也只是解决了深拷贝的问题,深拷贝对象传引用返回只需要移动资源即可。浅拷贝的问题并没有解决,但是浅拷贝的代价比较小,也不需要解决。这里没有调用深拷贝的拷贝构造,而是调用了移动构造,移动构造中没有新开空间,拷贝数据,所以效率提高了。(如出了函数作用域,返回的对象还在,就可以使用左值引用减少拷贝)。
2024-07-30 13:48:03
222
原创 【C++】学习笔记——C++11_1
一般在模板情况下,我们不知道接下来要定义的变量是什么类型,我们就可以借助这个关键字,他会推导类型,然后根据这个类型来定义变量。在C++11中,扩大了用大括号括起的初始化列表的使用范围,使其可用于所有的内置类型和用户自定义的类型,使用初始化列表时,可添加等号。传统的C++语法中就有引用的语法,而C++11中新增了的右值引用语法特性,所以从现在开始我们之前学习的引用就叫做。语法上:引用都是别名,不开空间,左值引用就是给左值取别名,右值引用就是给右值取别名。左值引用就是给左值的引用,给左值取别名。
2024-07-30 13:47:38
994
原创 【C++】学习笔记——哈希_2
/ 哈希表, Hash是仿函数(哈希函数)public:// 构造函数_n = 0;// 析构函数// 遍历每个桶i++)// 遍历每个桶的每个数据节点delete cur;cur = next;private:// 哈希表每个位置都是一个链表(桶)// 数据个数size_t _n;
2024-07-23 17:24:01
259
原创 【C++】学习笔记——哈希_1
顺序结构以及平衡树中,元素关键码与其存储位置之间没有对应的关系,因此在查找一个元素时,必须要经过关键码的多次比较。在C++11中,STL又提供了4个unordered系列的关联式容器,这四个容器与红黑树结构的关联式容器使用方式基本类似,只是其底层结构不同。具有相同地址的关键码归于同一子集合,每一个子集合称为一个桶,各个桶中的元素通过一个单链表链接起来,各链表的头结点存储在哈希表中。unordered容器底层实现采用的是哈希表的存储结构,并不会对数据排序。例如:数据集合{1,7,6,4,5,9};
2024-07-23 13:09:33
1060
原创 【Linux】网络基础_2
主机在链路层会把路由器的MAC地址作为目标,将报文借助子网1传输给路由器,路由器会将报文进行解包再封装,修改MAC地址,源MAC地址变成路由器节点的MAC地址,目标MAC地址变成目标主机的MAC地址(可能会重复以上流程),再借助子网2把报文传输给目标主机。因为进程ID是操作系统的理论,端口号是网络方面的理论,如果同一使用进程ID,那么耦合程度就高了,我们要避免不同模块之间的过度耦合。端口号是一个2字节16位的整数,端口号用来标识一个进程,告诉操作系统当前的这个数据要交给哪一个进程来处理。
2024-07-22 16:50:45
573
原创 【C++】学习笔记——红黑树
红黑树节点的定义与AVL树及其相似,唯二的区别就是,红黑树没有平衡因子,红黑树有颜色值。// 枚举颜色enum ColorRED,BLACK// 颜色Color _col;,_kv(kv),_col(RED){}为什么我们构造函数将颜色值默认设置成红色?因为我们创建的节点有两个孩子,两个孩子都是空节点,都是黑色的,如果创建的节点不把原本的这个位置的黑色空节点给替换掉,那么这条路径就会比其他路径多出一个黑色,违反了红黑树的性质。因此,新创建的节点一定是红色的。
2024-07-21 15:32:40
719
原创 【Linux】网络基础_1
负责设备之间的数据帧的传送和识别。在网络环境下,主机距离过远时,必定会引发新的问题,比如数据可靠性,主机定位问题,数据传输等问题。OSI(Open System Interconnection,开放系统互连)七层网络模型称为开放式系统互联参考模型,是一个逻辑上的定义和规范,把网络从逻辑上分为七层,概念非常清楚。TCP/IP通讯协议采用了5层的层级结构,每一层都呼叫它的下一层所提供的网络来完成自己的需求。其实本质上都是一样的,传输层和网络层都是操作系统的部分,网络其实也是操作系统源代码的一部分。
2024-07-19 16:57:54
1093
原创 【C++】学习笔记——AVL树
/ AVLTree 节点// 指向左孩子的指针// 指向右孩子的指针// 指向父节点的指针// 平衡因子int _bf;// 存储的Key-Value结构数据// 节点的构造函数,_bf(0),_kv(kv){}
2024-07-19 13:44:28
764
原创 【Linux】多线程_8
我们之前学过可重入,同一个函数被不同的执行流调用,当前一个流程还没有执行完,就有其他的执行流再次进入,我们称之为重入。死锁是指在一组进程中的各个进程均占有不会释放的资源,但因互相申请被其他进程所站用不会释放的资源而处于的一种永久等待状态。如果将对临界资源的访问加上锁,则这个函数是线程安全的,但如果这个重入函数若锁还未释放则会产生死锁,因此是不可重入的。一个函数在重入的情况下,运行结果不会出现任何不同或者任何问题,则该函数被称为可重入函数,否则,是不可重入函数。条件:一个资源每次只能被一个执行流使用。
2024-07-16 11:34:39
744
原创 【Linux】多线程_7
POSIX信号量和SystemV信号量作用相同,都是用于同步操作,达到无冲突的访问共享资源目的。但POSIX可以用于线程间同步。这里演示的是单生产者单消费者的模型,可以在主函数改成多生产者多消费者的模型。
2024-07-15 12:22:18
383
原创 【Linux】多线程_5
在多线程编程中,一个或多个线程可能需要等待某个条件的发生,然后才能继续执行,而不是一直忙等。条件变量提供了一种机制,使得线程可以等待某个条件的发生,同时释放CPU资源给其他线程使用。当一个线程需要等待某个条件时,它会先获取互斥锁,然后调用条件变量的等待函数,此时互斥锁会被释放,线程。,所以生产者生产完数据之后不用等待消费者处理,直接扔给阻塞队列,消费者不找生产者要数据,而是直接从阻塞队列里取,阻塞队列就相当于一个缓冲区,平衡了生产者和消费者的处理能力。这里条件变量的使用方法和互斥锁几乎一样,这里不再描述。
2024-07-14 22:32:11
429
原创 【Linux】多线程_4
/ 定义互斥锁// 此处加锁// 此处是临界区// 此处解锁// 定义锁// 初始化锁// 将锁传入到线程中// 再加锁// 临界区// 再解锁// 销毁锁。
2024-07-13 22:54:32
318
原创 【Linux】多线程_3
默认情况下,新创建的线程是joinable的,线程退出后,需要对其进行pthread_join操作,否则无法释放资源,从而造成系统泄漏。如果不关心线程的返回值,join是一种负担,这个时候,我们可以告诉系统,当线程退出时,自动释放线程资源。线程分离底层依旧是属于进程,没有分开,线程分离只是一种状态,唯一的区别就是主线程不需要等待新线程。我们要是不关注线程的结果,只需要线程把自己的任务完成,这种情况就可以将线程进行分离。joinable和分离是冲突的,一个线程不能既是joinable又是分离的。
2024-07-13 22:54:20
547
原创 【Linux】多线程_2
为了避免主线程退出,但是新线程并没有执行完自己的任务的问题,主线程同样要跟进程一样等待新线程返回。多线程中,任何一个线程出现异常,都会导致整个进程退出 ---- 多线程代码往往健壮性不好。主线程退出 等同于 进程退出 等同于 所有线程都退出。来取消新线程线程,新线程会返回 -1。来退出线程,也可以在主线程中使用。函数则可以等待新线程返回。是退出进程的,可以使用。
2024-07-09 22:47:24
475
原创 【Linux】多线程_1
线程是进程内部的一个执行分支。一个进程内部可以有多个执行流(内核数据结构),这些执行流都是线程。刚跟我们说了,线程是CPU调度的基本单位,但是CPU怎么区分一个进程里的线程呢?pthread_create函数可以创建一个线程。进程 = 内核数据结构 + 进程代码和数据。线程是CPU调度的基本单位。
2024-07-09 16:42:09
238
原创 【Linux】进程信号_4
可重入函数是指一个函数在多执行流环境下能够正确执行,并且不会产生不可预料的结果或错误。重入的意思是指该函数可以被重复调用,且每次调用都能够独立执行而不会影响其他调用。通俗来讲就是对该变量不进行优化,指从内存中读取。在某些情况下,编译器会进行优化,导致结果不符合我们的预期,因此就需要。当某个信号的处理函数被调用时,操作系统会自动将当前信号假如进程的信号屏蔽字当中。如果处理完该信号,该信号同样也会自动从信号屏蔽字中 移除。函数中,接收到信号捕捉,执行信号自定义处理函数,但是信号自定义处理函数中也调用了。
2024-07-05 16:10:47
435
原创 【Linux】进程信号_3
在进程的PCB中,有两个位图,一个是判断是否收到相应信号的位图pending,一个是决定该位置的信号是否阻塞的位图block。注意,阻塞和忽略是不同的,只要信号被阻塞就不会递达,而忽略是在递达之后可选的一种处理动作。被阻塞的信号产生时将保持在未决状态,直到进程解除对此信号的阻塞,才执行递达的动作。信号从产生到递达之间的状态,称为信号未决(Pending)。9号信号、19号信号无法被屏蔽,18号信号会做特殊处理。可以读取或更改进程的信号屏蔽字(阻塞信号集)。进程可以选择阻塞 (Block )某个信号。
2024-06-26 21:40:17
575
原创 【Linux】进程信号_2
我们之前学管道通信的时候,当具有读功能的进程关闭读端时,此时由于写操作将毫无意义,所以软件会产生信号给具有写功能的进程,使其关闭写端。,可以通过捕获信号的方式再次设定闹钟。abort函数的作用是给自己发送固定信号(6号信号),终止进程。命令和键盘来生成信号,我们也可以使用系统调用来生成信号。alarm函数是一个闹钟,可以经过指定秒后发送。kill函数可以对指定进程发送指定信号。raise可以让进程自己调用指定信号。这里是除0异常,我们来看看会怎样。这里产生的信号是 8号信号。异常情况也可以产生信号。
2024-06-26 18:19:37
455
原创 【Linux】进程信号_1
当信号产生时,可选的处理方式有三种:①忽略此信号。②执行该信号的默认处理动作。③自定义处理信号,这种方式称为捕捉(Catch)一个信号。信号和信号量之间没有任何关系。信号是Linux系统提供的让用户/进程给其他进程发送异步信息的一种方式。此时进程当接收到2号信号时,就输出了一句话并没有终止进程。接下来我们让进程对2号信号做出自定义行为。信号可以由 kill 命令产生,也能够从键盘上产生。会被操作系统解释成2号信号。函数这样设置时,就代表将。
2024-06-25 21:08:18
822
原创 【Linux】进程间通信_4
每个数据块都被认为是有一个类型,接收者进程接收的数据块可以有不同的类型值。IPC资源必须删除,否则不会自动清除,除非重启,所以system V IPC资源的生命周期随内核。所有的进程访问临界资源,都需要申请信号量,所有的进程都必须要看到同一个信号量,说明信号量本身就是一个共享资源。进程访问临界资源本质就是申请信号量,申请成功才能访问临界资源,否者只能等待申请。我们申请的共享内存,如果进程结束了,但共享内存并不会释放,需要我们手动释放。管道文件的生命周期是随进程的,但是共享内存的生命周期是随内核的。
2024-06-25 19:16:05
628
原创 【Linux】进程间通信_3
管道应用的一个限制就是只能在具有共同祖先(具有亲缘关系)的进程间通信。如果我们想在不相关的进程之间交换数据,可以使用FIFO文件来做这项工作,它经常被称为命名管道。命名管道是一种特殊类型的文件。命令就可以在当前路径下创建命名管道。
2024-06-24 23:27:37
455
原创 【Linux】进程间通信_2
②管道内部被写满,并且具有读端的继承没有关闭读端,写端写满管道pipe后,就需要阻塞等待,直到管道清空。③对于写端而言:关闭了写端管道,读端会将管道pipe中的数据读完,最后会读到返回值0,表示读取完毕。①管道内部没有数据,并且具有写端的进程没有关闭写端,读端就要阻塞等待,知道管道pipe内部有数据。④对于读端而言:关闭了读端管道,操作系统会直接终止具有写端的进程,通过十三号信号。④父子退出,管道自动释放,文件的生命周期是随进程的。②通过血缘关系进程进行通信,常见为父子进程。我们曾经学的命令行管道。
2024-06-24 20:51:17
643
原创 【Linux】进程间通信_1
但是我们通常需要多个进程协同,共同完成一件事,所以我们需要进程间通信的手段。进程间通信的本质就是先让不同的进程看到同一份资源,这个资源一般都是由操作系统提供。pipe函数的作用是创建一个管道,用于进程间通信,参数需要一个两个元素的数组,数组1用来读取管道数据,数组2用来向管道中写入数据。方式打开同一个文件就形成了管道,这个文件已经被加载在内存中,不需要向磁盘中刷新的方式叫做。匿名管道是如何让不同的进程看到同一份资源的呢?子进程会继承父进程的所有熟悉信息。父进程和子进程分别使用。这里的管道就是文件。
2024-06-23 21:25:37
582
原创 【Linux】基础IO_4
因为我之前编译的时候只是告诉了编译器我们的链接文件和头文件在哪,现在是运行当中,操作系统并不知道链接文件在哪。所以我们应该怎么解决呢?如果我们只提供静态链接,那么程序会对这个库进行局部静态链接,程序的其他库仍可能是动态链接的。如果我们只提供动态库,强制使用静态链接则会发生链接报错。如果我们同时提供动态库和静态库,gcc默认使用的是动态库。如果我们非要静态链接,我们必须使用static选项。所有系统进程中公共的代码和数据,只需要在物理内存中存在一份即可。命令就是告诉编译器我们链接的库在哪个路径下。
2024-06-22 22:09:02
323
原创 【Linux】基础IO_3
硬链接的本质就是在指定的目录下,插入新的文件名和目标文件的映射关系,并让inode的引用计数++。我们之前使用过库,也对库有一定的了解,这里我们将对库进行更深入的理解。可以给目录建立软链接,但是不能建立硬链接(除非系统建立的)。即便是我们没有实现 mymath 库中函数,我们同样可以用。可以通过目录的硬链接数 -2 计算得到目录有多少个子目录。软链接的本质就是一个独立文件,软链接内容里面放的是。硬链接数从 1 变成 0 后才会清空文件的信息。文件的删除本质上是减少硬链接数。可以创建软链接,直接使用。
2024-06-22 22:06:51
936
原创 【Linux】基础IO_2
未使用的block和inode的数量,一个block和inode的大小,最近一次挂载的时间,最近一次写入数据的时间,最近一次检验磁盘的时间等其他文件系统的相关信息。文件的内容大小不确定,但是文件的属性大小是确定的(文件的名称不是文件的属性!),因此文件的内容和文件的属性不会存在一起。文件的属性是一个结构体,里面存着文件的属性。系统中不是所有对文件都是打开的状态,大部分的文件都是没有被打开的。而文件就存储在这一个个扇区当中。文件的属性的结构体中存储着文件内容所在的块的编号,通过这个编号就能找到相应的文件。
2024-06-20 16:36:19
422
原创 【Linux】基础IO_1
在操作系统内部,进程PCB中存储着一个结构体指针,指向一个文件描述符表,这个表是一个结构体指针数组,内部存的是文件结构体指针,分别指向不同的打开的文件。我们从键盘接收输入,打印到显示器上,但键盘和显示器都是硬件资源,他们统统被操作系统所管理,我们的文件(进程)无法绕过操作系统打开这些资源,所以操作系统必须要提供一些接口来支持文件访问。所以我们可以:close(1),即关闭stdout流,然后新打开的文件的fd就是1,但是输出函数并不知道,向显示器打印的字符串统统转变成向文件内写入。那打开文件是什么意思呢?
2024-06-19 17:13:10
660
1
原创 【Linux】进程_9
根据我们之前学的知识,这里来编写一个 shell 程序。102464// 用户名// 主机名// 路径上面代码可以通过环境变量接口获取指定信息。我们先输出一下命令行提示符试试看。封装输出命令行提示符的信息和接收用户输入成一个函数。我们获取到了用户一行输入的整个字符串,现在需要对这个字符串进行切割。// 分隔符是空格int i = 0;当做完以上操作后就可以执行命令了,我们创建一个子进程,让子进程来程序替换为我们要执行的命令。我们封装一下执行命令。
2024-06-18 15:50:57
370
原创 【Linux】进程_8
阻塞等待就是当父进程没有收到子进程的返回结果时,会在此阻塞,非阻塞等待就是父进程没有收到子进程的返回结果时,仍可以执行其他代码,并不会将父进程阻塞至此。我们知道,创建一个进程时,先创建的是 PCB,地址空间,页表等,然后再将程序加载到内存,将程序加载到内存与程序替换的本质是一样的。由于进程具有独立性,子进程替换的时候会发生写实拷贝,所以不影响父进程的代码,父进程同样可以等待子进程的返回结果。这个参数其实就是进程的返回结果,当子进程结束的时候,就会返回这个值给父进程。进程程序替换,不会替换环境变量数据。
2024-06-17 22:24:53
582
原创 【Linux】进程_7
申请内存时,其实是向虚拟地址申请合法空间,此时页表并不将申请的虚拟地址空间映射到物理地址,即此时并没有申请到合法的物理空间,知道我们向申请的空间中写入的时候,页表才会映射物理地址,才真正的申请了内存中的物理空间,然后向物理空间中写入。我们上节知道了进程地址空间是根据页表来使虚拟地址转换成内存中的物理地址,那这种 地址空间 + 页表 的机制有什么好处呢?父进程通过进程等待的方式,回收子进程资源(必要的,防止出现僵尸进程),获取子进程退出信息(可选的)。进程的退出场景:①代码运行完毕,结果正确。
2024-06-17 17:13:57
374
空空如也
空空如也
TA创建的收藏夹 TA关注的收藏夹
TA关注的人