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

【Linux】进程间通信IPC

目录

进程间通信 IPC

1. 进程间通信方式

2. 无名管道

2.1 特点

2.2 函数接口

2.3 注意事项

3. 有名管道

3.1 特点

3.2 函数接口

3.3 注意事项

3.4 有名管道和无名管道的区别

4. 信号

4.1概念

4.2信号的响应方式

4.3 信号种类

4.4 函数接口

4.4.1 信号发送和挂起

4.4.2 定时器

4.4.3 信号处理函数signal()

5. 共享内存

5.1特点

5.2步骤

5.3函数接口

5.4命令

6. 信号灯集

6.1 特点

6.2步骤

6.3 命令

6.4 函数接口

创建和使用信号灯:

函数操作:

把信号灯集加到共享内存:

7.消息队列:messagequeue

7.1特点

7.2步骤

7.3命令

7.4函数接口


进程间通信 IPC

InterProcess Communication

1. 进程间通信方式

1)早期的进程间通信:

无名管道(pipe)、有名管道(fifo)、信号(signal)

2)systerm V IPC:

共享内存(share memory)、消息队列(message queue)、信号灯集(semaphore set)

3)BSD:

套接字(socket)

2. 无名管道

2.1 特点

(1) 只能用于具有亲缘关系的进程之间的通信

(2) 半双工的通信模式,具有固定的读端fd[0]和写端fd[1]。

(3) 管道可以看成是一种特殊的文件,对于它的读写可以使用文件IO如read、write函数。

(4) 管道是基于文件描述符的通信方式。当一个管道建立时,它会创建两个文件描述符 fd[0]和fd[1]。其中fd[0]固定用于读管道,而fd[1]固定用于写管道。

2.2 函数接口

int pipe(int fd[2])功能:创建无名管道
参数:文件描述符 fd[0]:读端  fd[1]:写端
返回值:成功 0失败 -1
#include <stdio.h>
#include <unistd.h>int main(int argc, char const *argv[])
{char buf[65536] = "";int fd[2] = {0}; //fd[0]代表读端,fd[1]代表写端if (pipe(fd) < 0){perror("pipe err");return -1;}printf("%d %d\n", fd[0], fd[1]);//读写// write(fd[1], "hello", 32);// read(fd[0], buf, 32);// printf("%s\n", buf);//结构类似队列,先进先出//1. 当管道中没有数据时,读阻塞。// read(fd[0], buf, 32);// printf("%s\n", buf);//但是关闭写端就不一样//当管道中有数据,关闭写端可以读出数据来。无数据,关闭写端,读操作会立即返回(返回0)。//write(fd[1],"hello",5);// close(fd[1]);// read(fd[0],buf,5);// printf("%s\n",buf);//2.当管道这个写满数据时,写阻塞,管道空间大小是64K// write(fd[1], buf, 65536);// printf("write full\n");// write(fd[1],"a",1);         //阻塞,因为管道中被写满了已经。// printf("write after\n");//3.写满一次后,当管道中至少有4K空间时(也就是读了4K),才可以继续写,否则阻塞。//先写满一次再读// read(fd[0], buf, 4096); //如果读4095后面写就阻塞了,因为不到4K空间。// write(fd[1], "k", 1);// printf("write after\n");//4.当读端关闭,向管道中写入数据无意义,管道破裂,进程会收到内核发送的SIGNAL信号close(fd[0]);write(fd[1], "hello", 5);printf("read close!\n");return 0;
}

2.3 注意事项

(1) 当管道中无数据的时候,读操作会阻塞。

管道当中有数据,关闭写段,可以将数据读出。

管道中无数据,关闭写段,读操作会立即返回。

(2) 管道中写满时(管道大小64K)写操作会阻塞,写满一次时一旦有4K空间可以继续写。

(3) 只有管道的读端存在时,向管道中写数据才有意义,否则会导致管道破裂,向管道中写入数据进程将收到内核传来的SIGPIPE信号(通常时Broken pipe错误)。

练习:父子进程实现通信,父进程循环从终端输入数据,子进程循环打印数据,当输入quit结束。

提示:不需要加同步机制, 因为pipe无数据时读会阻塞。

考虑:创建管道是在fork之前还是之后?

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>int main(int argc, char const *argv[])
{pid_t pid;char buf[32] = "";int fd[2] = {0};if (pipe(fd) < 0){perror("pipe err");return -1;}printf("%d %d\n", fd[0], fd[1]);pid = fork();if (pid < 0){perror("fokr err");return -1;}else if (pid == 0){//循环打印while (1){//read读管道中内容read(fd[0], buf, 32);//判断quit就breakif (strcmp(buf, "quit") == 0)break;//printf打印到终端printf("%s\n", buf);}}else{//循环输入while (1){//先scanfscanf("%s", buf);//将写入的buf中内容用write写进管道write(fd[1], buf, 32);//write(fd[1], buf, strlen(buf)+1);//+1是为了把\0也写进管道//判断quit就breakif (strcmp(buf, "quit") == 0)break;}}//回收子进程wait(NULL);return 0;
}

3. 有名管道

3.1 特点

1) 有名管道可以使互不相关的两个进程互相通信。

2) 有名管道可以通过路径名来指出,并且在文件系统中可见,但内容存放在内存中。但是读写数据不会存在文件中,而是在管道中。

3) 进程通过文件IO来操作有名管道

4) 有名管道遵循先进先出规则

5) 不支持如lseek() 操作

3.2 函数接口

int mkfifo(const char *filename,mode_t mode);
功能:创健有名管道
参数:filename:有名管道文件名mode:权限
返回值:成功:0失败:-1,并设置errno号注意对错误的处理方式:
如果错误是file exist时,注意加判断,如:if(errno == EEXIST)

注:函数只是在路径下创建管道文件,往管道中写的数据依然写在内核空间。

先创建有名管道,然后用文件IO操作:打开、读写和关闭。

3.3 注意事项

1) 只写方式打开阻塞,一直到另一个进程把读打开

2) 只读方式打开阻塞,一直到另一个进程把写打开

3) 可读可写,如果管道中没有数据,读阻塞

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>int main(int argc, char const *argv[])
{int fd;char buf[32] = "";if (mkfifo("./fifo", 0666) < 0){if (errno == EEXIST)printf("file exist\n");else{perror("mkfifo err");return -1;}}printf("mkfifo success\n");//打开管道文件fd = open("./fifo", O_RDWR);if (fd < 0){perror("open err");return -1;}//读写文件write(fd, "hello", 5);read(fd, buf, 5);printf("%s\n", buf);return 0;
}

练习:通过两个进程实现cp功能。

./input srcfile

./output destfile

input.c 读源文件

//创建有名管道

//打开管道文件,打开源文件

//循环读源文件,再把内容写道管道

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>int main(int argc, char const *argv[])
{int fd_fifo, fd_file;char buf[32] = "";ssize_t s;if (mkfifo("./fifo", 0666) < 0){if (errno == EEXIST)printf("file exist\n");else{perror("mkfifo err");return -1;}}printf("mkfifo success\n");//打开管道文件fd_fifo = open("./fifo", O_WRONLY);if (fd_fifo < 0){perror("open fifo err");return -1;}fd_file = open(argv[1], O_RDONLY);if (fd_file < 0){perror("open file err");return -1;}//读写while (1){//从文件读到buf,判断读不到就结束s=read(fd_file,buf,32);if(s==0)break;//把buf中数据写到有名管道中write(fd_fifo,buf,s);}close(fd_fifo);close(fd_file);return 0;
}

