线程处理—线程创建1.Linux 线程简介Linux 中的线程是指轻量级的执行单元,相比于进程,具有以下特点:
**进程(Process)是正在执行的程序的实例。每个进程都有自己的地址空间、代 码段、数据段和打开的文件描述符等资源。线程(Thread)是进程内的一个执行单元,它 共享相同的地址空间和其他资源,包括文件描述符、信号处理等,但每个线程都有自己的 栈空间。 **
**由于共享地址空间和数据段,同一进程的多线程之间进行数据交换比进程间通信 方便很多,但也由此带来线程同步问题。 **
**同一进程的多线程共享大部分资源,除了每个线程独立的栈空间。这代表线程的 创建、销毁、切换要比进程的创建、销毁、切换的资源消耗小很多,所以多线程比多进程 更适合高并发。 **
2.相关函数2.1 系统调用pthread_create线程操作相关函数来源于 pthread 共享库:
12345678910111213141516171819#include <pthread.h>/*** 创建一个新线程* * pthread_t *thread: 指向线程标识符的指针,线程创建成功时 ...
进程通讯—信号处理signal 系统调用是 UNIX 和类 UNIX 系统(如 Linux)中用于处理信号的一个基础机制。信号是软件中断,它们可以被系统或者进程发送给另一个进程或线程,以通知它们发生了某个事件。这些事件可以是硬件中断(如外部设备请求服务)、软件异常(如除零错误)、用户请求(如用户请求停止进程)等。
1.基本用法signal 函数的原型定义在 <signal.h> 头文件中,我们可以通过 signal 系统调用注册信号处理函数:
123456789101112#include <signal.h>// 信号处理函数声明typedef void (*sighandler_t)(int);/*** signal 系统调用会注册某一信号对应的处理函数。如果注册成功,当进程收到这一信号时,将不会调用默认的处理函数,而是调用这里的自定义函数* * int signum: 要处理的信号* sighandler_t handler: 当收到对应的 signum 信号时,要调用的函数* return: sighandler_t 返回之前的信号处理函数,如果错误会返 ...
进程通讯——消息队列在Linux系统中,进程间通信(IPC, Inter-Process Communication)是一种允许不同进程或同一进程的不同线程之间交换数据的机制。消息队列是IPC的一种形式,它允许一个或多个进程向它写入或从中读取消息。这些消息是用户定义的数据块,它们存储在内核中,直到被接收进程取走。
1.相关函数1.1 数据类型mqd_t**该数据类型定义在 **mqueue.h 中,是用来记录消息队列描述符的
12typedef int mqd_t; //实质上是 int 类型的别名。
1.2 结构体struct mq_attr在POSIX消息队列中,mq_attr 结构体用于指定消息队列的属性。这个结构体在 <mqueue.h> 头文件中定义,并且当你创建或打开消息队列时,可以通过这个结构体来指定或获取消息队列的属性。
1234567891011121314/*** @brief 消息队列的属性信息* mq_flags 标记,对于 mq_open,忽略它,因为这个标记是通过前者的调用传递的* mq_maxmgs 队列可以容纳的消息的最大数量* mq_m ...
进程通讯—共享内存1.共享内存的定义与原理1.1定义共享内存是指允许多个不相关的进程访问同一个逻辑内存区域。这种内存区域在物理上通常是同一段内存,但在虚拟地址空间中,不同进程通过各自的页表将其映射到这一共同的物理内存区域。
1.2原理在Linux系统中,每个进程都有自己的进程控制块(PCB)和地址空间(Addr Space),以及与之对应的页表。通过页表,进程的虚拟地址被映射到物理地址。当两个或多个进程通过页表将虚拟地址映射到同一块物理内存区域时,这块区域即成为共享内存。这样,当一个进程修改共享内存中的数据时,其他进程通过访问共享内存就能立即看到这些改动。
2.共享内存的特点
高效性:共享内存是进程间通信中最快的方式之一,因为它减少了数据复制的次数,直接在内存中进行数据交换。
灵活性:进程可以灵活地读写共享内存中的数据,实现复杂的数据交换和同步机制。
复杂性:共享内存的使用需要额外的同步机制(如信号量、互斥锁等)来避免数据竞争和条件竞争等问题。
2.创建共享内存对象2.1库函数shm_open()和 shm_unlink()shm_open() 可以开启一块内存共享对象,我们可以像使 ...
Linux内核实现进程通讯思路FIFO 和 Pipe 一样,提供了双向进程间通信渠道。但要注意的是,无论是有名管道还 是匿名管道同一条管道只应用于单向通信,否则可能出现通信混乱(进程读到自己发的 数据)
采用库函数 mkfifo() 创建fifo有名管道.
函数说明:
12345678910111213#include <sys/types.h>#include <sys/stat.h>/*** @brief 用于创建有名管道。该函数可以创建一个路径为 pathname 的 FIFO 专用文件,mode 指定了 FIFO 的权限,FIFO 的权限和它绑定的文件是一致的。FIFO 和 pipe 唯一的区别在于创建方式的差异。一旦创建了 FIFO 专用文件,任何进程都可以像操作文件一样打开 FIFO,执行读写操作。* * @param pathname 有名管道绑定的文件路径* @param mode 有名管道绑定文件的权限* @return int */int mkfifo(const char *pathname, mode_t mode);
管道发送端创建f ...
学习环境搭建Linux(以Ubuntu为例)1sudo apt install gcc g++ make
Windows学习与演示过程以Windows为主,Windows上装MinGW环境,MinGW官网: https://www.mingw-w64.org/ 之前我们提过两个版本的环境,****MingW-W64-builds和w64devkit 推荐使用****w64devkit套件,里面工具比较齐全,还提供模拟了许多Linux命令,用这个套件环境来学习可以保持在Linux与Windows上Makefile书写方式一致。** **以下是w64devkit与其他包一些命令的区别
w64devkit(模拟Linux)
MingW-W64-builds或其他套件(Windows cmd命令)
make
mingw32-make
cc
gcc
rm
del
touch
ls
dir
sh
mv
cp
copy/xcopy
sed
Makefile基础知识make使用流程
准备好需要编译的源代码
编写Makefile文件
在 ...
二叉树01.概念一棵二叉树是结点的一个有限集合,该集合或者为空,或者是由一个根节点加上两棵别称为左子树和右子树的二叉树组成。
1.1 二叉树的特点
每个结点最多有两棵子树,即二叉树不存在度大于2的结点。
二叉树的子树有左右之分,其子树的次序不能颠倒。(排序树)
1.2 特殊的二叉树
满二叉树:一个二叉树,如果每一个层的结点数都达到最大值,则这个二叉树就是满二叉树。也就是说,如果一个二叉树的层数为K,且结点总数是(2^k) -1 ,则它就是满二叉树。
完全二叉树:完全二叉树是效率很高的数据结构,完全二叉树是由满二叉树而引出来的。对于深度为K的,有n个结点的二叉树,当且仅当其每一个结点都与深度为K的满二叉树中编号从1至n的结点一一对应时称之为完全二叉树。 要注意的是满二叉树是一种特殊的完全二叉树。
02.二叉树的性质2.1 性质1在二叉树的第i层上最多有2^(i-1)个结点(i≥1)。 第一层是根结点,只有一个,所以2(1-1)=20=1。 第二层有两个,2(2-1)=21=2。 第三层有四个,2(3-1)=22=4。 第四 ...
TCP网络实时通讯工具1. 开发流程
2.QTtcp客户端的关键流程2.1流程细节工程建立,需要在.pro加入网络权限
创建一个基于 QTcpSocket 的Qt客户端涉及以下步骤:
创建 QTcpSocket 实例:
实例化 QTcpSocket 。
连接到服务器:
使用 connectToHost 方法连接到服务器的IP地址和端口。
发送数据到服务器:
使用 write 方法发送数据。
接收来自服务器的数据:
为 readyRead 信号连接一个槽函数来接收数据。
关闭连接:
关闭 QTcpSocket 连接。
2.1代码实现123456789101112131415161718class MyClient : public QObject { Q_OBJECTpublic: MyClient() { QTcpSocket *socket = new QTcpSocket(this); connect(socket, &QTcpSocket::readyRead, this, &My ...
强制转换在 C++ 中,强制类型转换(或类型转换)是一种将变量从一种类型转换为另一种类型的方法。C++ 提供 了四种强制转换运算符,每种都有其特定的用途和适用场景:
static_cast static_cast
static_cast static_cast是最常用的类型转换运算符,用于无风险的转换,如整数到浮点数,字符到整 数等。
它在编译时执行,不执行运行时类型检查(RTTI)。
示例: int x = static_cast(y); 其中 y 可能是 float 类型。
dynamic_cast
专门用于处理对象的多态性,只能用于指针和引用,且涉及对象类必须有虚函数。
它在运行时检查类型的安全性,如果转换失败,对于指针类型返回 nullptr,对于引用类型 抛出异常。
示例: Derived *dp = dynamic_cast(bp); 其中 bp 是基类指针, Derived 是派生类。
const_cast
用于修改类型的 const 或 volatile 属性。
通常用于去除对象的 const 性质,允许修改原本被声明为 const 的变量 ...
加一问题描述给定一个由整数 组成的 非空 数组所表示的非负整数,在该数的基础上加一。
最高位数字存放在数组的首位, 数组中每个元素只存储单个数字。
你可以假设除了整数 0 之外,这个整数不会以零开头。
示例 1:
123输入:digits = [1,2,3]输出:[1,2,4]解释:输入数组表示数字 123。
示例 2:
123输入:digits = [4,3,2,1]输出:[4,3,2,2]解释:输入数组表示数字 4321。
示例 3:
12输入:digits = [0]输出:[1]
提示:
1 <= digits.length <= 100
0 <= digits[i] <= 9
思路从数组的最后一个元素(即整数的最低位)开始,将进位(初始为1,因为我们要加一)加到当前位上,然后更新当前位的值和进位。如果遍历完数组后还有进位(即最高位有进位),则在数组的最前面插入这个进位。这种方法避免了使用额外的数据类型(如long int或string),直接在原数组上进行操作,因此更加高效且避免了溢出问题。
代码实现12345678910111213141 ...