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

进程间的通信方式

文章目录

      • 1.简单介绍
      • 2.管道
          • 2.1管道的基础概念
            • **管道读写规则**:
            • **管道特点**
          • 2.2匿名管道
            • 匿名管道父子进程间通信的经典案例:
          • 2.3命名管道
            • 基本概念:
            • 命名管道的创建:
            • 命名管道的打开规则:
            • 匿名管道与普通管道的区别
            • **例子:用命名管道实现server&client通信**
      • 3.system v ipc
          • 3.1system v 共享内存 基础介绍
          • 3.2system v 共享内存常用接口介绍
          • 3.3system v 信息队列 以及信号量
      • 4.一些基础概念的讲解

1.简单介绍

在计算机中,进程间通信(Inter-Process Communication,IPC)是指两个或多个进程之间交换数据或信息的机制。

首先我们通过之前的学习知道进程是具有独立性的,其交互数据成本非常高,所以通信这一机制就诞生了。

通信的本质其实就是由os参与,提供一份所有通信进程能看到的公共资源。

进程间通信的方式非常多以下是对这些方式的梳理以及分类:

进程间通信分类

管道

  • 匿名管道pipe

  • 命名管道

    System V IPC

  • System V 消息队列

  • System V 共享内存

  • System V 信号量

    POSIX IPC

  • 消息队列

  • 共享内存

  • 信号量

  • 互斥量

  • 条件变量

  • 读写锁

2.管道

2.1管道的基础概念

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

我们把从一个进程连接到另一个进程的一个数据流称为一个管道

在这里插入图片描述

管道读写规则:

当没有数据可读时

  • O_NONBLOCK disable:read调用阻塞,即进程暂停执行,一直等到有数据来到为止。
  • O_NONBLOCK enable:read调用返回-1,errno值为EAGAIN。

当管道满的时候

  • O_NONBLOCK disable: write调用阻塞,直到有进程读走数据
  • O_NONBLOCK enable:调用返回-1,errno值为EAGAIN

如果所有管道写端对应的文件描述符被关闭,则read返回0

如果所有管道读端对应的文件描述符被关闭,则write操作会产生信号SIGPIPE,进而可能导致write进程

退出

当要写入的数据量不大于PIPE_BUF时,linux将保证写入的原子性。

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

管道特点
  • 管道提供流式服务

  • 一般而言,进程退出,匿名管道释放,所以匿名管道的生命周期随进程

  • 一般而言,内核会对管道操作进行同步与互斥

  • 管道是半双工的,数据只能向一个方向流动;需要双方通信时,需要建立起两个管道

在这里插入图片描述