output.c 写目标文件

//创建有名管道

//打开有名管道,打开目标文件

//循环读管道,把内容写到目标文件

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>int main(int argc, char const *argv[])
{int fd_fifo, fd_file;char buf[32] = "";ssize_t s;if (mkfifo("./fifo", 0666) < 0){if (errno == EEXIST)printf("file exist\n");else{perror("mkfifo err");return -1;}}printf("mkfifo success\n");//打开管道文件fd_fifo = open("./fifo", O_RDONLY);if (fd_fifo < 0){perror("open fifo err");return -1;}fd_file = open(argv[1], O_WRONLY | O_CREAT | O_TRUNC, 0666);if (fd_file < 0){perror("open file err");return -1;}//读写while (1){//从有名管道中读数据到buf,判断s = read(fd_fifo, buf, 32);if (s == 0)break;//把buf中数据写到目标文件write(fd_file, buf, s);}close(fd_fifo);close(fd_file);return 0;
}

3.4 有名管道和无名管道的区别

无名管道

有名管道

使用场景

有亲缘关系的进程使用

不相干的两个进程使用

特点

半双工通讯方式

固定读端fd[0]和写端fd[1]

看做一种特殊文件,可以通过文件IO操作

在文件系统中会存在管道文件,数据存放在内核空间中

通过文件IO进行操作

不支持lseek操作,也遵循先进先出

函数

pipe()

直接read、write

mkfifo()

先打开open,再对管道文件read、write读写

读写特性

当管道中无数据,读阻塞

当管道中写满,写阻塞,直到有4K空间才可以写

读端关闭,写会导致管道破裂

只读方式打开会阻塞,直到另一个进程把写打开

只写方式打开会阻塞,直到另一个进程把读打开

可读可写,如果管道中没有数据,读阻塞。

4. 信号

kill -l: 显示系统中的信号

kill -num PID:给某个进程发送信号

4.1概念

1)信号是在软件层次上对中断机制的一种模拟,是一种异步通信方式

2)信号可以直接进行用户空间进程和内核进程之间的交互,内核进程也可以利用它来通知用户空间进程发生了哪些系统事件。

3)如果该进程当前并未处于执行态,则该信号就由内核保存起来,直到该进程恢复执行再传递给它;如果一个信号被进程设置为阻塞,则该信号的传递被延迟,直到其阻塞被取消时才被传递给进程。

信号的生命周期:

4.2信号的响应方式

1)忽略信号:对信号不做任何处理,但是有两个信号不能忽略:即SIGKILL及SIGSTOP。

2)捕捉信号:定义信号处理函数,当信号发生时,执行相应的处理函数。

3)执行缺省操作:Linux对每种信号都规定了默认操作

4.3 信号种类

SIGINT(2):中断信号,Ctrl-C 产生,用于中断进程

SIGQUIT(3):退出信号, Ctrl-\ 产生,用于退出进程并生成核心转储文件

SIGKILL(9):终止信号,用于强制终止进程。此信号不能被捕获或忽略。

SIGALRM(14):闹钟信号,当由 alarm() 函数设置的定时器超时时产生。

SIGTERM(15):终止信号,用于请求终止进程。此信号可以被捕获或忽略。termination

SIGCHLD(17):子进程状态改变信号,当子进程停止或终止时产生。

SIGCONT(18):继续执行信号,用于恢复先前停止的进程。

SIGSTOP(19):停止执行信号,用于强制停止进程。此信号不能被捕获或忽略。

SIGTSTP(20):键盘停止信号,通常由用户按下 Ctrl-Z 产生,用于请求停止进程。

4.4 函数接口

4.4.1 信号发送和挂起

#include <signal.h>
int kill(pid_t pid, int sig);
功能:信号发送
参数:pid:指定进程sig:要发送的信号
返回值:成功 0     失败 -1int raise(int sig);
功能:进程向自己发送信号
参数:sig:信号
返回值:成功 0   失败 -1相当于:kill(getpid(),sig);int pause(void);
功能:用于将调用进程挂起,直到收到信号为止。
#include <sys/types.h>
#include <signal.h>
#include <stdio.h>
#include <unistd.h>int main(int argc, char const *argv[])
{// //kill(getpid(), SIGKILL); //给指定进程发送信号,此例子指定当前进程// raise(SIGKILL);            //给自己发信号// while (1)//     ;pause(); //将进程挂起,直到收到信号为止,作用类似死循环但是不占用CPU。printf("hello\n");return 0;
}

4.4.2 定时器

unsigned int alarm(unsigned int seconds)功能:在进程中设置一个定时器。当定时器指定的时间到了时,它就向进程发送SIGALARM信号。
参数:seconds:定时时间,单位为秒
返回值:如果调用此alarm()前,进程中已经设置了闹钟时间,则返回上一个闹钟时间的剩余时间,否则返回0。
注意:一个进程只能有一个闹钟时间。如果在调用alarm时已设置过闹钟时间,则之前的闹钟时间被新值所代替。
常用操作:取消定时器alarm(0),返回旧闹钟余下秒数。
#include <sys/types.h>
#include <signal.h>
#include <stdio.h>
#include <unistd.h>int main(int argc, char const *argv[])
{printf("%d\n", alarm(10));sleep(1);printf("%d\n", alarm(3));pause(); //将进程挂起,直到收到信号为止,作用类似死循环但是不占用CPU。return 0;
}

系统默认对SIGALRM(闹钟到点后内核发送的信号)信号的响应: 如果不对SIGALRM信号进行捕捉或采取措施,默认情况下,闹钟响铃时刻会退出进程。例如:

#include <sys/types.h>
#include <signal.h>
#include <stdio.h>
#include <unistd.h>int main(int argc, char const *argv[])
{printf("%d\n", alarm(3));while(1);  //闹钟响铃后结束进程return 0;
}

4.4.3 信号处理函数signal()

typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
功能:信号处理函数
参数:signum:要处理的信号handler:信号处理方式SIG_IGN:忽略信号  (忽略 ignore)SIG_DFL:执行默认操作 (默认 default)handler:捕捉信号 (handler为函数名,可以自定义)void handler(int sig){} //函数名可以自定义, 参数为要处理的信号返回值:成功:设置之前的信号处理方式失败:-1

补充:typedef给数据类型重命名

#include <stdio.h>//给普通数据类型int重命名
typedef int size4;        //给指针类型int* 重命名
typedef int *int_p;       //给数组类型int [10]重命名
typedef int intArr10[10]; //给函数指针void (*)()重命名
typedef void (*fun_p)(); void fun()
{printf("fun\n");
}int main(int argc, char const *argv[])
{size4 a = 10;             //相当于int aint_p p = &a;             //相当于int* pintArr10 arr = {1, 2, 3}; //相当于int arr[10]fun_p fp = fun;           //相当于void (*pf)();printf("%d\n", *p);printf("%d\n", arr[0]);fp();return 0;
}

例子:

#include <signal.h>
#include <stdio.h>
#include <unistd.h>void handler(int sig)
{printf("ctrl+C\n");
}int main(int argc, char const *argv[])
{//signal(SIGINT,SIG_IGN);   //忽略信号//signal(SIGINT,SIG_DFL);   //按默认方式处理信号signal(SIGINT,handler);     //捕捉信号,比较常用的方式while(1); //为了不让进程结束return 0;
}

