当前位置: 首页 > news >正文

第七章:进程间通信(IPC)——构成进程间通信的信道方案

系列文章目录


文章目录

  • 系列文章目录
  • 前言
  • 进程间通信介绍
    • 进程间通信目的
    • 进程间通信发展
    • 进程间通信分类
    • 进程通信的原理
  • 管道
    • 什么是管道
    • pipe
    • 管道通信特点
    • 简单设计
  • 命名管道
    • 什么是命名管道
    • mkfifo
    • strcmp/strncasecmp
    • unlink
    • getch
    • 简单设计
  • 共享内存
    • 什么是共享内存
    • shmget/ftok
    • ipcs
    • shmctl
    • shmat/shmdt
    • 共享内存的特点
  • 消息队列
    • msgget/msgctl
    • msgsnd/msgrcv
  • 信号量
    • 互斥等四个概念
    • 什么是信号量
    • semget/semctl
    • semop
  • 理解IPC
  • 总结


前言

进程通信是数据传输的重要方式。


进程间通信介绍

进程间通信目的

  1. 数据传输:一个进程需要将它的数据发送给另一个进程

  2. 资源共享:多个进程之间共享同样的资源。

  3. 通知事件:一个进程需要向另一个或一组进程发送消息,通知它(它们)发生了某种事件(如进程终止时要通知父进程)。

  4. 进程控制:有些进程希望完全控制另一个进程的执行(如Debug进程),此时控制进程希望能够拦截另一个进程的所有陷入和异常,并能够及时知道它的状态改变。

进程间通信发展

  1. 管道

  2. System V进程间通信

  3. POSIX进程间通信

进程间通信分类

  1. 管道

    • 匿名管道pipe

    • 命名管道

  2. System V IPC

    • System V 消息队列

    • System V 共享内存

    • System V 信号量

  3. POSIX IPC

    • 消息队列

    • 共享内存

    • 信号量

    • 互斥量

    • 条件变量

    • 读写锁

进程通信的原理

让两个不同的进程进行通信,前提条件是让两个进程看到同一份”资源“(不违背进程的独立性并且由OS提供直接或间接提供)。

任何进程通信手段:

  1. 先让不同的进程看到同一份资源。(通信方式)

  2. 让一方写入,一方读取,完成通信过程。

管道

什么是管道

管道是Unix中最古老的进程间通信的形式。

我们把从一个进程连接到另一个进程的一个数据流称为一个“管道”,管道也是文件 。

who | wc -l
// | 管道符

