进程间通信

article/2025/9/14 10:18:23

文章目录

  • 前言
  • 1、进程间通信的目的
  • 2、管道
    • 1.1 匿名管道
    • 1.2 命名管道
  • 3、共享内存
    • 2.1 shmget函数
    • 2.2 shmctl函数
    • 2.3 shmat函数
    • 2.4 shmdt函数
  • 4、消息队列
  • 5、信号量

前言

进程间通信(IPC,InterProcess Communication)是指在不同进程之间传播或交换信息。IPC的方式通常有管道(包括匿名管道和命名管道)、消息队列、信号量、共享内存等。

在学习之前我们需要了解以下内容:
1.因为进程具有独立性,如果两个或者多个进程需要相互通信,就必须要看到同一份资源:就是一段内存!这个内存可能以文件的方式提供,也可能以队列的方式,也可能提供的就是原始的内存块!
2.这个公共资源应该属于谁?这个公共资源肯定不属于任何进程,如果这个资源属于进程,这个资源就不应该再让其它进程看到,要不然进程的独立性怎么保证呢?所以这个资源只能属于操作系统!
3.进程间通信的前提:是有OS参与,提供一份所有通信进程能看到的公共资源!


1、进程间通信的目的

  • 数据传输:一个进程需要将它的数据发送给另一个进程
  • 资源共享:多个进程之间共享同样的资源
  • 通知事件:一个进程需要向另一个或一组进程发送消息,通知它(它们)发生了某种事件(如进程终止时要通知父进程)
  • 进程控制:有些进程希望完全控制另一个进程的执行(如Debug进程),此时控制进程希望能够拦截另一个进程的所有陷入和异常

2、管道

管道是Unix中最古老的进程间通信的形式。
我们把从一个进程连接到另一个进程的一个数据流称为一个"管道"。
管道是一个只能单向通信的通信管道,如果想要双向通信,那么就建立两个管道。
管道的本质是内核中的一块缓冲区
在这里插入图片描述

1.1 匿名管道

在这里插入图片描述
参数:是一个输出型参数,通过这个参数读取到两个打开的fd
返回值:若创建成功则返回0,否则返回-1

pipedf[0] 和 pipedf[1] 哪一个是读,哪一个是写呢?
0:读取端 1:写入端。 我们可以把0想象成嘴巴, 把1想象成笔,这样就很容易记住了

#include<stdio.h>
#include<unistd.h>
int main()
{//pipefd[2]:是一个输出型参数!int pipefd[2] = {0};if(pipe(pipefd) != 0){perror("pipe error!\n");return -1;}//pipedf[0], pipedf[1] 哪一个是读,哪一个是写呢?//0(嘴):读取端    1(笔):写入端printf("pipefd[0]: %d\n", pipefd[0]);//打印3,因为0,1,2已经被占用printf("pipefd[1]: %d\n", pipefd[1]);//打印4,因为0,1,2,3已经被占用return 0;
}

运行代码:
在这里插入图片描述
结果跟预想的一样是 3 和 4

看着样一段代码:

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<string.h>
int main()
{//pipefd[2]:是一个输出型参数!int pipefd[2] = {0};if(pipe(pipefd) != 0){perror("pipe error!\n");return -1;}//pipedf[0], pipedf[1] 哪一个是读,哪一个是写呢?//0(嘴):读取端    1(笔):写入端printf("pipefd[0]: %d\n", pipefd[0]);//打印3,因为0,1,2已经被占用printf("pipefd[1]: %d\n", pipefd[1]);//打印4,因为0,1,2,4已经被占用//我们让父进程读取,子进程写入if(fork() == 0){//子进程close(pipefd[0]);//关闭读端,保留写端const char* msg = "hello fl";while(1){ write(pipefd[1], msg, strlen(msg));//strlen 不需要加1,因为'\0'是语言层面的结束标志符,不是系统的sleep(1);}eixt(0);}//父进程close(pipefd[1]);//关闭写端,保留读端while(1){   //我们没有让父进程sleepchar buffer[64] = {0};ssize_t s = read(pipefd[0], buffer, sizeof(buffer)-1);//zero indicates end of file, 如果read的返回值是0,意味着子进程关闭了文件描述符if(s == 0){	printf("read quit...\n");break;}else if(s > 0){buffer[s] = 0;printf("child say to father# %s\n", buffer);         }else{printf("read error...\n");break;}}return 0;
}