练习:

用信号的知识实现司机和售票员问题。

1)售票员捕捉SIGINT(代表开车)信号,向司机发送SIGUSR1信号,司机打印(let's gogogo)

2)售票员捕捉SIGQUIT(代表停车)信号,向司机发送SIGUSR2信号,司机打印(stop the bus)

3)司机捕捉SIGTSTP(代表到达终点站)信号,向售票员发送SIGUSR1信号,售票员打印(please get off the bus)

4)司机等待售票员下车,之后司机再下车。

分析:司机(父进程)、售票员(子进程)

售票员:捕捉:SIGINT SIGQUIT SIGUSR1

忽略:SIGTSTP

司机:捕捉:SIGUSR1 SIGUSR2 SIGTSTP

忽略:SIGINT SIGQUIT

#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>pid_t pid;void saler(int sig)
{if (sig == SIGINT)kill(getppid(),SIGUSR1);else if(sig == SIGQUIT)kill(getppid(),SIGUSR2);else if(sig == SIGUSR1){printf("pls get off the bus!\n");exit(0);}
}void driver(int sig)
{if(sig == SIGUSR1)printf("[driver] let's gogogo!\n");else if(sig == SIGUSR2)printf("stop the bus!\n");else if(sig == SIGTSTP){kill(pid,SIGUSR1);wait(NULL);         //等售票员下车以后再下车exit(0);        }
}int main(int argc, char const *argv[])
{if ((pid = fork()) < 0){perror("fokr err");return -1;}else if (pid == 0){printf("hi,i am saler %d\n", getpid());signal(SIGINT,saler);signal(SIGQUIT,saler);signal(SIGUSR1,saler);signal(SIGTSTP,SIG_IGN);}else{printf("hi, i am driver %d\n", getpid());signal(SIGUSR1,driver);signal(SIGUSR2,driver);signal(SIGTSTP,driver);signal(SIGINT,SIG_IGN);signal(SIGQUIT,SIG_IGN);}while(1)pause(); //不能只发送一个信号进程就结束了,所以可以循环挂起,不占用CPU。return 0;
}

5. 共享内存

5.1特点

1)共享内存是一种最为高效的进程间通信方式,进程可以直接读写内存,而不需要任何数据的拷贝。

2)为了在多个进程间交换信息,内核专门留出了一块内存区,可以由需要访问的进程将其映射到自己的私有地址空间。

3)进程就可以直接读写这一内存区而不需要进行数据的拷贝,从而大大提高的效率。

4)由于多个进程共享一段内存,因此也需要依靠某种同步机制,如互斥锁和信号量等

5.2步骤

a.创建key值

b.创建或者打开共享内存

c.映射共享内存到用户空间

d.撤销映射

e.删除共享内存

5.3函数接口

key_t ftok(const char *pathname, int proj_id);
功能:创建出来的具有唯一映射关系的一个key值,帮助操作系统用来标识一块共享内存
参数:Pathname:已经存在的可访问文件的名字Proj_id:一个字符(因为只用低8位)
返回值:成功:key值失败:-1int shmget(key_t key, size_t size, int shmflg);
功能:创建或打开共享内存
参数:key  键值size   共享内存的大小shmflg   IPC_CREAT|IPC_EXCL|0777注意:shmflg为IPC_CREAT|IPC_EXCL|0777这种形式代表创建共享内存,如果只有权限代表打开共享内存返回值:成功  shmid出错    -1void  *shmat(int  shmid,const  void  *shmaddr,int  shmflg); //attaches
功能:映射共享内存,即把指定的共享内存映射到进程的地址空间用于访问
参数:shmid   共享内存的id号shmaddr   一般为NULL,表示由系统自动完成映射如果不为NULL,那么有用户指定shmflg:SHM_RDONLY就是对该共享内存只进行读操作0     可读可写
返回值:成功:完成映射后的地址,出错:-1(地址)
用法:if((p = (char *)shmat(shmid,NULL,0)) == (char *)-1)int shmdt(const void *shmaddr); //detaches
功能:取消映射
参数:要取消的地址
返回值:成功0  失败的-1int  shmctl(int  shmid,int  cmd,struct shmid_ds *buf); //control
功能:(删除共享内存),对共享内存进行各种操作
参数:shmid   共享内存的id号cmd     IPC_STAT 获得shmid属性信息,存放在第三参数IPC_SET 设置shmid属性信息,要设置的属性放在第三参数IPC_RMID:删除共享内存,此时第三个参数为NULL即可buf    shmid所指向的共享内存的地址,空间被释放以后地址就赋值为null
返回:成功0 失败-1用法:shmctl(shmid,IPC_RMID,NULL);
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <errno.h>
#include <string.h>int main(int argc, char const *argv[])
{key_t key;int shmid;char *p;//创建key值,key值由指定文件的inode号和字符的ascii码组合而成key = ftok("./shm.c", '6');if (key < 0){perror("ftok err");return -1;}printf("key: %#x\n", key);//打开或创建共享内存shmid = shmget(key, 128, IPC_CREAT | IPC_EXCL | 0666); //创建共享内存if (shmid <= 0){if (errno == EEXIST)shmid = shmget(key, 128, 0666); //直接打开已经存在的共享内存else{perror("shmget err");return -1;}}printf("shmid: %d\n", shmid);//映射共享内存p = (char *)shmat(shmid, NULL, 0);if (p == (char *)-1){perror("shmat err");return -1;}//操作共享内存strcpy(p, "hello");printf("%s\n", p);//取消映射shmdt(p);// //删除共享内存// shmctl(shmid,IPC_RMID,NULL);return 0;
}

5.4命令

ipcs-m:查看系统中的共享内存

ipcrm-mshmid:删除指定共享内存

注意:可能不能删除还存在进程使用的共享内存,这时候可以用kill杀死多余进程,再使用ipcs查看。

练习:两个进程实现通信,一个进程循环从终端输入,另一个进程循环打印,当输入quit时结束。

提示:为了共享标志位可以和buf封装到一个结构体里作为共享内存。

struct msg

{

int flag;

char buf[32];

};

输入进程:

#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <errno.h>
#include <string.h>typedef struct msg
{int flag;char buf[32];
} msg_t;int main(int argc, char const *argv[])
{key_t key;int shmid;msg_t *p;//创建key值,key值由指定文件的inode号和字符的ascii码组合而成key = ftok("./shm.c", '6');if (key < 0){perror("ftok err");return -1;}printf("key: %#x\n", key);//打开或创建共享内存shmid = shmget(key, 128, IPC_CREAT | IPC_EXCL | 0666); //创建共享内存if (shmid <= 0){if (errno == EEXIST)shmid = shmget(key, 128, 0666); //直接打开已经存在的共享内存else{perror("shmget err");return -1;}}printf("shmid: %d\n", shmid);//映射共享内存p = (msg_t *)shmat(shmid, NULL, 0);if (p == (msg_t *)-1){perror("shmat err");return -1;}p->flag = 0; //初始化//操作共享内存while (1){scanf("%s", p->buf);p->flag = 1;if (strcmp(p->buf, "quit") == 0)break;}//取消映射shmdt(p);// //删除共享内存// shmctl(shmid,IPC_RMID,NULL);return 0;
}

输出进程:

#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <errno.h>
#include <string.h>typedef struct msg
{int flag;char buf[32];
} msg_t;int main(int argc, char const *argv[])
{key_t key;int shmid;msg_t *p;//创建key值,key值由指定文件的inode号和字符的ascii码组合而成key = ftok("./shm.c", '6');if (key < 0){perror("ftok err");return -1;}printf("key: %#x\n", key);//打开或创建共享内存shmid = shmget(key, 128, IPC_CREAT | IPC_EXCL | 0666); //创建共享内存if (shmid <= 0){if (errno == EEXIST)shmid = shmget(key, 128, 0666); //直接打开已经存在的共享内存else{perror("shmget err");return -1;}}printf("shmid: %d\n", shmid);//映射共享内存p = (msg_t *)shmat(shmid, NULL, 0);if (p == (msg_t *)-1){perror("shmat err");return -1;}//操作共享内存p->flag = 0;while (1){if (strcmp(p->buf, "quit") == 0)break;if (p->flag == 1){printf("%s\n", p->buf);p->flag = 0;}}//取消映射shmdt(p);// //删除共享内存// shmctl(shmid,IPC_RMID,NULL);return 0;
}

6. 信号灯

线程:全局变量,或者通过信号量来实现同步

初始化:sem_init(&sem,0,0);

申请资源:sem_wait(&sem);p操作-1

释放资源:sem_post(&sem);v操作+1

6.1 特点

信号灯(semaphore),也叫信号量,信号灯集是一个信号灯的集合。它是不同进程间或一个给定进程内部不同线程间同步的机制;

而Posix信号灯指的是单个计数信号灯:无名信号灯、有名信号灯。(咱们学的是无名信号灯)

System V的信号灯是一个或者多个信号灯的一个集合。其中的每一个都是单独的计数信号灯。

通过信号灯集实现共享内存的同步操作。

6.2步骤

(1) 创建或者打开信号灯集:semget

(2) 初始化信号灯:semctl

(3) PV操作:semop

(4) 删除信号灯集:semctl

6.3 命令

ipcs -s查看系统中信号灯集

ipcrm -s semid:删除信号灯集

注意:有时候可能会创建失败,或者semid为0,所以创建完可以用命令看看,如果为0删除了重新创建就可以了。

6.4 函数接口

int semget(key_t key, int nsems, int semflg);
功能:创建/打开信号灯
参数:key:ftok产生的key值nsems:信号灯集中包含的信号灯数目semflg:信号灯集的访问权限,通常为IPC_CREAT|IPC_EXCL|0666返回值:成功:信号灯集ID失败:-1int semctl ( int semid, int semnum,  int cmd…/*union semun arg*/);
功能:信号灯集合的控制(初始化/删除)
参数:semid:信号灯集IDsemnum: 要操作的集合中的信号灯编号,信号灯编号从0开始cmd: GETVAL:获取信号灯的值,返回值是获得值SETVAL:设置信号灯的值,需要用到第四个参数:共用体IPC_RMID:从系统中删除信号灯集合
返回值:成功 0失败 -1用法:
1. 初始化信号灯集:
需要自定义共用体
union semun{int val;
} mysemun;
mysemun.val = 10;
semctl(semid, 0, SETVAL, mysemun);
2. 获取信号灯值:函数semctl(semid, 0, GETVAL)的返回值
3. 删除信号灯集:semctl(semid, 0, IPC_RMID);int semop ( int semid, struct sembuf  *opsptr,  size_t  nops);
功能:对信号灯集合中的信号量进行PV操作
参数:semid:信号灯集IDopsptr:操作方式nops:  要操作的信号灯的个数 1个
返回值:成功 :0失败:-1struct sembuf {short  sem_num; // 要操作的信号灯的编号short  sem_op;  //    0 :  等待,直到信号灯的值变成0//   1  :  释放资源,V操作//   -1 :  申请资源,P操作                    short  sem_flg; // 0(阻塞),IPC_NOWAIT, SEM_UNDO};
注意:直接用这个结构体定义变量就可以了,结构体不需要自己写。用法:
申请资源 P操作:mysembuf.sem_num = 0;
mysembuf.sem_op = -1;
mysembuf.sem_flg = 0;
semop(semid, &mysembuf, 1);
释放资源 V操作:mysembuf.sem_num = 0;
mysembuf.sem_op = 1;
mysembuf.sem_flg = 0;
semop(semid, &mysembuf, 1);

创建使用信号灯

#include <sys/types.h>
#include <sys/ipc.h>
#include <stdio.h>
#include <sys/sem.h>
#include <errno.h>union semun
{int val;
};int main(int argc, char const *argv[])
{int semid;key_t key;if ((key = ftok("./sem.c", 66)) < 0){perror("ftok err");return -1;}printf("key: %#x\n", key);//创建信号灯集semid = semget(key, 2, IPC_CREAT | IPC_EXCL | 0666);if (semid <= 0){if (errno == EEXIST)semid = semget(key, 2, 0666);   //直接打开信号灯集else{perror("semget err");return -1;}}else  //确保对信号灯集中的信号灯只初始化一次,下次执行程序还继续获得上一次的值。如果想修改初值,需要删除信号灯然后重新打开。{union semun sem;sem.val = 10;semctl(semid,0,SETVAL,sem); //对编号为0的信号灯初值设为10sem.val = 0;semctl(semid,1,SETVAL,sem); //对编号为1的信号灯初值设为0}printf("semid: %d\n", semid);//获取信号灯初值printf("%d\n",semctl(semid,0,GETVAL)); //获取编号为0的信号灯的值printf("%d\n",semctl(semid,1,GETVAL)); //获取编号为1的信号灯的值//pv操作struct sembuf buf;buf.sem_num=0;buf.sem_op=-1; //申请资源,P操作,-1buf.sem_flg=0; //阻塞semop(semid,&buf,1);buf.sem_num=1;buf.sem_op=1; //释放资源,V操作,+1buf.sem_flg=0;semop(semid,&buf,1);printf("%d\n",semctl(semid,0,GETVAL)); //获取编号为0的信号灯的值printf("%d\n",semctl(semid,1,GETVAL)); //获取编号为1的信号灯的值// //删除信号灯集// semctl(semid,0,IPC_RMID); //指定任意一个信号灯就可以,会删除整个信号灯集。return 0;
}

函数操作:

#include <sys/types.h>
#include <sys/ipc.h>
#include <stdio.h>
#include <sys/sem.h>
#include <errno.h>union semun {int val;
};void seminit(int semid, int num, int val) //初始化
{union semun sem;sem.val = val;semctl(semid, num, SETVAL, sem); //对编号为num的信号灯初值设为val
}void sem_op(int semid, int num,int op)
{struct sembuf buf;buf.sem_num = num;buf.sem_op = op;buf.sem_flg =0;semop(semid,&buf,1);
}int main(int argc, char const *argv[])
{int semid;key_t key;if ((key = ftok("./sem.c", 66)) < 0){perror("ftok err");return -1;}printf("key: %#x\n", key);//创建信号灯集semid = semget(key, 2, IPC_CREAT | IPC_EXCL | 0666);if (semid <= 0){if (errno == EEXIST)semid = semget(key, 2, 0666); //直接打开信号灯集else{perror("semget err");return -1;}}else //确保对信号灯集中的信号灯只初始化一次,下次执行程序还继续获得上一次的值。如果想修改初值,需要删除信号灯然后重新打开。{seminit(semid, 0, 10);seminit(semid, 1, 0);}printf("semid: %d\n", semid);//获取信号灯初值printf("%d\n", semctl(semid, 0, GETVAL)); //获取编号为0的信号灯的值printf("%d\n", semctl(semid, 1, GETVAL)); //获取编号为1的信号灯的值//pv操作sem_op(semid,0,-1);sem_op(semid,1,1);printf("%d\n", semctl(semid, 0, GETVAL)); //获取编号为0的信号灯的值printf("%d\n", semctl(semid, 1, GETVAL)); //获取编号为1的信号灯的值// //删除信号灯集// semctl(semid,0,IPC_RMID); //指定任意一个信号灯就可以,会删除整个信号灯集。return 0;
}