在这里插入图片描述

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aUyvLxqJ-1691281054764)(https://flowus.cn/preview/213b1e32-56a9-47eb-8eff-73cd9fba819b)]

  1. 创建子进程的时候,只会浅拷贝进程相关的数据结构对象,不会赋值父进程打开的文件对象!

  2. 管道是一个OS提供的内存匿名文件,以读写的方式打开。

  3. fork之后需要确定数据的流向,关闭不必要的fd。

  4. 因为只支持单向通信,所以称之为管道。

pipe

man pipe#include <unistd.h>int pipe(int pipefd[2]);On success, zero is returned.  On error, -1 is returned, and errno is set appropriately.

管道通信特点

  1. 单向通信(半双工)。

  2. 管道本质是文件,因为fd的生命周期随进程的,即管道的生命周期是随进程的。

  3. 管道通信,通常用来进行具有”血缘“关系的进程进行通信,常用于父子进程通信——pipe打开管道,并不清楚管道的名字——匿名管道。

  4. 管道通信中,写入的次数,和读取的次数,并不是严格匹配的(读写没有强相关)—— 面向字节流

  5. 管道具有一定的协同能力,让reader和writer能够按照一定的步骤进行通信 —— 自带同步与互斥机制

    • 如果read读取完毕了所有的管道数据,如果对方不发,就只能等待。

    • 如果write端将管道写满了,就不能写了(管道容量有限)。

    • 关闭了写端,读取完管道数据,再读取read就会返回0,表明读到了文件结尾。

    • 如果写端一直再写,读端关闭,OS会通过信号的方式杀死一直在写入的进程。

  6. 当要一次写入的数据量不大于PIPE_BUF时,linux将保证写入的原子性;当要一次写入的数据量大于PIPE_BUF时,linux将不再保证写入的原子性。

简单设计

父进程可以通过向子进程写入特定的消息,唤醒子进程,甚至让子进程定向的执行某种任务。

在这里插入图片描述

父进程要管理自己创建的管道和进程。

#include <iostream>
#include <string>
#include <vector>
#include <cassert>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#include "Task.hpp"
using namespace std;const int gnum = 3;
Task t;class EndPoint
{
private:static int number;
public:pid_t _child_id;int _write_fd;std::string processname;
public:EndPoint(int id, int fd) : _child_id(id), _write_fd(fd){//process-0[pid:fd]char namebuffer[64];snprintf(namebuffer, sizeof(namebuffer), "process-%d[%d:%d]", number++, _child_id, _write_fd);processname = namebuffer;}std::string name() const{return processname;}~EndPoint(){}
};int EndPoint::number = 0;// 子进程要执行的方法
void WaitCommand()
{while (true){int command = 0;int n = read(0, &command, sizeof(int));if (n == sizeof(int)){t.Execute(command);}else if (n == 0){std::cout << "父进程让我退出,我就退出了: " << getpid() << std::endl; break;}else{break;}}
}void createProcesses(vector<EndPoint> *end_points)
{vector<int> fds;for (int i = 0; i < gnum; i++){// 1.1 创建管道int pipefd[2] = {0};int n = pipe(pipefd);assert(n == 0);(void)n;// 1.2 创建进程pid_t id = fork();assert(id != -1);// 一定是子进程if (id == 0){for(auto &fd : fds) close(fd);//关闭其他进程的管道写端// std::cout << getpid() << " 子进程关闭父进程对应的写端:";// for(auto &fd : fds)// {//     std::cout << fd << " ";//     close(fd);// }// std::cout << std::endl;// 1.3 关闭不要的fdclose(pipefd[1]);// 我们期望,所有的子进程读取"指令"的时候,都从标准输入读取// 1.3.1 输入重定向,可以不做dup2(pipefd[0], 0);// 1.3.2 子进程开始等待获取命令WaitCommand();close(pipefd[0]);exit(0);}// 一定是父进程//  1.3 关闭不要的fdclose(pipefd[0]);// 1.4 将新的子进程和他的管道写端,构建对象end_points->push_back(EndPoint(id, pipefd[1]));fds.push_back(pipefd[1]);}
}int ShowBoard()
{std::cout << "##########################################" << std::endl;std::cout << "|   0. 执行日志任务   1. 执行数据库任务    |" << std::endl;std::cout << "|   2. 执行请求任务   3. 退出             |" << std::endl;std::cout << "##########################################" << std::endl;std::cout << "请选择# ";int command = 0;std::cin >> command;return command;
}void ctrlProcess(const vector<EndPoint> &end_points) 
{// 2.1 我们可以写成自动化的,也可以搞成交互式的int num = 0;int cnt = 0;while(true){//1. 选择任务int command = ShowBoard();if(command == 3) break;if(command < 0 || command > 2) continue;//2. 选择进程int index = cnt++;cnt %= end_points.size();std::string name = end_points[index].name();std::cout << "选择了进程: " <<  name << " | 处理任务: " << command << std::endl;//3. 下发任务write(end_points[index]._write_fd, &command, sizeof(command));sleep(1);}
}void waitProcess(const vector<EndPoint> &end_points)
{// 1. 我们需要让子进程全部退出 --- 只需要让父进程关闭所有的write fd就可以了!// for(const auto &ep : end_points) // for(int end = end_points.size() - 1; end >= 0; end--)for(int end = 0; end < end_points.size(); end++){std::cout << "父进程让子进程退出:" << end_points[end]._child_id << std::endl;close(end_points[end]._write_fd);waitpid(end_points[end]._child_id, nullptr, 0);std::cout << "父进程回收了子进程:" << end_points[end]._child_id << std::endl;} sleep(10);// 2. 父进程要回收子进程的僵尸状态// for(const auto &ep : end_points) waitpid(ep._child_id, nullptr, 0);// std::cout << "父进程回收了所有的子进程" << std::endl;// sleep(10);
}// #define COMMAND_LOG 0
// #define COMMAND_MYSQL 1
// #define COMMAND_REQEUST 2
int main()
{vector<EndPoint> end_points;// 1. 先进行构建控制结构, 父进程写入,子进程读取 , bug?createProcesses(&end_points);// 2. 我们的得到了什么?end_pointsctrlProcess(end_points);// 3. 处理所有的退出问题waitProcess(end_points);return 0;
}
#pragma once#include <iostream>
#include <vector>
#include <unistd.h>
#include <unordered_map>// typedef std::function<void ()> func_t;typedef void (*fun_t)(); //函数指针void PrintLog()
{std::cout << "pid: "<< getpid() << ", 打印日志任务,正在被执行..." << std::endl;
}void InsertMySQL()
{std::cout << "执行数据库任务,正在被执行..." << std::endl;
}void NetRequest()
{std::cout << "执行网络请求任务,正在被执行..." << std::endl;
}// void ExitProcess()
// {
//     exit(0);
// }//约定,每一个command都必须是4字节
#define COMMAND_LOG 0
#define COMMAND_MYSQL 1
#define COMMAND_REQEUST 2class Task
{
public:Task(){funcs.push_back(PrintLog);funcs.push_back(InsertMySQL);funcs.push_back(NetRequest);}void Execute(int command){if(command >= 0 && command < funcs.size()) funcs[command]();}~Task(){}
public:std::vector<fun_t> funcs;// std::unordered_map<std::string, fun_t> funcs;
};

命名管道

  • 管道应用的一个限制就是只能在具有共同祖先(具有亲缘关系)的进程间通信。

  • 如果我们想在不相关的进程之间交换数据,可以使用FIFO文件来做这项工作,它经常被称为命名管道。

  • 命名管道是一种特殊类型的文件

什么是命名管道

mkfifo fifo//创建一个文件名为filo的命名管道

有文件属性的内存级文件。

在这里插入图片描述

  1. 不同的进程打开同一个文件不会打开新的文件属性结构体对象。

  2. 两个进程打开同一个磁盘文件也可以构成磁盘通信信道。

  3. 为了不使用磁盘文件(避免需要缓冲区刷新),直接用内存级文件——命名管道文件来做”资源“。

  4. 命名管道与管道的原理一样。

  5. 命名管道必须要有文件名,所以称为命名管道。

【总结】:让不同的进程通过文件路径+文件名看到同一个文件,并打开就是看到了同一个”资源“。

mkfifo

man 3 mkfifo#include <sys/types.h>
#include <sys/stat.h>int mkfifo(const char *pathname, mode_t mode);On success mkfifo() returns 0.  In the case of an error, -1 is returned (in which case, errno is set appropriately).

strcmp/strncasecmp

man strncasecmp#include <strings.h>int strcasecmp(const char *s1, const char *s2);int strncasecmp(const char *s1, const char *s2, size_t n);man strcmp#include <string.h>int strcmp(const char *s1, const char *s2);int strncmp(const char *s1, const char *s2, size_t n);

unlink

man 2 unlink#include <unistd.h>int unlink(const char *pathname);

getch

man getch#include <curses.h>int getch(void);

简单设计

#pragma once#include <iostream>
#include <string>#define NUM 1024const std::string fifoname = "./fifo";
uint32_t mode = 0666; 
#include <iostream>
#include <cerrno>
#include <cstring>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include "comm.hpp"//少年们, 我刚刚写了一个基于匿名管道的进程池
// 可不可以把它改成使用命名管道呢??
int main()
{// 1. 创建管道文件,我们今天只需要一次创建umask(0); //这个设置并不影响系统的默认配置,只会影响当前进程int n = mkfifo(fifoname.c_str(), mode);if(n != 0){std::cout << errno << " : " << strerror(errno) << std::endl;return 1;}std::cout << "create fifo file success" << std::endl;// 2. 让服务端直接开启管道文件int rfd = open(fifoname.c_str(), O_RDONLY);if(rfd < 0 ){std::cout << errno << " : " << strerror(errno) << std::endl;return 2;}std::cout << "open fifo success, begin ipc" << std::endl;// 3. 正常通信char buffer[NUM];while(true){buffer[0] = 0;ssize_t n = read(rfd, buffer, sizeof(buffer) - 1);if(n > 0){buffer[n] = 0;//std::cout << "client# " << buffer << std::endl;printf("%c", buffer[0]);fflush(stdout);}else if(n == 0){std::cout << "client quit, me too" << std::endl;break;}else {std::cout << errno << " : " << strerror(errno) << std::endl;break;}}// 关闭不要的fdclose(rfd);unlink(fifoname.c_str());return 0;
}
#include <iostream>
#include <cstdio>
#include <cerrno>
#include <cstring>
#include <cassert>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
// #include <ncurses.h>
#include "comm.hpp"int main()
{//1. 不需创建管道文件,我只需要打开对应的文件即可!int wfd = open(fifoname.c_str(), O_WRONLY);if(wfd < 0){std::cerr << errno << ":" << strerror(errno) << std::endl;return 1;}// 可以进行常规通信了char buffer[NUM];while(true){// std::cout << "请输入你的消息# ";// char *msg = fgets(buffer, sizeof(buffer), stdin);//C库函数读取的是字符串,会处理'\0',系统调用处理字节流,不会处理'\0'.// assert(msg);// (void)msg;// int c = getch();// std::cout << c << std::endl;// if(c == -1) continue;system("stty raw");int c = getchar();system("stty -raw");//std::cout << c << std::endl;//sleep(1);//buffer[strlen(buffer) - 1] = 0;// abcde\n\0// 012345//if(strcasecmp(buffer, "quit") == 0) break;ssize_t n = write(wfd, (char*)&c, sizeof(char));assert(n >= 0);(void)n;}close(wfd);return 0;
}

共享内存

【system V共享内存】:

共享内存区是最快的IPC形式。一旦这样的内存映射到共享它的进程的地址空间,这些进程间数据传递不再涉及到\内核,换句话说是进程不再通过执行进入内核的系统调用来传递彼此的数据。

什么是共享内存

在这里插入图片描述

  1. 让不同的进程,先看到同一份资源——内存块–共享内存。

  2. 虚拟进程空间的共享区映射同一块物理内存。

  3. 在任意一个时刻,可能有多个共享内存在被用来进行通信,所以系统中一定存在很多shm同时存在,OS要管理所有的共享内存。所以共享内存不仅仅会在内存中开辟空间,OS也会构建对应的描述共享内存的结构体对象!

  4. 共享内存 = 共享内存的内核数据结构(伪代码:struct shm) + 真正开辟的内存空间

shmget/ftok

man shmget#include <sys/ipc.h>
#include <sys/shm.h>int shmget(key_t key, size_t size, int shmflg);On success, a valid shared memory identifier is returned.  On errir, -1 is returned, and errno is set to indicate the error.man ftok
#include <sys/types.h>
#include <sys/ipc.h>key_t ftok(const char *pathname, int proj_id);
// pathname:路径字符串 proj_id:项目ID

让两个进程映射同一个内存块。

在这里插入图片描述

  1. 传入同样的参数形成一样key_t,一个进程用key_t创建一个内存块并映射,另一个进程用key_t映射该内存块,使两个进程地址空间映射同一个内存块。

  2. key_t是在内核中使用的,用数字来标识唯一的内存块。

  3. key vs shmid:key类比文件inode编号,shmid类比文件的fd。所以在用户层的操作都是用shmid。

ipcs

[admin1@VM-4-17-centos linux_code]$ ipcs------ Message Queues --------
key        msqid      owner      perms      used-bytes   messages    
//消息队列
------ Shared Memory Segments --------
key        shmid      owner      perms      bytes      nattch     status      
//共享内存
------ Semaphore Arrays --------
key        semid      owner      perms      nsems     
//信号量//显示ipc通信系统所支持的三种ipc通信策略
//1.key:表示共享内存的key
//2.shmid:表示共享内存的编号
//3.owner:表示创建共享内存的用户
//4.perms:表示共享内存的使用权限
//5.bytes:表示共享内存的大小
//6.nattch:表示连接到共享内存的进程数
//7.status:表示共享的状态(不显示则为正常使用,显示"dest"表示共享内存已被删除,但任有用户使用)[admin1@VM-4-17-centos linux_code]$ ipcs -m
//查看共享内存
------ Shared Memory Segments --------
key        shmid      owner      perms      bytes      nattch     status //-m 针对共享内存的操作
//-q 针对消息队列的操作
//-s 针对消息队列的操作
//-a 针对所有资源的操作[admin1@VM-4-17-centos linux_code]$ ipcrm -m 196611
//删除shmid为196611的共享内存

shmctl

man shmctl#include <sys/ipc.h>
#include <sys/shm.h>int shmctl(int shmid, int cmd, struct shmid_ds *buf);//删除共享内存

shmat/shmdt

man shmat/shmdt#include <sys/types.h>
#include <sys/shm.h>void *shmat(int shmid, const void *shmaddr, int shmflg);
//关联共享内存int shmdt(const void *shmaddr);
//去关联共享内存

共享内存的特点

  1. 创建共享内存的进程已经退出了,但我们发现共享内存一定还存在!共享内存的生命周期不随进程,随OS。

  2. 一旦共享内存映射到进程的地址空间,该共享内存就直接被所有的进程看到了。可以让进程通信的时候减少拷贝次数,所以共享进程是所有进程通信方式中速度最快的。

在这里插入图片描述

  1. 共享内存没有任何的保护机制(同步互斥)。因为管道通过系统接口通信,共享内存直接通信。

消息队列

msgget/msgctl

man msgget#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>int msgget(key_t key, int msgflg);#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>int msgctl(int msqid, int cmd, struct msqid_ds *buf);

msgsnd/msgrcv

#include <sys/types.h>#include <sys/ipc.h>#include <sys/msg.h>int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);

信号量

互斥等四个概念

我们把大家都能看到的资源称为”公共资源“。

  1. 互斥:任何一个时刻,都只允许一个执行流在进行共享资源的访问——加锁。

  2. 我们把任何一个时刻,都只允许一个执行流在进行访问的共享资源称为”临界资源“。

  3. 临界资源是要通过代码访问的,凡是访问临界资源的代码,叫做”临界区“。

  4. 原子性:要么不做,要么做完,只有两种确定状态的属性。

什么是信号量

任何计技术都有自己的应用场景。

看电影之前,先要买票。

买票的本质功能:1. 对坐位资源的预定机制 2. 确保不会因为多放出去特定的座位资源而导致冲突。

假设一个放映厅只有一个座位→互斥

信号量/信号灯:本质就是一个计数器!描述资源数量的计数器。

任何一个执行流,想访问临界资源的一个子资源的时候,不能直接访问。先申请信号量资源(–count),只要我申请信号量成功,我就一定未来能够拿到一个子资源。如果信号量用完了,即没有成功申请,就会挂起阻塞进程。—— P操作

成功申请后进入自己的临界区,访问对应的临界资源。

释放信号量资源(++count)只要将计数器增加,就表示将我们对应的资源进行了归还。阻塞的进程就可以继续申请并执行了。—— V操作

在这里插入图片描述

进程通过执行代码来申请,每个进程都要遵守这个规则。

所有的进程都得先看到信号量——共享资源——必须保证自己的操作++/–是原子的!

两个进程可以看到同一个count,即让不同的进程看到同一个”计数器“(资源)。所以信号量被归类到了进程间通信!

如果这个计数器是1呢?这就是二元信号量,完成了互斥功能!互斥的本质就是将临界资源独立使用!!!!!

semget/semctl

man semget#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>int semget(key_t key, int nsems, int semflg);#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>int semctl(int semid, int semnum, int cmd, ...);

semop

man semop#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>int semop(int semid, struct sembuf *sops, unsigned nsops);

理解IPC

“多态”:

在这里插入图片描述


总结

进程通信的本质是让不同进程看到同一份”资源“。
如果你真的愿意为自己的梦想去努力,最差的结果,不过是大器晚成。

相关文章:

第七章:进程间通信(IPC)——构成进程间通信的信道方案

系列文章目录 文章目录 系列文章目录前言进程间通信介绍进程间通信目的进程间通信发展进程间通信分类进程通信的原理 管道什么是管道pipe管道通信特点简单设计 命名管道什么是命名管道mkfifostrcmp/strncasecmpunlinkgetch简单设计 共享内存什么是共享内存shmget/ftokipcsshmct…...

部分常用CSS样式

目录 1.字体样式 2.文本样式 3.鼠标样式 cursor 4.背景样式 5.列表样式 6.CSS伪类 7.盒子模型 1.字体样式 font-family 字体类型&#xff1a;隶书” “楷体” font-size 字体大小&#xff1a;像素px font-weight 字体粗细&#xff1a;bold 定义粗体字…...

思科单臂路由、lacp链路聚合、NAT实验

实验拓扑图&#xff1a; 实验目的&#xff1a; 如图所示配置相应IP地址和VLAN&#xff0c;并通过在AR1上配置单臂路由&#xff0c;实现VLAN10和VLAN20的主机能够在VLAN间通信&#xff1b;在SW1和SW2的三条链路实施链路聚合&#xff0c;使用静态LACP模式&#xff0c;使一条链…...

【力扣每日一题】2023.8.5 合并两个有序链表

目录 题目&#xff1a; 示例&#xff1a; 分析&#xff1a; 代码&#xff1a; 题目&#xff1a; 示例&#xff1a; 分析&#xff1a; 题目给我们两个有序的链表&#xff0c;要我们保持升序的状态合并它们。 我们可以马上想要把两个链表都遍历一遍&#xff0c;把所有节点的…...

QT 驱动条码打印机(没有验证过)

这里的打印机是条码打印机&#xff0c;因为第一次接触这种设备&#xff0c;所以买了斑马的GK888t型条码打印机&#xff0c;据说ZPL语言就是斑马的杰作想必支持会好点。实际是&#xff0c;除了ZPL本身外&#xff0c;没有SDK&#xff0c;也没有DDK&#xff0c;所以&#xff0c;一…...

Kafka介绍

目录 1&#xff0c;kafka简单介绍 2&#xff0c;kafka使用场景 3&#xff0c;kafka基本概念 kafka集群 数据冗余 分区的写入 读取分区数据 顺序消费 顺序消费典型的应用场景&#xff1a; 批量消费 提交策略 kafka如何保证高并发 零拷贝技术&#xff08;netty&#…...

Django使用uwsgi+nginx部署,admin没有样式解决办法

Django使用uwsginginx部署,admin没有样式解决办法 如果使用了虚拟环境则修改nginx.conf文件中的/static/路径为你虚拟环境的路径&#xff0c;没有使用虚拟环境则改为你python安装路径下的static server {listen 8008;server_name location; #改为自己的域名&#xff0c;没域名…...

穷举深搜暴搜回溯剪枝(3)

一)字母大小写全排列 784. 字母大小写全排列 - 力扣&#xff08;LeetCode&#xff09; 1)从每一个字符开始进行枚举&#xff0c;如果枚举的是一个数字字符&#xff0c;直接忽视 如果是字母的话&#xff0c;进行选择是变还是不变 2)当进行遍历到叶子结点的时候&#xff0c;直接将…...

Bash 脚本的参数等

bash 的 $值 $0 : 表示当前脚本的名称${BASH_SOURCE[0]} : 表示当前 Bash 脚本文件的路径&#xff0c;可以理解为 $0 的安全版本&#xff0c;防止被修改。$1 : 表示第一个参数&#xff0c;以此类推$ : 表示所有传入脚本的参数$UID : 表示当前用户的 ID 号。如果当前用户是 roo…...

从哪些方面学HTML技术? - 易智编译EaseEditing

学习HTML技术是前端开发的基础&#xff0c;它用于定义网页的结构和内容。以下是学习HTML技术时可以关注的方面&#xff1a; HTML基本语法&#xff1a; 了解HTML标签的基本语法和用法&#xff0c;学习如何创建HTML文档和元素。 常用HTML标签&#xff1a; 学习常用的HTML标签&…...

非阻塞IO

非阻塞IO fcntl 一个文件描述符, 默认都是阻塞IO。fcntl可以将某个文件描述符设置为非阻塞IO&#xff0c;先看一下文档介绍。 传入的cmd的值不同&#xff0c;后面追加的参数也不相同。 fcntl函数有5种功能: 复制一个现有的描述符&#xff08;cmd F_DUPFD&#xff09;。获得…...

Debian如何让multilib和交叉编译工具链共存

Debian一个槽点是gcc/g/gfortran-multilib和交叉编译工具链如gcc/g/gfortran-riscv64-linux-gnu会互相卸载&#xff0c;解决办法如下&#xff1a; 1、安装build-essential&#xff08;gcc/g/libc6-dev/make/dpkg-dev&#xff09;和gfortran&#xff0c;记下被安装的gcc版本&am…...

Flink之JDBC Sink

这里介绍一下Flink Sink中jdbc sink的使用方法,以mysql为例,这里代码分为两种,事务和非事务 非事务代码 import org.apache.flink.connector.jdbc.JdbcConnectionOptions; import org.apache.flink.connector.jdbc.JdbcExecutionOptions; import org.apache.flink.connector.…...

lifecycleScope Unresolved reference

描述 导入了lifecycle.lifecycleScope&#xff0c;但是在activity中使用lifecycleScope报错出现Unresolved reference找不到引用。 导包 import androidx.lifecycle.lifecycleScope使用 lifecycleScope.launch(Dispatchers.IO) {...}错误 方案 代码中的activity继承Activ…...

P5960 【模板】差分约束算法

【模板】差分约束算法 题目描述 给出一组包含 m m m 个不等式&#xff0c;有 n n n 个未知数的形如&#xff1a; { x c 1 − x c 1 ′ ≤ y 1 x c 2 − x c 2 ′ ≤ y 2 ⋯ x c m − x c m ′ ≤ y m \begin{cases} x_{c_1}-x_{c_1}\leq y_1 \\x_{c_2}-x_{c_2} \leq y_2 \\…...

VSCode---通过ctrl+鼠标滚动改变字体大小

打开设置然后在右边输editor.mouseWheelZoo勾选即可实现鼠标滚动改变字体大小 4.这种设置的字体大小是固定的...

视频监控汇聚平台EasyCVR视频分享页面WebRTC流地址播放不了是什么原因?

开源EasyDarwin视频监控TSINGSEE青犀视频平台EasyCVR能在复杂的网络环境中&#xff0c;将分散的各类视频资源进行统一汇聚、整合、集中管理&#xff0c;在视频监控播放上&#xff0c;TSINGSEE青犀视频安防监控汇聚平台可支持1、4、9、16个画面窗口播放&#xff0c;可同时播放多…...

Libevent开源库的介绍与应用

libeventhttps://libevent.org/ 一、初识 1、libevent介绍 Libevent 是一个用C语言编写的、轻量级的开源高性能事件通知库&#xff0c;主要有以下几个亮点&#xff1a;事件驱动&#xff08; event-driven&#xff09;&#xff0c;高性能;轻量级&#xff0c;专注于网络&#xff…...

【LNMP】LNMP

LNMP&#xff1a;是目前成熟的企业网站的应用模式之一&#xff0c;指的是一套协同工作的系统和相关软件&#xff1b;能够提供静态页面服务&#xff0c;也可以提供动态web服务 L Linux系统&#xff0c;操作系统N Nginx网站服务&#xff0c;前端&#xff0c;提供前端的静态…...

uniapp自定义头部导航栏

有时我们需要一些特殊的头部导航栏页面&#xff0c;取消传统的导航栏&#xff0c;来增加页面的美观度。 下面我就教大家如何配置&#xff1a; 一、效果图 二、实现 首先在uniapp中打开pages.json配置文件&#xff0c;在单个路由配置style里面设置导航栏样式​​​​​​nav…...

Lombok 的 @Data 注解失效,未生成 getter/setter 方法引发的HTTP 406 错误

HTTP 状态码 406 (Not Acceptable) 和 500 (Internal Server Error) 是两类完全不同的错误&#xff0c;它们的含义、原因和解决方法都有显著区别。以下是详细对比&#xff1a; 1. HTTP 406 (Not Acceptable) 含义&#xff1a; 客户端请求的内容类型与服务器支持的内容类型不匹…...

YSYX学习记录(八)

C语言&#xff0c;练习0&#xff1a; 先创建一个文件夹&#xff0c;我用的是物理机&#xff1a; 安装build-essential 练习1&#xff1a; 我注释掉了 #include <stdio.h> 出现下面错误 在你的文本编辑器中打开ex1文件&#xff0c;随机修改或删除一部分&#xff0c;之后…...

【SQL学习笔记1】增删改查+多表连接全解析(内附SQL免费在线练习工具)

可以使用Sqliteviz这个网站免费编写sql语句&#xff0c;它能够让用户直接在浏览器内练习SQL的语法&#xff0c;不需要安装任何软件。 链接如下&#xff1a; sqliteviz 注意&#xff1a; 在转写SQL语法时&#xff0c;关键字之间有一个特定的顺序&#xff0c;这个顺序会影响到…...

Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决

Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决 问题背景 在一个基于 Spring Cloud Gateway WebFlux 构建的微服务项目中&#xff0c;新增了一个本地验证码接口 /code&#xff0c;使用函数式路由&#xff08;RouterFunction&#xff09;和 Hutool 的 Circle…...

Springboot社区养老保险系统小程序

一、前言 随着我国经济迅速发展&#xff0c;人们对手机的需求越来越大&#xff0c;各种手机软件也都在被广泛应用&#xff0c;但是对于手机进行数据信息管理&#xff0c;对于手机的各种软件也是备受用户的喜爱&#xff0c;社区养老保险系统小程序被用户普遍使用&#xff0c;为方…...

CVE-2020-17519源码分析与漏洞复现(Flink 任意文件读取)

漏洞概览 漏洞名称&#xff1a;Apache Flink REST API 任意文件读取漏洞CVE编号&#xff1a;CVE-2020-17519CVSS评分&#xff1a;7.5影响版本&#xff1a;Apache Flink 1.11.0、1.11.1、1.11.2修复版本&#xff1a;≥ 1.11.3 或 ≥ 1.12.0漏洞类型&#xff1a;路径遍历&#x…...

FFmpeg:Windows系统小白安装及其使用

一、安装 1.访问官网 Download FFmpeg 2.点击版本目录 3.选择版本点击安装 注意这里选择的是【release buids】&#xff0c;注意左上角标题 例如我安装在目录 F:\FFmpeg 4.解压 5.添加环境变量 把你解压后的bin目录&#xff08;即exe所在文件夹&#xff09;加入系统变量…...

spring Security对RBAC及其ABAC的支持使用

RBAC (基于角色的访问控制) RBAC (Role-Based Access Control) 是 Spring Security 中最常用的权限模型&#xff0c;它将权限分配给角色&#xff0c;再将角色分配给用户。 RBAC 核心实现 1. 数据库设计 users roles permissions ------- ------…...

解析两阶段提交与三阶段提交的核心差异及MySQL实现方案

引言 在分布式系统的事务处理中&#xff0c;如何保障跨节点数据操作的一致性始终是核心挑战。经典的两阶段提交协议&#xff08;2PC&#xff09;通过准备阶段与提交阶段的协调机制&#xff0c;以同步决策模式确保事务原子性。其改进版本三阶段提交协议&#xff08;3PC&#xf…...

云安全与网络安全:核心区别与协同作用解析

在数字化转型的浪潮中&#xff0c;云安全与网络安全作为信息安全的两大支柱&#xff0c;常被混淆但本质不同。本文将从概念、责任分工、技术手段、威胁类型等维度深入解析两者的差异&#xff0c;并探讨它们的协同作用。 一、核心区别 定义与范围 网络安全&#xff1a;聚焦于保…...