上面这段代码的意思就是创建一个匿名管道,再创建一个子进程,让子进程每隔1秒就往管道里面写数据,父进程不挺从管道里面读数据
运行代码:
在这里插入图片描述
如果我们让子进程不要sleep,一直往管道里面写数据,父进程每隔一秒从管道里读取一次数据,buffer的大小还是64

运行代码后:
在这里插入图片描述
不是应该一句一句的读取吗?为什么一次读取这么多呢?
对于写端来说,pipe里面只要有缓冲区,就会一直写入。对于读端来说,缓冲区只要有数据,就会一直读取(每次都读63个字节)。
对于这种特性,就叫做字节流

如果子进程写入,而父进程就是不读

//子进程
int count = 0;
while(1)
{write(pipefd[1], "a", 1);count++;printf("count: %d\n", count);//写一个字符(一字节),就打印一下
}
//父进程
while(1)
{sleep(1);
}

运行代码:
在这里插入图片描述
最终我们发现写满了65536字节就不会写入了。65536字节也就是64KB,所以管道的大小是64KB
当写端写满的时候,为什么不写了?
因为要让读端来读。此时可能会有人问,为什么要让读端来读?如果管道满了,不等读端读取的话,再写入,也就相当于把之前写入的数据给覆盖了,那对于写端来讲,之前做的工作就白做了。进程间通信也是为了相互合作的,写入的数据就应该给读端来读,这才是相互合作的表现。

//父进程
while(1)
{   sleep(5);char c[64] = {0};ssize_t s = read(pipefd[0], &c, 63);c[s] = '\0';printf("father take 63 byte\n");
}

如果我们让父进程一次读取63个字节,每次读完后就打印一次数据
运行代码:
在这里插入图片描述
虽然父进程一次读63个字节,但是子进程依旧没有写入

那就让父进程每次读取2KB

//父进程while(1)
{   //我们没有让父进程sleepsleep(5);char c[1024*2+1] = {0};ssize_t s = read(pipefd[0], &c, sizeof(c));c[s] = '\0';printf("father take 2KB \n");
}

运行代码:
在这里插入图片描述
我们发现每次读取4KB之后,写端才会写入。为什么会这样呢?原因就是要保证读写的原子性!

如果让子进程每隔4秒,写一个字符串

//子进程
while(1)
{write(pipefd[1], msg, strlen(msg));sleep(4);
}
//父进程
while(1)
{   char c[64] = {0};ssize_t s = read(pipefd[0], &c, sizeof(c));c[s] = '\0';printf("father take %s\n", c);
}

运行代码:
在这里插入图片描述
虽然读端一直在读,但是写端每隔4秒才写入一次数据,就相当于读端一直在等写端写入数据

子进程写入一条消息后,间隔7秒就退出,再关闭写端的文件描述符,而父进程一直读取