信号灯加到共享内存

输入功能程序:

#include <sys/types.h>
#include <sys/ipc.h>
#include <stdio.h>
#include <sys/sem.h>
#include <errno.h>
#include <string.h>
#include <sys/shm.h>union semun {int val;
};void seminit(int semid, int num, int val) //初始化
{union semun sem;sem.val = val;semctl(semid, num, SETVAL, sem); //对编号为num的信号灯初值设为val
}void sem_op(int semid, int num, int op)
{struct sembuf buf;buf.sem_num = num;buf.sem_op = op;buf.sem_flg = 0;semop(semid, &buf, 1);
}int main(int argc, char const *argv[])
{int semid;int shmid;char *p;key_t key;if ((key = ftok("./sem.c", 66)) < 0){perror("ftok err");return -1;}printf("key: %#x\n", key);//创建/打开共享内存shmid = shmget(key, 128, IPC_CREAT | IPC_EXCL | 0666);if (shmid <= 0){if (errno == EEXIST)shmid = shmget(key, 128, 0666);else{perror("shmget err");return -1;}}printf("shmid: %d\n", shmid);//创建信号灯集semid = semget(key, 2, IPC_CREAT | IPC_EXCL | 0666);if (semid <= 0){if (errno == EEXIST)semid = semget(key, 2, 0666); //直接打开信号灯集else{perror("semget err");return -1;}}else //确保对信号灯集中的信号灯只初始化一次,下次执行程序还继续获得上一次的值。如果想修改初值,需要删除信号灯然后重新打开。{seminit(semid, 0, 0);}printf("semid: %d\n", semid);//共享内存映射p = (char *)shmat(shmid, NULL, 0);if (p == (char *)-1){perror("shmat err");return -1;}while (1){scanf("%s", p);//释放资源sem_op(semid,0,1);if (strcmp(p, "quit")==0)break;}shmdt(p);shmctl(shmid,IPC_RMID,NULL);// //删除信号灯集// semctl(semid,0,IPC_RMID); //指定任意一个信号灯就可以,会删除整个信号灯集。return 0;
}

out.c

#include <sys/types.h>
#include <sys/ipc.h>
#include <stdio.h>
#include <sys/sem.h>
#include <errno.h>
#include <string.h>
#include <sys/shm.h>union semun {int val;
};void seminit(int semid, int num, int val) //初始化
{union semun sem;sem.val = val;semctl(semid, num, SETVAL, sem); //对编号为num的信号灯初值设为val
}void sem_op(int semid, int num, int op)
{struct sembuf buf;buf.sem_num = num;buf.sem_op = op;buf.sem_flg = 0;semop(semid, &buf, 1);
}int main(int argc, char const *argv[])
{int semid;int shmid;char *p;key_t key;if ((key = ftok("./sem.c", 66)) < 0){perror("ftok err");return -1;}printf("key: %#x\n", key);//创建/打开共享内存shmid = shmget(key, 128, IPC_CREAT | IPC_EXCL | 0666);if (shmid <= 0){if (errno == EEXIST)shmid = shmget(key, 128, 0666);else{perror("shmget err");return -1;}}printf("shmid: %d\n", shmid);//创建信号灯集semid = semget(key, 2, IPC_CREAT | IPC_EXCL | 0666);if (semid <= 0){if (errno == EEXIST)semid = semget(key, 2, 0666); //直接打开信号灯集else{perror("semget err");return -1;}}else //确保对信号灯集中的信号灯只初始化一次,下次执行程序还继续获得上一次的值。如果想修改初值,需要删除信号灯然后重新打开。{seminit(semid, 0, 0);}printf("semid: %d\n", semid);//共享内存映射p = (char *)shmat(shmid, NULL, 0);if (p == (char *)-1){perror("shmat err");return -1;}while (1){sem_op(semid,0,-1); //申请资源if (strcmp(p, "quit")==0)break;printf("%s\n", p);}shmdt(p);shmctl(shmid, IPC_RMID, NULL);// //删除信号灯集// semctl(semid,0,IPC_RMID); //指定任意一个信号灯就可以,会删除整个信号灯集。return 0;
}

7.消息队列:messagequeue

传统:无名管道、有名管道、信号

systemV:共享内存、信号灯集、消息队列

按消息的类型添加或读取消息

队列原则

7.1特点

消息队列是IPC对象(活动在内核级别的一种进程间通信的工具)的一种

一个消息队列由一个标识符 (即队列ID)来标识

消息队列就是一个消息的列表。用户可以在消息队列中添加消息、读取消息等

消息队列可以按照类型(自己设一个值作为类型)来发送/接收消息

7.2步骤

(1) 产生key值ftok

(2) 创建或打开消息队列msgget()

(3) 添加消息:按照消息的类型添加到已经打开的消息队列的末尾msgsnd()

(4) 读取消息:可以按照消息的类型从消息队列中读走msgrcv()

(5) 删除消息队列:msgctl()

7.3命令

ipcs -q:查看系统中的消息队列

ipcrm -q msgid:删除消息队列

7.4函数接口