2.2匿名管道
#include <unistd.h>
功能:创建一无名管道,管道是一种特殊的文件,它具有两个端点,一个用于写入数据,另一个用于读取数据。
原型
int pipe(int fd[2]);
参数
fd:文件描述符数组,其中fd[0]表示读端, fd[1]表示写端是一个包含两个整数的数组,用于接收管道的两个文件描述符
返回值:成功返回0,失败返回-1
匿名管道父子进程间通信的经典案例:
使用例子:从键盘读取数据,写入管道,读取管道,写到屏幕#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>
#include <stdlib.h>int main()
{int pipe_fd[2] = {0};if(pipe(pipe_fd) < 0){perror("pipe");return 1;}printf("%d, %d\n", pipe_fd[0], pipe_fd[1]);pid_t id = fork();if(id < 0){perror("fork");return 2;}else if(id == 0) { //write//childclose(pipe_fd[0]);//const char *msg = "hello parent, I am child";char c = 'x';int count = 0;//while(count){while(1){write(pipe_fd[1], &c, 1); //strlen(msg) + 1??//   sleep(1);count++;printf("write: %d\n", count);}close(pipe_fd[1]);exit(0);}else{              //read//parentclose(pipe_fd[1]);char buffer[64];while(1){sleep(100);buffer[0] = 0;ssize_t size = read(pipe_fd[0], buffer, sizeof(buffer)-1);if(size > 0){buffer[size] = 0;printf("parent get messge from child# %s\n", buffer);}else if(size == 0){printf("pipe file close, child quit!\n");break;}else{//TODObreak;}}int status = 0;if(waitpid(id, &status,0) > 0){printf("child quit, wait success!, sig: %d\n", status&0x7F);}close(pipe_fd[0]);}return 0;
}

对上端代码用fork来共享管道的原理

在这里插入图片描述

2.3命名管道
基本概念:

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

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

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

命名管道的创建:
//命名管道可以从命令行上创建,命令行方法是使用下面这个命令:
$ mkfifo filename
//命名管道也可以从程序里创建,相关函数有:int mkfifo(const char *filename,mode_t mode);
//filename:一个指向要创建的命名管道的路径和名称的字符串。它是一个以 null 结尾的字符数组。//mode:用于指定创建的命名管道的权限(访问权限和文件类型)。mode 是一个 mode_t 类型的参数,通常使用八进制表示的权限值。可以使用一些预定义的常量(如 S_IRUSR、S_IWUSR、S_IRGRP 等)来设置权限。//创建命名管道
int main(int argc, char *argv[])
{mkfifo("p2", 0644);return 0;
}
命名管道的打开规则:

如果当前打开操作是为读而打开FIFO时

O_NONBLOCK disable:阻塞直到有相应进程为写而打开该FIFO

O_NONBLOCK enable:立刻返回成功

如果当前打开操作是为写而打开FIFO时

O_NONBLOCK disable:阻塞直到有相应进程为读而打开该FIFO

O_NONBLOCK enable:立刻返回失败,错误码为ENXIO

匿名管道与普通管道的区别

匿名管道由pipe函数创建并打开(使用了pipe函数默认该描述符对应的文件打开了)。

命名管道由mkfififo函数创建,打开用open

FIFO(命名管道)与pipe(匿名管道)之间唯一的区别在它们创建与打开的方式不同,一但这些工作完

成之后,它们具有相同的语义。

tips:

通过 mkfifo 函数创建的命名管道在文件系统中持久存在,与创建它的进程无关。

使用 pipe 函数创建的管道是进程特有的,与创建它的进程相关联。当创建管道的进程结束时,管道会被自动关闭和销毁。而mkfifo函数所创建的管道是不会随着进程结束而销毁的,我们需要手动销毁。

//销毁命名管道所使用的函数int unlink(const char *pathname);
pathname 是一个指向要删除的文件的路径和名称的字符串。它是一个以 null 结尾的字符数组。//unlink 函数执行以下操作:1.首先,它会检查指定路径的文件是否存在。如果文件不存在,unlink 函数会返回一个错误码,并不执行任何其他操作。2.如果文件存在,并且调用进程对该文件具有足够的权限,unlink 函数将从文件系统中删除该文件。删除文件会将文件从目录中删除,并释放文件所占用的磁盘空间。3.注意,删除文件并不会关闭已打开的文件描述符。如果有进程仍然持有对该文件的打开描述符,文件将继续存在于文件系统中,直到所有打开的描述符关闭。4.当调用进程成功删除文件时,unlink 函数返回 0。如果出现错误,比如权限不足或指定的文件路径无效,unlink 函数返回 -1,并设置相应的错误码,可以通过 errno 全局变量获取错误信息。//删除命名管道文件并不会自动关闭已打开的管道文件描述符。在删除命名管道文件之前,需要确保所有打开的文件描述符都已关闭,以免导致资源泄漏或其他问题。
例子:用命名管道实现server&client通信

例子中使用的一些函数的解析:

1.mode_t umask(mode_t mask);
umask 函数接受一个参数 mask,它是一个无符号整数类型 mode_t 的值,表示要设置的权限掩码。mode_t 是一个用于表示文件权限和文件类型的数据类型。2.key_t ftok(const char *pathname, int proj_id);用于生成一个唯一的键值(key)用于标识一个共享资源,例如共享内存、信号量或消息队列。pathname:一个存在的文件路径名,可以是任意合法的文件路径。proj_id:一个用户定义的整数,用于区分不同的共享资源。通常取值为一个非负整数。  生成的键值可以用于创建或访问共享资源,例如在调用 shmget 函数创建共享内存时使用。3.int shmdt(const void *shmaddr);      shmdt 函数用于将共享内存从当前进程的地址空间中分离,即解除共享内存的挂载。shmdt 函数并不会删除共享内存段,只是将其与当前进程分离。在调用 shmdt 函数之后,当前进程将无法再直接访问共享内存中的数据。如果需要重新访问共享内存,必须使用 shmat 函数重新将共享内存段挂载到当前进程的地址空间。
Log.hpp(一个简单的日志记录功能的实现)#ifndef _LOG_H_
#define _LOG_H_#include <iostream>
#include <ctime>#define Debug   0
#define Notice  1
#define Warning 2
#define Error   3const std::string msg[] = {"Debug","Notice","Warning","Error"
};std::ostream &Log(std::string message, int level)
{std::cout << " | " << (unsigned)time(nullptr) << " | " << msg[level] << " | " << message;return std::cout;
}#endif   
Makefile.PHONY:all
all:shmClient shmServershmClient:shmClient.ccg++ -o $@ $^ -std=c++11
shmServer:shmServer.ccg++ -o $@ $^ -std=c++11.PHONY:clean
clean:rm -f shmClient shmServer
comm.hpp#pragma once#include <iostream>
#include <cstdio>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <cassert>
#include <cstring>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "Log.hpp"using namespace std; //不推荐#define PATH_NAME "/home/whb"
#define PROJ_ID 0x66
#define SHM_SIZE 4096 //共享内存的大小,最好是页(PAGE: 4096)的整数倍#define FIFO_NAME "./fifo"class Init
{
public:Init(){umask(0);//表示没有遮掩任何权限int n = mkfifo(FIFO_NAME, 0666);assert(n == 0);(void)n;Log("create fifo success",Notice) << "\n";}~Init(){unlink(FIFO_NAME);Log("remove fifo success",Notice) << "\n";}
};#define READ O_RDONLY
#define WRITE O_WRONLYint OpenFIFO(std::string pathname, int flags)
{int fd = open(pathname.c_str(), flags);assert(fd >= 0);return fd;
}void Wait(int fd)
{Log("等待中....", Notice) << "\n";uint32_t temp = 0;//为了确保读取的值可以正确地存储和比较ssize_t s = read(fd, &temp, sizeof(uint32_t));assert(s == sizeof(uint32_t));(void)s;
}void Signal(int fd)
{uint32_t temp = 1;   //为了确保读取的值可以正确地存储和比较ssize_t s = write(fd, &temp, sizeof(uint32_t));assert(s == sizeof(uint32_t));(void)s;Log("唤醒中....", Notice) << "\n";
}void CloseFifo(int fd)
{close(fd);
}
shmClient.cc#include "comm.hpp"int main()
{Log("child pid is : ", Debug) << getpid() << endl;key_t k = ftok(PATH_NAME, PROJ_ID);if (k < 0){Log("create key failed", Error) << " client key : " << k << endl;exit(1);}Log("create key done", Debug) << " client key : " << k << endl;// 获取共享内存int shmid = shmget(k, SHM_SIZE, 0);if(shmid < 0){Log("create shm failed", Error) << " client key : " << k << endl;exit(2);}Log("create shm success", Error) << " client key : " << k << endl;// sleep(10);char *shmaddr = (char *)shmat(shmid, nullptr, 0);if(shmaddr == nullptr){Log("attach shm failed", Error) << " client key : " << k << endl;exit(3);}Log("attach shm success", Error) << " client key : " << k << endl;// sleep(10);int fd = OpenFIFO(FIFO_NAME, WRITE);// 使用// client将共享内存看做一个char 类型的bufferwhile(true){ssize_t s = read(0, shmaddr, SHM_SIZE-1);if(s > 0){shmaddr[s-1] = 0;Signal(fd);if(strcmp(shmaddr,"quit") == 0) break;}}CloseFifo(fd);// char a = 'a';// for(; a <= 'z'; a++)// {//     shmaddr[a-'a'] = a;//     // 我们是每一次都向shmaddr[共享内存的起始地址]写入//     // snprintf(shmaddr, SHM_SIZE - 1,\//     //     "hello server, 我是其他进程,我的pid: %d, inc: %c\n",\//     //     getpid(), a);//     sleep(5);// }// strcpy(shmaddr, "quit");// 去关联int n = shmdt(shmaddr);assert(n != -1);Log("detach shm success", Error) << " client key : " << k << endl;// sleep(10);// client 要不要chmctl删除呢?不需要!!return 0;
}
shmServer.cc#include "comm.hpp"// 是不是对应的程序,在加载的时候,会自动构建全局变量,就要调用该类的构造函数 -- 创建管道文件
// 程序退出的时候,全局变量会被析构,自动调用析构函数,会自动删除管道文件
Init init; string TransToHex(key_t k)
{char buffer[32];snprintf(buffer, sizeof buffer, "0x%x", k);return buffer;
}int main()
{// 我们之前为了通信,所做的所有的工作,属于什么工作呢:让不同的进程看到了同一份资源(内存)// 1. 创建公共的Key值key_t k = ftok(PATH_NAME, PROJ_ID);assert(k != -1);Log("create key done", Debug) << " server key : " << TransToHex(k) << endl;// 2. 创建共享内存 -- 建议要创建一个全新的共享内存 -- 通信的发起者int shmid = shmget(k, SHM_SIZE, IPC_CREAT | IPC_EXCL | 0666); //if (shmid == -1){perror("shmget");exit(1);}Log("create shm done", Debug) << " shmid : " << shmid << endl;// sleep(10);// 3. 将指定的共享内存,挂接到自己的地址空间char *shmaddr = (char *)shmat(shmid, nullptr, 0);Log("attach shm done", Debug) << " shmid : " << shmid << endl;// sleep(10);// 这里就是通信的逻辑了// 将共享内存当成一个大字符串// char buffer[SHM_SIZE];// 结论1: 只要是通信双方使用shm,一方直接向共享内存中写入数据,另一方,就可以立马看到对方写入的数据。//         共享内存是所有进程间通信(IPC),速度最快的!不需要过多的拷贝!!(不需要将数据给操作系统)// 结论2: 共享内存缺乏访问控制!会带来并发问题 【如果我想一定程度的访问控制呢? 能】int fd = OpenFIFO(FIFO_NAME, READ);for(;;){Wait(fd);// 临界区printf("%s\n", shmaddr);if(strcmp(shmaddr, "quit") == 0) break;// sleep(1);}// 4. 将指定的共享内存,从自己的地址空间中去关联int n = shmdt(shmaddr);assert(n != -1);(void)n;Log("detach shm done", Debug) << " shmid : " << shmid << endl;// sleep(10);// 5. 删除共享内存,IPC_RMID即便是有进程和当下的shm挂接,依旧删除共享内存n = shmctl(shmid, IPC_RMID, nullptr);assert(n != -1);(void)n;Log("delete shm done", Debug) << " shmid : " << shmid << endl;CloseFifo(fd);return 0;
}    

3.system v ipc

3.1system v 共享内存 基础介绍

在这里插入图片描述

实际上,system v本质也是创建虚拟内存进而映射到实际内存上,不过跟上面管道不同的是,进程间信息交互的处理不在由内核系统那一套来处理,而是由操作系统直接处理,因此其是最快的ipc模式。

3.2system v 共享内存常用接口介绍
功能:用来创建共享内存
原型int shmget(key_t key, size_t size, int shmflg);
参数key:这个共享内存段名字size:共享内存大小shmflg:由九个权限标志构成,它们的用法和创建文件时使用的mode模式标志是一样的返回值:成功返回一个非负整数,即该共享内存段的标识码;失败返回-1
功能:将共享内存段连接到进程地址空间
原型void *shmat(int shmid, const void *shmaddr, int shmflg);参数shmid: 共享内存标识shmaddr:指定连接的地址shmflg:它的两个可能取值是SHM_RND和SHM_RDONLY
返回值:成功返回一个指针,指向共享内存第一个节;失败返回-1
shmaddr为NULL,核心自动选择一个地址
shmaddr不为NULL且shmflg无SHM_RND标记,则以shmaddr为连接地址。
shmaddr不为NULL且shmflg设置了SHM_RND标记,则连接的地址会自动向下调整为SHMLBA的整数倍。公式:shmaddr - (shmaddr % SHMLBA)
shmflg=SHM_RDONLY,表示连接操作用来只读共享内存
功能:将共享内存段与当前进程脱离
原型int shmdt(const void *shmaddr);
参数shmaddr: 由shmat所返回的指针
返回值:成功返回0;失败返回-1
注意:将共享内存段与当前进程脱离不等于删除共享内存段
功能:用于控制共享内存
原型int shmctl(int shmid, int cmd, struct shmid_ds *buf);参数shmid:由shmget返回的共享内存标识码cmd:将要采取的动作(有三个可取值)buf:指向一个保存着共享内存的模式状态和访问权限的数据结构
返回值:成功返回0;失败返回-1

在这里插入图片描述

3.3system v 信息队列 以及信号量

这一块学有余力的同学再去详细了解,这里便不在一一呈现了。

4.一些基础概念的讲解

为了之后获取更好的阅读文章体验,我将提前把一些经常出现的基础概念在此总结一下,强烈介意第一次打基础的同学阅读。

当多个进程对同一份资源进行来回操作时,因为时序问题,可能会造成数据不一致的问题,为了解决这一问题,我们将定制一套基础方案,下面先对其中涉及到的专有名词的含义进行讲解。

临界资源:多个进程执行流看到的公用的一份资源

临界区:进程访问临界资源的代码

互斥性:为了更好的临界区的保护,可以让各执行流在同一时刻只有一个进程进入临界区

原子性:一件事情要么不做,要么做完,没有中间状态

信号量:信号量是一种用于实现进程间同步和互斥的机制。可以将信号量看作是一个计数器,它的值可以被多个进程或线程修改和读取。信号量的值表示可用的资源数量或某种条件的状态。

主要有两种类型的信号量:

  1. 二进制信号量(Binary Semaphore):也称为互斥信号量,它的值只能为0或1。用于实现互斥访问共享资源,只允许一个进程或线程访问资源。
  2. 计数信号量(Counting Semaphore):它的值可以是任意非负整数。用于控制一定数量的资源,多个进程或线程可以同时访问资源,但受限于信号量的计数值。

相关文章:

进程间的通信方式

文章目录 1.简单介绍2.管道2.1管道的基础概念**管道读写规则**:**管道特点** 2.2匿名管道匿名管道父子进程间通信的经典案例&#xff1a; 2.3命名管道基本概念:命名管道的创建&#xff1a;命名管道的打开规则&#xff1a;匿名管道与普通管道的区别**例子&#xff1a;用命名管道…...

分类预测 | Matlab实现基于MIC-BP-Adaboost最大互信息系数数据特征选择算法结合Adaboost-BP神经网络的数据分类预测

分类预测 | Matlab实现基于MIC-BP-Adaboost最大互信息系数数据特征选择算法结合Adaboost-BP神经网络的数据分类预测 目录 分类预测 | Matlab实现基于MIC-BP-Adaboost最大互信息系数数据特征选择算法结合Adaboost-BP神经网络的数据分类预测效果一览基本介绍研究内容程序设计参考…...

phpcms v9对联广告关闭左侧广告

修改目录“\caches\poster_js”下的文件“53.js”&#xff0c;修改函数“showADContent()” 将代码&#xff1a; str "<div idPCMSAD_"this.PosID"_"i" style"align_b":"x"px;top:"y"px;width:"this.Width&…...

7.2.4 【MySQL】匹配范围值

回头看我们 idx_name_birthday_phone_number 索引的 B 树示意图&#xff0c;所有记录都是按照索引列的值从小到大的顺序排好序的&#xff0c;所以这极大的方便我们查找索引列的值在某个范围内的记录。比方说下边这个查询语句&#xff1a; SELECT * FROM person_info WHERE nam…...

1400*C. No Prime Differences(找规律数学)

解析&#xff1a; 由于 1 不是质数&#xff0c;所以我们令每一行的数都相差 1 对于行间&#xff0c;分为 n、m之中有存在偶数和都为奇数两种情况。 如果n、m存在偶数&#xff0c;假设m为偶数。 如果都为奇数&#xff0c;则&#xff1a; #include<bits/stdc.h> using name…...

Python基础之装饰器

文章目录 1 装饰器1.1 定义1.2 使用示例1.2.1 使用类中实例装饰器1.2.2 使用类方法装饰器1.2.3 使用类中静态装饰器1.2.4 使用类中普通装饰器 1.3 内部装饰器1.3.1 property 2 常用装饰器2.1 timer:测量执行时间2.2 memoize:缓存结果2.3 validate_input:数据验证2.4 log_result…...

IDEA设置Maven 镜像

第一步&#xff1a;右键项目&#xff0c;选择Maven->Create ‘settings.xml’ 已经存在的话是Open ‘settings.xml’&#xff1a; 第二步&#xff1a;在settings.xml文件中增加阿里云镜像地址&#xff0c;代码如下&#xff1a; <?xml version"1.0" encodin…...

项目评定等级L1、L2、L3、L4

软件项目评定等级的数量可以因不同的评定体系和标准而异。一般情况下&#xff0c;项目评定等级通常按照项目的规模、复杂性和风险等因素来划分&#xff0c;可以有多个等级&#xff0c;常见的包括&#xff1a; L1&#xff08;Level 1&#xff09;&#xff1a;通常表示较小规模、…...

一个基于SpringBoot+Vue前后端分离学生宿舍管理系统详细设计实现

博主介绍&#xff1a;✌全网粉丝30W,csdn特邀作者、博客专家、CSDN新星计划导师、Java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专…...

工作相关----《配置bond》

进入到/etc/sysconfig/network-scripts&#xff0c;按照要求配置主备关系 vim ifcfg-bond0&#xff0c;编写主要内容如下&#xff1a; /*mode1 表示主备份策略&#xff0c;miimon100 系统每100毫秒监测一次链路连接状态&#xff0c; 如果有一条线路不通就转入另一条线路*/ BOND…...

Nacos、ZooKeeper和Dubbo的区别

Nacos、ZooKeeper和Dubbo是三个不同的分布式系统组件&#xff0c;它们之间有以下几点区别&#xff1a; 功能定位&#xff1a;Nacos主要提供服务发现、配置管理和服务治理等功能&#xff0c;而ZooKeeper主要是分布式协调服务&#xff0c;提供了分布式锁、分布式队列等原语&#…...

刷一下算法

记录下自己的思路与能理解的解法,可能并不是最优解法,不定期持续更新~ 1.盛最多水的容器 给定一个长度为 n 的整数数组 height 。有 n 条垂线&#xff0c;第 i 条线的两个端点是 (i, 0) 和 (i, height[i]) 。 找出其中的两条线&#xff0c;使得它们与 x 轴共同构成的容器可以容…...

three.js——GUI的使用

GUI的使用 效果图1、导入gui2、创建一个GUI对象3、通过gui调用方法 name:按钮的名称 效果图 1、导入gui // 导入ligui import { GUI } from three/examples/jsm/libs/lil-gui.module.min.js2、创建一个GUI对象 const gui new GUI()3、通过gui调用方法 name:按钮的名称 // 创…...

LeetCode 332. Reconstruct Itinerary【欧拉回路,通路,DFS】困难

本文属于「征服LeetCode」系列文章之一&#xff0c;这一系列正式开始于2021/08/12。由于LeetCode上部分题目有锁&#xff0c;本系列将至少持续到刷完所有无锁题之日为止&#xff1b;由于LeetCode还在不断地创建新题&#xff0c;本系列的终止日期可能是永远。在这一系列刷题文章…...

236. 二叉树的最近公共祖先 Python

文章目录 一、题目描述示例 1示例 2示例 3 二、代码三、解题思路 一、题目描述 给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。 百度百科中最近公共祖先的定义为&#xff1a;“对于有根树 T 的两个节点 p、q&#xff0c;最近公共祖先表示为一个节点 x&#xff0c;满…...

WPF中DataGrid控件绑定数据源

步骤 创建数据源&#xff1a;首先&#xff0c;我们需要创建一个数据源&#xff0c;可以是一个集合&#xff08;如List、ObservableCollection等&#xff09;&#xff0c;也可以是一个DataTable对象。数据源中的每个元素代表一行数据。 设置DataGrid的ItemsSource属性&#xff…...

Linux arm64 set_memory_ro/rw函数

文章目录 一、函数简介1.1 简介1.2 change_memory_common1.3 __change_memory_common 二、apply_to_page_range函数2.1 apply_to_page_range2.2 apply_to_p4d_range2.3 apply_to_pud_range2.4 apply_to_pmd_range2.5 apply_to_pte_range 三、hook系统调用参考资料 一、函数简介…...

安达发|APS排单软件中甘特图的应用

近几年来&#xff0c;企业对生产效率和管理水平的要求越来越高。为了提高生产效率&#xff0c;降低生产成本&#xff0c;许多企业开始引入先进的生产计划与调度系统&#xff08;APS&#xff09;&#xff0c;实现生产过程的自动化、智能化管理。APS排产软件是一种能够根据企业的…...

快速上手Linux基础开发工具

目录 软件包管理器 概念理解 用法示例 - 以yum为例 vim 模式的切换 常用操作 插件和配置 gcc/g gdb make / makefile 软件包管理器 概念理解 在Linux下安装软件的话&#xff0c;一个比较原始的办法是下载程序的源代码&#xff0c;然后进行编译&#xff0c;进而得到…...

【开发工具】idea 的全局搜索快捷键(Ctrl+shift+F)失效

文章目录 前言1. 取消 输入法的快捷键&#xff08;推荐使用&#xff09;2.更改 idea的快捷键3. 热键占用总结 前言 当你发现在idea 中看到用于全局搜索的快捷键就是 CtrlshiftF&#xff0c;可是怎么按都不管用的时候&#xff0c;你就不要再执着于自己的操作继续狂点电脑按键了…...

港联证券:“火箭蛋”来袭 蛋价涨势能否延续?

上个交易周&#xff08;9月11日至15日&#xff09;&#xff0c;鸡蛋期货商场呈现了意想不到的涨势。9月15日&#xff0c;鸡蛋期货多个合约大涨&#xff0c;其中2310合约涨超5.6%&#xff0c;主力合约2311盘中两度触及涨停&#xff0c;最终收涨6%。业内人士以为&#xff0c;鸡蛋…...

Vue3_vite

使用Vue-cli创建 使用vite创建 Composition API 组合API setup 1.Vue3中的一个新的配置项,值为一个函数 2.可以将组件中所用到的数据,方法等配置在setup中. 3.setup函数的两种返回值 3.1若返回一个对象,则对象中的属性,方法,在模板中均可以直接使用. 3.2若返回一个渲染函数…...

python-字符串去掉空格的常见方法

python提供了去掉字符串空格的方法&#xff0c;可以满足大部分需求。 但在实际应用中&#xff0c;还需要灵活借助python其他方法&#xff0c;来实现字符串空格的删除。 比如&#xff0c;去掉字符串的全部空格、字符串连续空格保留一个等&#xff0c;都需要结合其他的方法来实现…...

如何写出一个成熟的线上线下结合的营销方案?

分享一下咱们案例库里策划的一个线上线下结合的活动的案例。 这个活动是为了推广一个新品牌&#xff0c;增加品牌知名度和用户粘性。 你可以根据以下几个要点来进行活动策划&#xff1a; 1、目标&#xff1a; 让目标用户了解并喜欢新品牌&#xff0c;激发用户参与和分享&am…...

Vc - Qt - “扩张“的窗口

该示例演示了一个"扩张的窗口"&#xff0c;主窗口的布局为水平布局&#xff0c;内置两个子窗口&#xff0c;采用定时器设置左边窗口的宽度&#xff0c;达到控制"扩张"的目的。 #include <QApplication> #include <QWidget> #include <QHBox…...

vue学习-02vue入门之组件

删除Vue-cli预设 在用户根目录下(C:\Users\你的用户名)这个地址里有一个.vuerc 文件,修改或删除配置 组件 Props(组件之间的数据传递) Prop 的大小写 (camelCase vs kebab-case)不敏感Prop 类型: String Number Boolean Array Object Date Function Symbol传递静态或动态 Pr…...

解决Pycharm使用Conda激活环境失败的问题

Q:公司电脑终端使用powershell来激活conda环境时报错? 同时手动打开powershell报"profile.ps1” 无法被加载的错误 A: 1,手动打开powershell&#xff0c;设置管理员打开 2,打开powershell 打开 PowerShell 终端&#xff0c;并输入以下命令&#xff1a;Get-ExecutionPo…...

SpringSecurity 核心组件

文章目录 SpringSecurity 结构组件&#xff1a;SecurityContextHolder组件&#xff1a;Authentication组件&#xff1a;UserDetailsService组件&#xff1a;GrantedAuthority组件总结 SpringSecurity 结构 在SpringSecurity中的jar分为4个&#xff0c;作用分别为 jar作用spri…...

【Vue】快速入门和生命周期

目录 前言 一、vue的介绍 1. Vue.js是什么&#xff1f; 2. 库和框架的区别 3.基本概念和用法&#xff1a; 二、MVVM的介绍 1. 什么是MVVM&#xff1f; 2. MVVM的组成部分 3. MVVM的工作流程 4. MVVM的优势 5. MVVM的应用场景 三、vue实例 1.模板语法&#xff1a; …...

JVM架构和内存管理优化

Java虚拟机&#xff08;JVM&#xff09;是Java编程语言的核心组件&#xff0c;负责执行Java字节码并提供运行时环境&#xff0c;使得Java程序可以在不同的平台上运行。了解JVM的工作原理和内存管理对于优化代码性能和理解Java的内存管理和垃圾收集机制非常重要。在本文中&#…...

wordpress 营销页面/怎样做竞价推广

福建师范大学网络与继续教育学院《计算机应用基础》期末考试A 卷姓名&#xff1a;专业&#xff1a;学号&#xff1a;学习中心&#xff1a;成绩&#xff1a;一、单项选择题(答案务必写在表格中&#xff0c;每小题1 分&#xff0c;共 30 分)12345678910CCCCBBCCAC111213141516171…...

公司可以做多个网站吗/网站推广优化外链

凡有位运算的全加括号&#xff01;&#xff01;&#xff01;&#xff01;&#xff01; 二进制嘛&#xff0c;可以加速 a*2a<<1 a/2a>>1 2^b1<< b 可以判断一些东西 a&1->(a%21) a^1可以求出数的一个相邻数 等等等等 下面我会在做题目时记录一…...

网站测试报告/网站推广网络营销方案

font awesome 页面小图标 前段时间做页面&#xff0c;从网上查找资料&#xff0c;发现了一个好用的工具&#xff0c;就是font awesome奥森图标&#xff0c;使用了一下&#xff0c;发现非常方便&#xff0c;而且很灵活&#xff0c;纯css编写&#xff0c;可以和bootstrap结合使用…...

wordpress建站原理/企业查询

有时我们已经得到充分的分层树形网格的数据&#xff0c;我们还想让树形网格按层次延迟加载节点。首先&#xff0c;只加载顶层节点&#xff1b;然后点击节点的展开图标来加载它的子节点。本教程展示如何创建带有延迟加载特性的树形网格。 jQuery EasyUI最新试用版下载请猛戳>…...

加强网站建设管理 及时更新/想要导航页面推广app

首先&#xff0c;我们来熟悉一下基本知识&#xff0c;再回顾前几篇文章&#xff01;1:阻塞/非阻塞–这两个概念是针对 IO 过程中进程的状态来说的阻塞 IO 是指调用结果返回之前&#xff0c;当前线程会被挂起非阻塞指在不能立刻得到结果之前&#xff0c;该函数不会阻塞当前线程&…...

本地计算机做网站服务器/郑州seo多少钱

这是参考大佬分享的代码写的有问题请提出指正&#xff0c;谢谢。using Serilog; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks;namespace TaskManager {class TaskFactoryMa…...