//子进程
while(1)
{write(pipefd[1], msg, strlen(msg));sleep(7);break;}
close(pipefd[1]);
//父进程
while(1)
{   //我们没有让父进程sleep//sleep(5);char c[64] = {0};ssize_t s = read(pipefd[0], &c, sizeof(c));if(s > 0){c[s] = '\0';printf("father take %s\n", c);}else if(s == 0){printf("writer quit...\n");break;}elsebreak;

运行代码:
在这里插入图片描述
经过测试我们发现,写端(子进程)退出后,紧接着读端(父进程)也退出了。

如果子进程不断地写入数据,而父进程先sleep10秒,然后只读一次数据,就关闭读端

//子进程
const char* msg = "hello fl";
while(1)
{write(pipefd[1], msg, strlen(msg));
}
//父进程
while(1)
{sleep(10);char c[64] = {0};ssize_t s = read(pipefd[0], &c, sizeof(c));break;
}
close(pipefd[0]);

我们可以用这段命令来监控一下while :; do ps axj | grep pipe_process | grep -v grep; sleep 1; echo "#################################";done
运行代码:
在这里插入图片描述
此时我们发现读端关闭,写端也会关闭。为什么呢?
当我们的读端关闭,写端还在写入,此时站在OS的层面,合理吗?
比如老师在讲课,学生都走了,都已经没人听老师讲课了
这种情况是严重不合理的,是没有任何价值的,站在OS层面,就是在浪费OS的资源,所以OS会直接终止写入进程!OS会给目标进程发送SIGPIPE信号

小总结:
匿名管道的4种情况

  1. 读端不读或者读得慢,写端要等待读端
  2. 读端关闭,写端会被关闭
  3. 写端不写或者写得慢,读端要等写端
  4. 写端关闭,读端读完数据后,并不会关闭

匿名管道的5个特点:

  1. 管道是一个只能单向通信的通信信道
  2. 管道是面向字节流的
  3. 具有血缘关系的进程进行进程间通信,例如父子进程
  4. 管道自带同步机制,原子性写入(互斥)
  5. 匿名管道是一块内核缓冲区,不会在文件系统存在,如果匿名管道对应的所有进程都关闭了,操作系统就会做相应的回收。所以管道的声生命周期是随进程的

1.2 命名管道

为了解决匿名管道只能具有血缘关系的进程进行通信,引入的命名管道。
命名管道与匿名管道的特点基本一致。
如果我们想在不相关的进程之间交换数据,可以使用FIFO文件来做这项工作,它经常被称为命名管道。
命名管道是一种特殊类型的文件,在文件系统中存在,生命周期随操作系统
在这里插入图片描述
第一个参数是创建管道的名字
第二个参数是创建管道的权限

#include<stdio.h>
#include<sys/stat.h>
#include<sys/types.h>#define MY_FIFO "./fifo"
int main()
{if(mkfifo(MY_FIFO, 0666) < 0){perror("mkfifo");return 1;}return 0;
}

运行代码:
在这里插入图片描述
我们看到确实创建了一个管道,但是给的权限是666,创建出来的管道为什么是664呢?
其实原因就是创建的时候是要受系统umask的影响的,所以只需要在创建管道的时候,把umask设置为0即可。
在这里插入图片描述
一旦我们具有了一个命名管道,此时,我们只需要让通信双方按文件操作即可!

看以下代码:

comm.h
#include<stdio.h>
#include<unistd.h>
#include<sys/stat.h>
#include<sys/types.h>
#include<fcntl.h>
#define MY_FIFO "./fifo"server.c
#include "comm.h"
int main()
{umask(0);if(mkfifo(MY_FIFO, 0666) < 0){perror("mkfifo");return 1;}//只需要文件操作即可,语言层面的文件操作也行,但是可能会受到缓冲区的影响int fd = open(MY_FIFO, O_RDONLY);if(fd < 0){perror("open");return 2;}//业务逻辑,可以进行对应的读写while(1){char buffer[64] = {0};ssize_t s = read(fd, buffer, sizeof(buffer)-1);if(s > 0){//successbuffer[s] = 0;printf("client## %s\n", buffer);}else if(s == 0){//perr closeprintf("client quit...\n");break;}else{//errorperror("read\n");break;}}close(fd);return 0;
}client.c
#include "comm.h"
#include<string.h>int main()
{//如果管道已经存在,就不需要创建管道int fd = open(MY_FIFO, O_WRONLY);if(fd < 0){perror("open");return 1;}//业务逻辑while(1){printf("请输入# ");fflush(stdout);char buffer[64] = {0};//先把数据从标准输入拿到我们的client进程内部ssize_t s = read(0, buffer, sizeof(buffer)-1);//键盘输入的时候,/n也是输入字符的一部分if(s > 0){buffer[s-1] = 0;printf("%s\n", buffer);//拿到了数据write(fd, buffer, strlen(buffer));}}close(fd);return 0;
}

运行代码:
在这里插入图片描述
一个server.c进程,一个client.c进程,两个进程没有任何关系,通过命名管道就能彼此通信

我们把server.c的代码稍作修改

if(s > 0)
{//successbuffer[s] = 0;if(strcmp(buffer, "show") == 0){if(fork() == 0){execl("usr/bin/ls", "ls", "-1", NULL);exit(1);}waitpid(-1, NULL, 0);}else if(strcmp(buffer, "run") == 0){if(fork() == 0){execl("/usr/bin/sl", "sl", NULL);exit(1);}waitpid(-1, NULL, 0);}elseprintf("client# %s\n", buffer);}

运行代码:
在这里插入图片描述
此时就通过cilent就能控制server的操作了

3、共享内存

共享内存区是最快的IPC形式。一旦这样的内存映射到共享它的进程的地址空间,这些进程间数据传递不再涉及到内核,换句话说是进程不再通过执行进入内核的系统调用来传递彼此的数据
在这里插入图片描述
1.通过某种调用,在内存中创建一份内存空间!
2.通过某种调用,让进程"挂接"到这份新开辟的内存空降上!
通过上述这两步操作后,我们就可以使不同的进程看到了同一份资源,就能使不同的进程进行通信了,这种通信方案就叫做共享内存

2.1 shmget函数

在这里插入图片描述
功能:用来创建共享内存
参数

  • key:这个共享内存段名字
  • size:共享内存大小
  • shmflg:由九个权限标志构成,它们的用法和创建文件时使用的mode模式标志是一样的返回值:成功返回一个非负整数,即该共享内存段的标识码;失败返回-1

对于shmflg,在这里只说两个IPC_CREAT 和 IPC_EXCL
在这里插入图片描述
IPC_CREAT:单独使用,或者shmflg为0,则表示如果不存在为key的共享内存,就会直接创建,如果存在了,则直接返回当前已经存在的共享内存(基本不会空手而归)。
IPC_EXCL:单独使用没有意义
IPC_CREAT | IPC_EXCL:如果不存在为key的共享内存,则创建。反之则报错。(意义:如果我调用成功,得到的一定是一个最新的,没有被别人使用过的共享内存!)

对于key:
相当于唯一标识符ID,需要用户自己填入。理论来讲,用户可以随便填什么值,具体是几并不重要,重要的是它和其他key不一样。但难免会填写的值与其他的key冲突,所以我们一般使用ftok()函数获取key
在这里插入图片描述
参数

  • 自定义路径名
  • 自定义项目ID

返回值:如果是-1,则表示失败

server.c
int main()
{key_t key = ftok(PATH_NAME, PROJ_ID);if(key < 0){perror("ftok");return 1;}printf("%u\n", key);return 0;
}

在这里插入图片描述
此时就形成了一个key为1711342188的共享内存,此时这段代码在client.c中也要有一份,因为要保证两个进程看到的是同一份共享内存,才能进行后续的通信

再看这样一段代码:

comm.h
#pragma once
#include<stdio.h>
#include<sys/ipc.h>
#include<sys/shm.h>#define PATH_NAME "./"
#define PROJ_ID 0X6666
#define SIZE 4097server.c
#include "comm.h"int main()
{key_t key = ftok(PATH_NAME, PROJ_ID);if(key < 0){perror("ftok");return 1;}int shmid = shmget(key, SIZE, IPC_CREAT|IPC_EXCL);if(shmid < 0){perror("shmget");return 2;}printf("key: %u, shmid: %d\n", key, shmid);return 0;
}

运行代码:
在这里插入图片描述
第一次执行server之后,成功打印了key和shimd,我们发现shimd默认是从0开始的

除了第一次执行server之外,后面执行的server为什么会打印"shmget:file exists"呢?
此时说明共享内存已经被创建出来了。
通过指令ipcs -m可以查看被创建出来的共享内存
在这里插入图片描述

通过这个例子我们可以知道,对于该进程曾经创建的共享内存在进程结束的时候被释放了吗?答案是没有。原因是:对于systemV的IPC资源,它不属于任何一个进程,生命周期是随内核的,如果程序员不显示的释放,那么内核存在多长时间,它就存在多长时间

key vs shimd
key:只是用来在系统层面上进行标识唯一性的,不能用来管理shm
shmid:是OS给用户返回的id,用来在用户层进行shm管理

buf的类型是一个描述共享内存的数据结构,我们可以看一看这个结构里面有什么
在这里插入图片描述

2.2 shmctl函数

在这里插入图片描述
功能:用于控制共享内存
参数

  • shmid:由shmget返回的共享内存标识码
  • cmd:将要采取的动作(有三个可取值)
  • buf:指向一个保存着共享内存的模式状态和访问权限的数据结构

返回值:成功返回0;失败返回-1

comm.c
#pragma once#include<stdio.h>
#include<sys/ipc.h>
#include<sys/shm.h>#define PATH_NAME "./"
#define PROJ_ID 0X6666
#define SIZE 4097server.c
#include "comm.h"int main()
{key_t key = ftok(PATH_NAME, PROJ_ID);if(key < 0){perror("ftok");return 1;}int shmid = shmget(key, SIZE, IPC_CREAT|IPC_EXCL);if(shmid < 0){perror("shmget");return 2;}printf("key: %u, shmid: %d\n", key, shmid);sleep(5);shmctl(shmid, IPC_RMID, NULL);printf("key: 0x%x, shmid: %d->shm delete success\n", key, shmid);sleep(5);return 0;
}

运行代码:

在这里插入图片描述

2.3 shmat函数

在这里插入图片描述
功能:将共享内存段连接到进程地址空间
参数

  • shmid: 共享内存标识
  • shmaddr:指定连接的地址
  • shmflg:它的两个可能取值是SHM_RND和SHM_RDONLY

返回值:成功返回一个指针,指向共享内存第一个字节,就是共享内存的起始地址(这个地址是虚拟地址);失败返回-1
注意:shmaddr为NULL,OS自动选择一个地址
shmaddr不为NULL且shmflg无SHM_RND标记,则以shmaddr为连接地址。
shmaddr不为NULL且shmflg设置了SHM_RND标记,则连接的地址会自动向下调整为SHMLBA的整数倍。公式:shmaddr - (shmaddr % SHMLBA)
shmflg=SHM_RDONLY,表示连接操作用来只读共享内存

int main()
{key_t key = ftok(PATH_NAME, PROJ_ID);if(key < 0){perror("ftok");return 1;}int shmid = shmget(key, SIZE, IPC_CREAT|IPC_EXCL|0666);if(shmid < 0){perror("shmget");return 2;}printf("key: %u, shmid: %d\n", key, shmid);sleep(5);char* mem = (char*)shmat(shmid, NULL, 0);printf("attaches shm success\n");sleep(5);//这里就是我们的通信逻辑shmctl(shmid, IPC_RMID, NULL);printf("key: 0x%x, shmid: %d->shm delete success\n", key, shmid);return 0;
}

运行代码:
在这里插入图片描述
我们发现nattch(有多少个进程与之相关联)最终有0变为了1,表明已经将该进程挂接到了shmid为4的共享内存上了

2.4 shmdt函数

在这里插入图片描述
功能:将共享内存段与当前进程脱离
参数

  • shmaddr: 由shmat所返回的指针(共享内存的起始地址)

    返回值:成功返回0;失败返回-1
    注意:将共享内存段与当前进程脱离不等于删除共享内存段

#include "comm.h"int main()
{key_t key = ftok(PATH_NAME, PROJ_ID);if(key < 0){perror("ftok");return 1;}int shmid = shmget(key, SIZE, IPC_CREAT|IPC_EXCL|0666);if(shmid < 0){perror("shmget");return 2;}printf("key: %u, shmid: %d\n", key, shmid);sleep(5);char* mem = (char*)shmat(shmid, NULL, 0);printf("attaches shm success\n");sleep(5);//这里就是我们的通信逻辑shmdt(mem);printf("detaches shm success\n");sleep(5);shmctl(shmid, IPC_RMID, NULL);printf("key: 0x%x, shmid: %d->shm delete success\n", key, shmid);return 0;
}

运行代码:
在这里插入图片描述
我们发现nattch先是由0变为了1,再由1变为了0,说明该进程先挂接到了共享内存上,然后又取消了挂接

再看这样一段代码:

comm.h
#pragma once#include<stdio.h>
#include<sys/ipc.h>
#include<sys/shm.h>
#include<unistd.h>#define PATH_NAME "./"
#define PROJ_ID 0X6666
#define SIZE 4097client.c
#include "comm.h"int main()
{key_t key = ftok(PATH_NAME, PROJ_ID);if(key < 0){perror("ftok");return 1;}printf("%u\n", key);//client这里只需要获取共享内存即可,因为它在server端已经被创建了int shmid = shmget(key, SIZE, IPC_CREAT);if(shmid < 0){perror("shmget");return 1;}char* mem = (char*)shmat(shmid, NULL, 0);sleep(5);printf("client process attaches success!\n");//这个地方就是我们要通信的区域shmdt(mem);sleep(5);printf("client process detaches success!\n");//client要不要删除共享内存呢? 不需要return 0;
}srerver.c
#include "comm.h"int main()
{key_t key = ftok(PATH_NAME, PROJ_ID);if(key < 0){perror("ftok");return 1;}int shmid = shmget(key, SIZE, IPC_CREAT|IPC_EXCL|0666);if(shmid < 0){perror("shmget");return 2;}printf("key: %u, shmid: %d\n", key, shmid);sleep(1);char* mem = (char*)shmat(shmid, NULL, 0);printf("attaches shm success\n");sleep(15);//这里就是我们的通信逻辑shmdt(mem);printf("detaches shm success\n");sleep(5);shmctl(shmid, IPC_RMID, NULL);printf("key: 0x%x, shmid: %d->shm delete success\n", key, shmid);return 0;
}

改代码的意思就是先让server端创建一个共享内存,自己再挂接到共享内存上,client端再挂接到共享内存上,然后client端取消挂接,server端再取消挂接。我们将看到nattch的变化过程为0->1->2->1->0
在这里插入图片描述

重点:

1.共享内存一旦建立好并映射进自己的进程地址空间,该进程就可以直接看到该共享内存,就如同malloc的空间一样,不需要任何系统调用接口
2.共享内存不提供任何同步或者互斥机制,需要程序员自行保证数据的安全性
3.共享内存是进程之间通信最快的方式,因为它省略了若干次数据拷贝,例如从用户到内核和内核到用户

4、消息队列

消息队列是消息的链表,是存放在内核中并由消息队列标识符标识。因此是随内核持续的,只有在内核重起或者显示删除一个消息队列时,该消息队列才会真正被删除。消息队列克服了信号传递信息少,管道只能承载无格式字节流以及缓冲区受限等特点。允许不同进程将格式化的数据流以消息队列形式发送给任意进程,对消息队列具有操作权限的进程都可以使用msgget完成对消息队列的操作控制,通过使用消息类型,进程可以按顺序读信息,或为消息安排优先级顺序。

与信号量相比,都以内核对象确保多进程访问同一消息队列。但消息队列发送实际数据,信号量进行进程同步控制。与管道相比,管道发送的数据没有类型,读取数据端无差别从管道中按照前后顺序读取;消息队列有类型,读端可以根据数据类型读取特定的数据。操作:创建或获取消息队列, int msgget((key_tkey, int flag);//若存在获取,否则创建它发送消息:int msgsnd(int msgid, void *ptr, size_t size, int flag); ptr指向一个结构体存放类型和数据 size 数据的大小接受消息:int msgrcv(int msgid, void *ptr, size_t size, long type, int flag);删除消息队列: int msgctl(int msgid, int cmd, struct msgid_ds*buff);

5、信号量

管道,共享内存,消息队列,它们都是以传输数据为目的的!
信号量不是以传输数据为目的!它是通过共享"资源"的方式,来达到多个进程的同步和互斥的目的!
信号量的本质:是一个计数器,类似int count。衡量临界资源中的资源数

1.什么是临界资源?

凡是被多个执行流同时能够访问的资源就是临界资源,例如进程间通信时,使用的管道、共享内存、消息队列都是临界资源。凡是需要进程通信,必定要引入多个进程看到的资源(通信需要),同时,也引入了一个新的问题,那就是临界资源

2.什么是临界区?

用来访问临街资源的代码就叫做临界区

3.什么是原子性?

任何情况下不能被打断的操作,也就是说要么都执行,要么不执行,没有中间态

4.什么是互斥?

在任意一个时刻,只能允许一个执行流进入临界资源,执行它自己的临界区

信号量的先关操作:

int semget((key_t)key, int nsems, int flag);//创建或获取信号量int semop(int semid, stuct sembuf*buf, int length);//加一操作(V操作):释放资源;减一操作(P操作):获取资源int semct(int semid, int pos, int cmd);//初始化和删除

http://chatgpt.dhexx.cn/article/fQFY8gKN.shtml

相关文章

【操作系统】进程通信

什么是进程通信&#xff1f;进程为什么需要通信&#xff1f; 进程通信&#xff1a;进程通信就是进程之间的信息交换。 进程通信的目的&#xff1a; 数据传输&#xff1a;一个进程需要将它的数据发送给另一个进程。通知事件&#xff1a;一个进程需要向另一个或一组进程发送消息…

【Linux】进程通信之管道通信详解

&#x1f34e;作者&#xff1a;阿润菜菜 &#x1f4d6;专栏&#xff1a;Linux系统编程 文章目录 一、什么是管道通信1. 管道通信是一种在进程间传递数据的方法2.看看接口&#xff1a;匿名管道和命名管道3. 管道通信的本质是什么&#xff1f; 二、管道通信的实现和深入理解1.如何…

进程通信方式总结与盘点

​ 进程通信是指进程之间的信息交换。这里需要和进程同步做一下区分&#xff0c;进程同步控制多个进程按一定顺序执行&#xff0c;进程通信是一种手段&#xff0c;而进程同步是目标。从某方面来讲&#xff0c;进程通信可以解决进程同步问题。 ​ 首先回顾下我们前面博文中讲到…

《操作系统》实验报告——进程通信

理论知识 Linux——Linux C语言编程基础知识 Linux——进程通信 一、实验目的 (1) 熟悉并掌握管道机制&#xff0c;并实现进程间通信 (2) 熟悉并掌握共享内存机制&#xff0c;并实现进程间通信 二、实验内容 任务一&#xff1a; &#xff08;1&#xff09;阅读以上父子…

多进程通信

多进程&#xff1a; 首先&#xff0c;先来讲一下fork之后&#xff0c;发生了什么事情。 由fork创建的新进程被称为子进程&#xff08;child process&#xff09;。该函数被调用一次&#xff0c;但返回两次。两次返回的区别是子进程的返回值是0&#xff0c;而父进程的返回值则…

进程间通信详解

目录 一.进程间通信介绍 1.进程间通信的目的 2.进程间通信的本质 3.进程间通信分类 二.什么是管道 三.匿名管道 1. 匿名管道只能用于具有亲缘关系的进程之间进行通信&#xff0c;常用于父子。 2.pipe函数 3. 匿名管道的使用 4.管道的读写…

QProcess实现进程通信

QProcess实现进程通信的方式有点类似于管道。 QProcess父进程通过write来写入标准输入stdin&#xff0c;通过ReadyRead信号来接收子进程的消息。 QProcess子进程通过QFile来读取标准输入来接收父进程信息。通过QFile绑定QSocketNotifier来接收标准输入的实时信号&#xff0c;…

进程通信原理

目录 进程通信原理 1、同主机间的消息通讯机制 1.1 管道&#xff08;pipe&#xff09;,流管道(s_pipe)和有名管道&#xff08;FIFO&#xff09; 1.2 信号&#xff08;signal&#xff09; 1.3 消息队列 1.4 共享内存 信号量 套接字&#xff08;socket) 进程间通信各种方式效率比…

Linux进程通信

笔者在学习linux的过程中对linux进程通信进行记录学习。现在在 Linux 中使用较多的进程间通信方式主要有以下几种。 &#xff08;1&#xff09;管道&#xff08;Pipe&#xff09;及有名管道&#xff08;named pipe&#xff09;&#xff1a;管道可用于具有亲缘关系进程间的通信…

【Linux】进程间通信

目录 1. 进程间通信 1.1. 进程间通信的目的 1.2. 如何实现进程间通信 2. 管道通信 2.1. 匿名管道 2.1.1 创建匿名管道 2.1.2 . 深入理解匿名管道 2.2. 命名管道 2.2.1. 创建命名管道 3. system V 标准进程间通信 3.1. 共享内存 3.1.1. 实现原理 3.1.2. 代码实现 3…

认知智能理论三体论介绍简介

三体论是探索研究宇宙&#xff0c;信息和人类大脑三者关系的理论体系。是认知智能的奠基理论体系之一。宇宙和信息&#xff0c;信息和人类大脑&#xff0c;人类大脑和宇宙&#xff0c;三者之间存在着某种未被完全揭示的奥秘。三体论的核心思想体系就是基于宇宙&#xff0c;信息…

认知智能三大技术体系之类脑模型简介

认知智能三大技术体系之类脑模型简介 类脑模型是认知智能核心三大技术体系之一&#xff0c;类脑结构&#xff0c;功能机制的总称。类脑模型技术体系核心指导思想来自认知智能三大奠基理论&#xff0c;脑科学&#xff0c;心理学&#xff0c;逻辑学&#xff0c;情感学&#xff0c…

认知智能CI和人工智能AI的区别介绍简介

认知智能CI和人工智能AI的区别 人工智能和认知智能都是计算机科学的分支学科之一。人工智能是智能时代的第二个阶段&#xff0c;认知智能是智能时代的第三个阶段。认知智能也并不是智能时代发展的最终阶段&#xff0c;最终阶段应该是通用智能强智能时代。 本文主要就人工智能和…

什么是认知智能?

认知智能是计算机科学的一个分支科学&#xff0c;是智能科学发展的高级阶段&#xff0c;它以人类认知体系为基础&#xff0c;以模仿人类核心能力为目标&#xff0c;以信息的理解、存储、应用为研究方向&#xff0c;以感知信息的深度理解和自然语言信息的深度理解为突破口&#…

认知智能是什么?

认知智能是计算机科学的一个分支科学&#xff0c;是智能科学发展的高级阶段&#xff0c;它以人类认知体系为基础&#xff0c;以模仿人类核心能力为目标&#xff0c;以信息的理解、存储、应用为研究方向&#xff0c;以感知信息的深度理解和自然语言信息的深度理解为突破口&#…

人工智能导论

目录 1.1 人工智能导论 1.2 人工智能应用 1.3 人工智能产业发展 1.4 人工智能发展的成功因素 1.5 人工智能迅速发展的技术领域 1.6人工智能的基础知识 1.1 人工智能导论 人工智能的三个层面&#xff1a; 计算智能&#xff1a;能算能存 感知智能&#xff1a;能听会说&…

如何用AI技术增强企业认知智能?超详细架构解读

认知的高度决定了创造价值的高度。 企业在从创办、发展、竞争、成功到衰亡的全生命周期中&#xff0c;会面临复杂多样的决策场景。 然而&#xff0c;时代演变产生的海量、分散、实时的信息&#xff0c;仅靠人类个体是难以高效、准确地感知、认知和决策的。 因此&#xff0c;企…