int msgget(key_t key, int flag);
功能:创建或打开一个消息队列
参数:  key值flag:创建消息队列的权限IPC_CREAT|IPC_EXCL|0666返回值:成功:msgid失败:-1int msgsnd(int msqid, const void *msgp, size_t size, int flag); 
功能:添加消息
参数:msqid:消息队列的IDmsgp:指向消息的指针。常用消息结构msgbuf如下:struct msgbuf{long mtype;          //消息类型char mtext[N]};     //消息正文size:发送的消息正文的字节数flag:IPC_NOWAIT消息没有发送完成函数也会立即返回    0:直到发送完成函数才返回
返回值:成功:0失败:-1使用:msgsnd(msgid, &msg,sizeof(msg)-sizeof(long), 0)注意:消息结构除了第一个成员必须为long类型外,其他成员可以根据应用的需求自行定义。int msgrcv(int msgid,  void* msgp,  size_t  size,  long msgtype,  int  flag);
功能:读取消息
参数:msgid:消息队列的IDmsgp:存放读取消息的空间size:接受的消息正文的字节数(sizeof(msgp)-sizeof(long))msgtype:0:接收消息队列中第一个消息。大于0:接收消息队列中第一个类型为msgtyp的消息.小于0:接收消息队列中类型值不小于msgtyp的绝对值且类型值又最小的消息。flag:0:若无消息函数会一直阻塞IPC_NOWAIT:若没有消息,进程会立即返回ENOMSG
返回值:成功:接收到的消息的长度失败:-1int msgctl ( int msgqid, int cmd, struct msqid_ds *buf );
功能:对消息队列的操作,删除消息队列
参数:msqid:消息队列的队列IDcmd:IPC_STAT:读取消息队列的属性,并将其保存在buf指向的缓冲区中。IPC_SET:设置消息队列的属性。这个值取自buf参数。IPC_RMID:从系统中删除消息队列。buf:消息队列缓冲区
返回值:成功:0失败:-1用法:msgctl(msgid, IPC_RMID, NULL)
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdio.h>
#include <errno.h>struct msgbuf
{long type; //必须有而且必须是long类型,表示消息类型,值>0int num;   //正文随便定义char ch;
};int main(int argc, char const *argv[])
{key_t key;int msgid;key = ftok("msg.c", 99);if (key < 0){perror("ftok err");return -1;}printf("key:%#x\n", key);//打开消息队列msgid = msgget(key, IPC_CREAT | IPC_EXCL | 0666);if (msgid <= 0){if (errno == EEXIST)msgid = msgget(key, 0666);else{perror("msgget err");return -1;}}printf("msgid:%d\n", msgid);//添加消息struct msgbuf msg;msg.type = 10;msg.num = 100;msg.ch = 'a';msgsnd(msgid, &msg, sizeof(msg) - sizeof(long), 0); //0:阻塞等发完才返回msg.type = 20;msg.num = 200;msg.ch = 'b';msgsnd(msgid, &msg, sizeof(msg) - sizeof(long), 0);//读取消息struct msgbuf m;msgrcv(msgid, &m, sizeof(m) - sizeof(long), 20, 0); //读取消息队列中类型为20的第一个消息printf("%d %c\n", m.num, m.ch);msgctl(msgid,IPC_RMID,NULL);return 0;
}

两个进程分别发和收:

相关文章:

【Linux】进程间通信IPC

目录 进程间通信 IPC 1. 进程间通信方式 2. 无名管道 2.1 特点 2.2 函数接口 2.3 注意事项 3. 有名管道 3.1 特点 3.2 函数接口 3.3 注意事项 3.4 有名管道和无名管道的区别 4. 信号 4.1概念 4.2信号的响应方式 4.3 信号种类 4.4 函数接口 4.4.1 信号发送和挂…...

go语言实现UTF8与GB2312内码转换

使用Go语言做个UTF-8转GB2312的代码,输入utf-8编码的文本&#xff0c;输出转换后的国标编码的hex内码 package mainimport ("fmt""os""strings""golang.org/x/text/encoding/simplifiedchinese""golang.org/x/text/transform&quo…...

【WPS】【WORDWORD】【JavaScript】实现微软WORD自动更正的效果

1.效果展示 2.核心代码展示 function readTableData(filePath) {let tableData {};let doc Application.Documents.Open(filePath); // 打开文档let table doc.Tables(1); // 获取第一个表格// 遍历表格&#xff0c;存储编号和描述的映射for (let i 1; i < table.Rows.…...

Excel数据叠加生成新DataFrame:操作指南与案例

目录 一、准备工作 二、读取Excel文件 三、数据叠加 四、处理重复数据(可选) 五、保存新DataFrame到Excel文件 六、案例演示 七、注意事项 八、总结 在日常数据处理工作中,我们经常需要将不同Excel文档中的数据整合到一个新的DataFrame中,以便进行进一步的分析和处…...

springBoot项目使用Elasticsearch教程

目录 一、引言&#xff08;一&#xff09;使用背景&#xff08;二&#xff09;版本库区别 二、引入依赖&#xff08;一&#xff09;springboot集成的es依赖&#xff08;建议&#xff09;&#xff08;二&#xff09;es提供的客户端库 三、配置&#xff08;以yaml文件为例&#x…...

摄像头模块在狩猎相机中的应用

摄像头模块是狩猎相机的核心组件&#xff0c;在狩猎相机中发挥着关键作用&#xff0c;以下是其主要应用&#xff1a; 图像与视频拍摄 高清成像&#xff1a;高像素的摄像头模块可确保狩猎相机拍摄出清晰的图像和视频&#xff0c;能够捕捉到动物的毛发纹理、行为细节及周围环境的…...

栈溢出原理

文章目录 前言一、基本示例二、分析栈1. 先不考虑gets函数的栈情况2. 分析gets函数的栈区情况 三、利用栈1. 构造字符串2. 利用漏洞 前言 栈溢出指的是程序向栈中某个变量中写入的字节数超过了这个变量本身所申请的字节数&#xff0c;因而导致与其相邻的栈中的变量的值被改变。…...

38.【3】CTFHUB web sql 报错注入

进入靶场 按照提示输入1 显示查询正确 既然是报错注入&#xff0c;先判断整形还是字符型注入 先输入1 and 11 再输入1 and 12 都显示查询正确&#xff0c;可知此为字符串型注入&#xff0c;不是数字型注入 然后就不会了 求助AI和其他wp 由以上2张搜索结果知updatexml是适用…...

SSE部署后无法连接问题解决

1. 问题现象 通过域名访问 https://api-uat.sfxs.com/sse/subscribe?tokenBearer%20eyJUxMiJ9.eyJhY2NvdW50IjoiYWRtaWZ0NvZGUiOiIwMDEiLCJyb2xidXNlcm5hbWUiOiLotoXnuqfnrqHnkIblkZgifQ.tlz9N61Y4 一直无法正常连接 2. 问题解决 nginx.conf进行配置 server {location /ss…...

sparkRDD教程之基本命令

作者&#xff1a;nchu可乐百香果 指导者&#xff1a;nchu-YoungDragon 1.前期准备 &#xff08;1&#xff09;从迅雷网盘上面下载这个项目&#xff0c;并且把scala&#xff0c;maven和java环境配置好 网盘链接&#xff1a; 分享文件&#xff1a;SparkRDD.zip 链接&#xf…...

【JavaScript】比较运算符的运用、定义函数、if(){}...esle{} 语句

比较运算符 !><> < 自定义函数&#xff1a; function 函数名&#xff08;&#xff09;{ } 判断语句&#xff1a; if(判断){ }else if(判断){ 。。。。。。 }else{ } 代码示例&#xff1a; <!DOCTYPE html> <html> <head><meta charset&quo…...

ShardingSphere—SQL 路由与执行解析原理

在分布式数据库中&#xff0c;SQL 路由、解析及执行是核心机制&#xff0c;用于高效处理 SQL 请求并将其分发到合适的数据节点。在 ShardingSphere 中&#xff0c;这一过程分为三个主要阶段&#xff1a;SQL 路由、SQL 解析 和 SQL 执行&#xff0c;通过灵活的策略和优化机制实现…...

机器学习——什么是代价函数?

1.代价函数的定义 首先,提到代价函数是估计值和实际值的差,这应该是指预测值和真实值之间的差异,用来衡量模型的好坏。 在一元线性模型中,模型是直线,有两个参数,可能是斜率和截距。 通过调整这两个参数,让代价函数最小,这应该是说我们要找到最佳的斜率和截距,使得预测…...

k8s集群换IP

k8s集群搭建及节点加入时需要确定IP&#xff0c;但安装完成后设备移动到新环境可能出现网段更换或者IP被占用的情况&#xff0c;导致无法ping通节点或者无法打开原IP的服务。 解决方法为保持原有IP不更换&#xff0c;给网卡再加一个IP 这边使用两个ubuntu虚拟机模拟服务器和w…...

RPC 源码解析~Apache Dubbo

解析 RPC&#xff08;远程过程调用&#xff09;的源码可以帮助你深入理解其工作原理和实现细节。为了更好地进行源码解析&#xff0c;我们选择一个流行的 RPC 框架——Apache Dubbo 作为示例。Dubbo 是一个高性能、轻量级的开源 Java RPC 框架&#xff0c;广泛应用于企业级应用…...

【认识油管头部频道】ep5 “5-Minute Crafts”——DIY 和生活技巧

5-Minute Crafts 是一个非常受欢迎的 DIY 和生活技巧频道&#xff0c;它的火爆有多方面的原因&#xff1a; 1. 简单实用的内容 视频主要以解决日常生活中遇到的小问题为主&#xff0c;提供简单易学的技巧&#xff0c;吸引了想快速获取实用知识的观众。 2. 短视频形式 每个视…...

vue3 uniapp封装一个瀑布流组件

新增组件m-waterfall 这样就可以在页面直接使用 不用在引入了 <template><view class"m-waterfall"><view id"m-left-column" class"m-column"><slot name"left" :leftList"leftList"></slot&…...

基于Java的语音陪聊软件——支持聊天私聊-礼物系统-直播系统-缘分匹配-游戏陪玩

丰富的经验、成熟的技术&#xff0c;打造适合当下市场发展的语音交友软件源码。Java 语言凭借其独特的优势&#xff0c;为这款语音陪聊软件的稳健运行和持续发展奠定了坚实基础。它不仅融合了聊天私聊、礼物系统和直播系统等实用且有趣的功能&#xff0c;还创新性地引入了缘分匹…...

山石防火墙命令行配置示例

现网1台山石SG6000防火墙&#xff0c;配置都可以通过GUI实现。 但有一些配置在命令行下配置效率更高&#xff0c;比如在1个已有策略中添加1个host或端口。 下面的双引号可以不加 1 创建服务 1.1 单个端口 service "tcp-901"tcp dst-port 901 1.2 端口范围 servi…...

WordPress内容保护策略:如何反击并利用被盗内容

当你的网站开始获得大量流量时&#xff0c;内容盗窃成为不可避免的问题。除了通过高级的保护措施防止内容被盗&#xff0c;你还可以采取一些策略来反击内容盗窃&#xff0c;并从中获益。 1. 识别并封锁恶意IP地址 当你发现某些IP地址频繁访问并抓取你的网站内容时&#xff0c…...

UDP报文格式

UDP是传输层的一个重要协议&#xff0c;他的特性有面向数据报、无连接、不可靠传输、全双工。 下面是UDP报文格式&#xff1a; 1&#xff0c;报头 UDP的报头长度位8个字节&#xff0c;包含源端口、目的端口、长度和校验和&#xff0c;其中每个属性均为两个字节。报头格式为二…...

【工具类】获取日出日落时间的Java工具类

博主介绍&#xff1a;✌全网粉丝22W&#xff0c;CSDN博客专家、Java领域优质创作者&#xff0c;掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域✌ 技术范围&#xff1a;SpringBoot、SpringCloud、Vue、SSM、HTML、Nodejs、Python、MySQL、PostgreSQL、大数据、物…...

ES7【2016】、ES8【2017】新增特性(六)

ES7【2016】新增特性 幂指数操作符 在ES7【2016】中新增了幂指数操作&#xff0c;幂指数操作符是**。它用于指数计算 基本语法&#xff1a;baseValue ** exponent 参数说明&#xff1a;baseValue是基数&#xff0c;exponent是指数。 let base 2; let exponent 4; let resul…...

SparX:一种用于层次视觉Mamba和变换器网络的稀疏跨层连接机制

摘要 https://arxiv.org/pdf/2409.09649 由于动态状态空间模型&#xff08;SSMs&#xff09;能够以线性时间计算复杂度捕获长距离依赖关系&#xff0c;Mamba在自然语言处理&#xff08;NLP&#xff09;任务中表现出了显著的性能。这激发了基于Mamba的视觉模型的快速发展&#…...

Spring Boot 中实现 WebSocket 的方式

在 Spring Boot 中实现 WebSocket 的方式主要有以下几种,每种方式适用于不同的场景和需求: 1. 基于 Spring WebSocket 的实现 特点: 原生支持 WebSocket,基于 Spring 提供的 API。使用 WebSocketConfigurer 和 WebSocketHandler 配置端点和消息处理逻辑。可以通过拦截器访…...

时序数据库TDengine 3.3.5.0 发布:高并发支持与增量备份功能引领新升级

近日&#xff0c;TDengine 3.3.5.0 版本正式发布&#xff0c;带来了多项重磅更新与优化&#xff0c;从功能拓展到性能提升&#xff0c;再到用户体验进行了全面改进。本次更新围绕用户核心需求展开&#xff0c;涵盖了开发工具、数据管理、安全性、可视化等多个层面&#xff0c;为…...

Elasticsearch:Jira 连接器教程第一部分

作者&#xff1a;来自 Elastic Gustavo Llermaly 将我们的 Jira 内容索引到 Elaasticsearch 中以创建统一的数据源并使用文档级别安全性进行搜索。 在本文中&#xff0c;我们将回顾 Elastic Jira 原生连接器的一个用例。我们将使用一个模拟项目&#xff0c;其中一家银行正在开发…...

HarmonyOS NEXT开发进阶(六):HarmonyOS NEXT实现嵌套 H5 及双向通信

文章目录 一、前言二、鸿蒙应用加载Web页面2.1 加载网络地址页面2.2 加载本地H5页面 三、实现Web组件 H5 层与鸿蒙应用层进行相互通讯3.1 鸿蒙应用向 H5 页面发送数据3.2 H5页面向鸿蒙应用发送数据 四、拓展阅读 一、前言 随着HarmonyOS NEXT的快速发展&#xff0c;越来越多的…...

Myeclipse最新版本 C1 2019.4.0

Myeclipse C1 2019.4.0下载地址&#xff1a;链接: https://pan.baidu.com/s/1MbOMLewvAdemoQ4FNfL9pQ 提取码: tmf6 1.1、什么是集成开发环境? ★集成开发环境讲究-站式开发&#xff0c;使用这个工具即可。有提示功能&#xff0c;有自动纠错功能。 ★集成开发环境可以让软件开…...

深度学习中PyTorch张量的重塑操作

深度学习中PyTorch张量的重塑操作 张量操作类型 在我们深入具体的张量操作之前&#xff0c;让我们先快速浏览一下主要的操作类别&#xff0c;这些类别包括我们将要介绍的操作。我们有以下高级类别的操作&#xff1a; 重塑操作元素级操作归约操作访问操作 有很多单独的操作&…...

lua下标是可以从0开始

故事背景&#xff0c;策划搞了一个功能配置表&#xff0c;我看居然是0开始的&#xff0c;功能也正常。于是测试了下&#xff0c;还真的可以。网上看了资料确实可以&#xff0c;但是也有需要注意的问题 local test {[0] 0} for k,v in pairs(test)doprint(k,v) endhttps://bl…...

从AI原理到模型演进及代码实践 的学习二

参考&#xff1a;全面解析&#xff1a;从AI原理到模型演进及代码实践-CSDN博客 训练过程 Transformer仅一个Encoder模块就可以工作&#xff0c;可以处理信息抽取、识别、主体识别等任务&#xff0c;比如 BERT&#xff08;Bidirectional Encoder Representations from Transfor…...

计算机组成原理(计算机系统3)--实验二:MIPS64乘法实现实验

一、实验目标&#xff1a; 实际运用WinMIPS64进行试验&#xff0c;以期更了解WinMIPS64的操作&#xff1b; 更加深入地了解MIPS程序的语法&#xff1b; 深入地了解在计算机中乘法的实现以及加法与乘法之间的关系。 二、实验内容 按照实验报告指导&#xff0c;完成相关操作…...

WPS excel使用宏编辑器合并 Sheet工作表

使用excel自带的工具合并Sheet表&#xff0c;我们会发现需要开通WPS会员才能使用合并功能&#xff1b; 那么WPS excel如何使用宏编辑器进行合并 Sheet表呢&#xff1f; 1、首先我们要看excel后缀是 .xlsx 还是 .xls &#xff1b;如果是.xlsx 那么 我们需要修改为 .xls 注…...

ASP.NET Core - 配置系统之配置添加

ASP.NET Core - 配置系统之配置添加 2. 配置添加 2. 配置添加 配置系统可以读取到配置文件中的信息&#xff0c;那必然有某个地方可以将配置文件添加到配置系统中。之前的文章中讲到 ASP.NET Core 入口文件中&#xff0c;builder(WebApplicationBuilder 对象) 中有一个 Config…...

【Redis】Redis 集群中节点之间如何通信?

【Redis】Redis 集群中节点之间如何通信&#xff1f; 一背景概述二通信协议Gossip 协议 三通信机制Gossip 消息类型(1).Ping消息(2).Pong消息(3).Meet消息(4).Fail消息 消息传播模式(1).反熵(Anti-entropy)(2).谣言传播(Rumor mongering) 四通信过程通信端口通信频率故障检测与…...

iOS - 内存对齐

1. 基本的内存对齐 // 对象内存对齐 struct objc_object {// isa 指针 8 字节对齐isa_t isa __attribute__((aligned(8))); };// 定义对齐常量 #define WORD_MASK 7UL // 字对齐掩码 #define WORD_SHIFT 3UL // 字对齐位移 #define WORD_SIZE 8 …...

RabbitMQ(四)

SpringBoot整合RabbitMQ SpringBoot整合1、生产者工程①创建module②配置POM③YAML④主启动类⑤测试程序 2、消费者工程①创建module②配置POM③YAML文件内配置&#xff1a; ④主启动类⑤监听器 3、RabbitListener注解属性对比①bindings属性②queues属性 SpringBoot整合 1、生…...

unity学习17:unity里的旋转学习,欧拉角,四元数等

目录 1 三维空间里的旋转与欧拉角&#xff0c;四元数 1.1 欧拉角比较符合直观 1.2 四元数 1.3 下面是欧拉角和四元数的一些参考文章 2 关于旋转的这些知识点 2.1 使用euler欧拉角旋转 2.2 使用quaternion四元数,w,x,y,z 2.3 使用quaternion四元数,类 Vector3.zero 这种…...

当PHP遇上区块链:一场奇妙的技术之旅

PHP 与区块链的邂逅 在技术的广袤宇宙中&#xff0c;区块链技术如同一颗耀眼的新星&#xff0c;以其去中心化、不可篡改、透明等特性&#xff0c;掀起了一场席卷全球的变革浪潮。众多开发者怀揣着对新技术的热忱与探索精神&#xff0c;纷纷投身于区块链开发的领域&#xff0c;试…...

Portainer.io安装并配置Docker远程访问及CA证书

Portainer.io安装并配置Docker远程访问及CA证书 文章目录 Portainer.io安装并配置Docker远程访问及CA证书一.安装 Portainer.io2.启动容器 二.docker API远程访问并配置CA安全认证1.配置安全(密钥)访问2.补全CA证书信息3.生成server-key.pem4.创建服务端签名请求证书文件5.创建…...

从漏洞管理到暴露管理:网络安全的新方向

在网络安全的快速发展中&#xff0c;传统的漏洞管理仍然是防御体系的基石。然而&#xff0c;面对日益复杂的威胁和不断增长的漏洞数量&#xff0c;单靠漏洞优先级评分&#xff08;如VPR&#xff09;已经无法满足现代安全需求。暴露管理这一全新的思维模式正在引领企业从被动应对…...

GraphRAG如何使用ollama提供的llm model 和Embedding model服务构建本地知识库

使用GraphRAG踩坑无数 在GraphRAG的使用过程中将需要踩的坑都踩了一遍&#xff08;不得不吐槽下&#xff0c;官方代码有很多遗留问题&#xff0c;他们自己也承认工作重心在算法的优化而不是各种模型和框架的兼容性适配性上&#xff09;&#xff0c;经过了大量的查阅各种资料以…...

HTTP/HTTPS ⑤-CA证书 || 中间人攻击 || SSL/TLS

这里是Themberfue ✨上节课我们聊到了对称加密和非对称加密&#xff0c;实际上&#xff0c;单纯地非对称加密并不能保证数据不被窃取&#xff0c;我们还需要一个更加重要的东西——证书 中间人攻击 通过非对称加密生成私钥priKey和公钥pubKey用来加密对称加密生成的密钥&…...

MYSQL学习笔记(二):基本的SELECT语句使用(基本、条件、聚合函数查询)

前言&#xff1a; 学习和使用数据库可以说是程序员必须具备能力&#xff0c;这里将更新关于MYSQL的使用讲解&#xff0c;大概应该会更新30篇&#xff0c;涵盖入门、进阶、高级(一些原理分析);这一篇是讲解SELECT语句使用&#xff0c;包括基本、条件、聚合函数查询&#xff0c;…...

云原生周刊:Prometheus 3.0 正式发布

开源项目推荐 Achilles-SDK Achilles-SDK 是一个专为构建 Kubernetes 控制器而设计的开源开发工具包。它简化了控制器的开发流程&#xff0c;提供了强大的 API 和高效的抽象层&#xff0c;使开发者能够专注于业务逻辑的实现&#xff0c;而无需处理底层复杂性。Achilles-SDK 支…...

维护数据完整性(三)

立即检查的&#xff08;IMMEDIATE&#xff09;或者延迟检查的&#xff08;DEFERRED&#xff09; 使用 SET CONSTRAINTS 语句 SET CONSTRAINTS语句可以用来在事务内部改变约束的检查时机。以下是如何使用该语句的示例&#xff1a; SET CONSTRAINTS constraint_name DEFERRED;…...

前后端分离开发心得

前后端分离开发是一种软件开发模式&#xff0c;将前端和后端的开发分离开来&#xff0c;使得前端和后端可以独立开发、测试和部署。具体来说&#xff1a; • 前端&#xff1a;负责展示数据和用户交互&#xff0c;使用 HTML、CSS、JavaScript 等技术实现用户界面和交互逻辑&…...

Redisson发布订阅学习

介绍 Redisson 的消息订阅功能遵循 Redis 的发布/订阅模式&#xff0c;该模式包括以下几个核心概念&#xff1a; 发布者&#xff08;Publisher&#xff09;&#xff1a;发送消息到特定频道的客户端。在 Redis 中&#xff0c;这通过 PUBLISH 命令实现。 订阅者&#xff08;Sub…...

【Golang/nacos】nacos配置的增删查改,以及服务注册的golang实例及分析

前言 本文分析的实例来源于nacos在github上的开源仓库 nacos配置的增删查改 先具体来看一段代码&#xff0c;我将逐步分析每一段的作用 package mainimport ("fmt""time""github.com/nacos-group/nacos-sdk-go/clients""github.com/naco…...