文章目录

  • 进程间通信——共享内存/守护进程
    • 一, 共享内存
      • 1. 共享内存概念
      • 2. 共享内存使用
        • 1. 共享内存使用步骤
        • 2. 共享内存操作函数
        • 3. 共享内存常用操作命令
        • 4. 共享内存使用示例:
        • 5. 共享内存使用总结
    • 二, 守护进程
      • 1. 终端
      • 2. 进程组
      • 3. 会话
      • 4. 进程组、会话等相关操作函数
      • 5. 守护进程
        • 5.1 创建守护进程
        • 5.2 创建守护进程示例
    • 三, 消息队列
      • 1. 什么是消息队列
      • 2. 消息队列相关操作函数
      • 3. 消息队列使用示例

进程间通信——共享内存/守护进程

共享内存是效率最高的进程间通信方式。

一, 共享内存

1. 共享内存概念

共享内存允许两个或多个进程共享物理内存的同一块区域(通常被称为段)。由于一个共享内存段会成为一个进程用户空间的一部分,因此这种IPC机制无需内核介入。
我们需要做的就是让一个进程将数据复制进共享内存中,并且这部分数据会对其它所有共享同一个段的进程可用。

与管道等要求 发送进程将数据从用户空间的缓冲区复制进内核内存 和 接收进程将数据从内核内存复制进用户空间的缓冲区的做法相比,这种IPC技术速度更快。

但共享内存也会带来一些问题,比如,几个进程共享内存,那么可能进程A在写数据,进程B也在写数据,进程C读数据,那么就会出现冲突,进程C最终读出来的数据到底是什么呢?涉及到进程同步的问题。

共享内存存储在用户区的共享库所在空间,如下:

2. 共享内存使用

1. 共享内存使用步骤

  1. 创建shmget:调用shmget()创建一个新的共享内存段或取得一个既有共享内存段的标识符(由其它进程创建的共享内存段)。这个调用将返回后续调用中需要用到的共享内存标识符。
  2. 链接shmat:使用shmat()附加共享内存段到当前的调用进程,使该段成为调用进程的虚拟内存的一部分。
  3. 操作:此刻在程序中可以像对待其它可用内存那样对待这个共享内存段。为引用这块共享内存,程序需要使用由shmat()调用返回的addr值,它是一个指向进程的虚拟地址空间中该共享内存段的起点的指针。
  4. 分离shmdt:调用shmdt()来分离共享内存段,在这个调用之后,进程就无法再引用这块共享内存了。这一步是可选的,并且在进程终止时会自动完成这一步。
  5. 删除shmctl():调用shmctl()来删除共享内存段。只有当当前所有附件内存段的进程都与之分离之后内存段才会销毁,只有一个进程需要执行这一步。

2. 共享内存操作函数

共享内存操作函数有如下:

int shmget(key_t key, size_t size, int shmflg);  // 创建或获取共享内存
void *shmat(int shmid, const void *shmaddr, int shmflg);  // 共享内存链接到当前调用进程
int shmdt(const void *shmaddr);   // 将当前进程与共享内存分离
int shmctl(int shmid, int cmd, struct shmid_ds *buf);  // 对共享内存操作,设置/获取/删除共享内存
key_t ftok(const char *pathname, int proj_id);  // 生成共享key,创建或获取共享内存时会使用该函数生成的key

shmget()函数说明:

头文件 #include <sys/ipc.h>
#include <sys/shm.h>
函数声明 int shmget(key_t key, size_t size, int shmflg);
功能 创建一个新的共享内存段,或者获取一个既有的共享内存段的标志
新创建的内存段中的数据都会被初始化为0
参数key key_t类型,通过该参数找到或创建一个共享内存。
一般使用16进制表示,非0值
参数size 共享内存的大小,以分页大小创建
参数shmflg 共享内存的属性,如访问权限、附加属性(创建/判断共享内存是不是存在)等
IPC_CREATE:创建共享内存
IPC_EXCL:判断共享内存是否存在,需要和IPC_CREATE一起使用,如:IPC_EXCL
返回值 成功返回>0的值,表示共享内存标识符,失败返回-1并设置errno

shmat()函数说明:

头文件 #include <sys/ipc.h>
#include <sys/shm.h>
函数声明 void *shmat(int shmid, const void *shmaddr, int shmflg);
功能 将共享内存与当前调用进程相关联
参数shmid 共享内存标识符,由shmget()返回
参数shmaddr 申请的共享内存的起始地址,设置为NULL,由内核指定
参数shmflg 对共享内存的操作
SHM_RDONLY:读权限,必须要有读权限
0:读写权限
返回值 成功返回共享内存的首地址,失败返回(void*)-1并设置errno

shmdt()函数说明:

头文件 #include <sys/ipc.h>
#include <sys/shm.h>
函数声明 int shmdt(const void *shmaddr);
功能 解除当前进程和共享内存的关联
参数shmaddr 共享内存的首地址
返回值 成功返回0,失败返回-1并设置errno

shmctl()函数说明:

头文件 #include <sys/ipc.h>
#include <sys/shm.h>
函数声明 int shmctl(int shmid, int cmd, struct shmid_ds *buf);
功能 对共享内存的操作(获取状态,设置状态、标记销毁),创建共享内存的进程被销毁对共享内存没有影响
参数shmid 共享内存标识符ID
参数cmd 对共享内存的操作
IPC_STAT:获取共享内存的当前状态
IPC_SET:设置共享内存的状态
IPC_RMID:标记共享内存被销毁
参数buf 需要设置或者获取的共享内存的属性,buf设置与参数cmd有关,当cmd为:
IPC_STAT:buf存储数据
IPC_SET:buf需要初始化数据,设置到内核中
IPC_RMID:buf没有用,设置NULL即可
返回值 成功返回0,失败返回-1并设置errno

当参数cmd设置为IPC_STAT或IPC_SET时,用到第三个参数buf,是结构体指针struct shmid_ds*,该结构体声明如下:

        struct shmid_ds {struct ipc_perm shm_perm;    /* Ownership and permissions */size_t          shm_segsz;   /* Size of segment (bytes) */time_t          shm_atime;   /* Last attach time */time_t          shm_dtime;   /* Last detach time */time_t          shm_ctime;   /* Last change time */pid_t           shm_cpid;    /* PID of creator */pid_t           shm_lpid;    /* PID of last shmat(2)/shmdt(2) */shmatt_t        shm_nattch;  /* No. of current attaches */...};

ftok()函数说明:

头文件 #include <sys/ipc.h>
#include <sys/types.h>
函数声明 key_t ftok(const char *pathname, int proj_id);
功能 根据指定的路径名和proj_id值生成一个共享内存key
参数pathname 指定一个存在的路径
参数proj_id 系统调用只使用其中的1个字节,一般指定一个字符,例如可以传递 'a'
返回值 成功返回生成的key,失败返回-1并设置errno

3. 共享内存常用操作命令

  • ipc命令

    • ipcs -a:打印当前系统中所有的进程键通信方式的信息
    • ipcs -m:打印使用共享内存进行进程间通信的信息
    • ipc -q:打印使用消息队列进行进程间通信的信息
    • ipc -s:打印使用信号量进行进程间通信的信息
  • ipcrm命令

    • ipcrm -M shmkey:移除用shmkey创建的共享内存段
    • ipcrm -m shmid:移除用shmid标识的共享内存段
    • ipcrm -Q msgkey:移除用msqkey创建的消息队列
    • ipcrm -q msgid:移除用msqid标识的消息队列
    • ipcrm -S semkey:移除semkey创建的信号量
    • ipcrm -s semid:移除用semid标识的信号量

4. 共享内存使用示例:

示例1:创建两个进程,一个向共享内存中写数据write_shm.c,一个从共享内存中读数据read_shm.c。

read_shm.c

/*** @file read_shm.c* @author zoya (2314902703@qq.com)* @brief 从共享内存中读取数据* @version 0.1* @@date: 2022-10-03** @copyright Copyright (c) 2022**/#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>int main()
{// 获取keykey_t key = ftok("/home/zoya/Linux/chap2/lesson14/key", 'a');if (key == -1){perror("ftok");exit(-1);}// 获取共享内存int shmid = shmget(key, 0, IPC_CREAT);if (shmid == -1){perror("shmget");exit(-1);}//  共享内存与当前进程关联void *ptr = shmat(shmid, NULL, SHM_RDONLY);if (ptr == (void *)-1){perror("shmat");exit(-1);}// 操作共享内存中的数据,即读取共享内存数据while (1){if(ptr == NULL)continue;printf("read : %s\n", (char *)ptr);sleep(2);}//  分离当前进程与共享内存的关联int ret = shmdt(ptr);if (ret == -1){perror("shmdt");exit(-1);}// 删除共享内存ret = shmctl(shmid, IPC_RMID, NULL);if (ret == -1){perror("shmctl");exit(-1);}return 0;
}

write_shm.c

/*** @file read_shm.c* @author zoya (2314902703@qq.com)* @brief 向共享内存中写数据* @version 0.1* @@date: 2022-10-03** @copyright Copyright (c) 2022**/#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>int main()
{// 获取keykey_t key = ftok("/home/zoya/Linux/chap2/lesson14/key", 'a');if (key == -1){perror("ftok");exit(-1);}// 获取共享内存int shmid = shmget(key, 4096, IPC_CREAT | 0664);if (shmid == -1){perror("shmget");exit(-1);}//  共享内存与当前进程关联void *ptr = shmat(shmid, NULL, 0); // 读写模式与当前进程关联if (ptr == (void *)-1){perror("shmat");exit(-1);}// 操作共享内存中的数据,即读取共享内存数据char str[1024] = {0};long int num = 0;while (1){memset(str, 0, sizeof(str));sprintf(str, "hello, %ld", num++);memcpy((char *)ptr, str, strlen(str));printf("按任意键继续\n");getchar();}//  分离当前进程与共享内存的关联int ret = shmdt(ptr);if (ret == -1){perror("shmdt");exit(-1);}// 删除共享内存ret = shmctl(shmid, IPC_RMID, NULL);if (ret == -1){perror("shmctl");exit(-1);}return 0;
}

程序运行:

示例2:命令ipcs -q查看进程间使用共享内存通信的个数,发现有很多,并且键为0。要删除这些用不到的共享内存,思路:

  • 首先获取是哪些进程用到了共享内存,根据shmid使用shmctl()函数可以获取进程pid;
  • 然后使用kill -9 pid杀死进程即可;

    参考代码:
/**
* @file rmshmid.c
* @author zoya (2314902703@qq.com)
* @brief 根据指定的shmid杀死对应的进程
* @version 0.1
* @@date: 2022-10-03
*
* @copyright Copyright (c) 2022
*
*/#include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>int main(int argc, char *argv[]){// 根据shmid获取进程idint shmid = atoi(argv[1]);struct shmid_ds buf;shmctl(shmid,IPC_STAT,&buf);printf("shmid=%d 对应的 pid=%d, last oppid=%d , uid=%d\n",shmid,buf.shm_cpid,buf.shm_lpid,buf.shm_perm.cuid);return 0;
}

获取到进程pid后使用kill命令杀死对应的进程即可。

5. 共享内存使用总结

1. 操作系统如何知道共享内存被多少个进程关联?
从内核来看,共享内存维护结构体struct shmid_ds,该结构体中有一个成员shm_nattach,shm_attach记录了关联的进程个数。
2. 是否可以对共享内存进行多次删除 shmctl
可以,shmctl只是标记共享内存,不是直接删除;
当和共享内存关联的进程数为0时,共享内存被真正删除;
当共享内存的key变为0时,共享内存被标记删除了;
如果一个进程和共享内存取消关联,该进程就不能继续操作共享内存,也不能进行再次关联;
3. 共享内存和内存映射的区别

  1. 共享内存可以直接创建,内存映射需要磁盘文件(匿名映射除外);
  2. 共享内存效率更高;
  3. 内存:
    共享内存:所有进程操作的是同一块共享内存;
    内存映射:每个进程在自己的虚拟地址空间有一个独立的空间;
  4. 数据安全
    1. 进程突然退出:共享内存还存在;内存映射区小时
    2. 运行进程的电脑宕机:数据存在共享内存中,就没有了;内存映射:由于磁盘文件中的数据还在,所以内存映射区中的数据还存在
  5. 生命周期
    1. 内存映射:进程退出,内存映射区销毁
    2. 共享内存:进程退出,共享内存还在,需要手动删除(所有关联的进程数为0也可以删除)或者关机;如果进程退出,会自动和共享内存取消关联。

二, 守护进程

先了解下终端、进程组、会话的概念。

1. 终端

UNIX/Linux中,用户通过终端登录系统后得到一个shell进程,这个终端称为shell进程的控制终端。进程中,控制终端是保存在PCB中的信息,而fork()会复制PCB中的信息,因此由shell()进程启动的其它进程的控制终端也是这个终端。

默认情况下,每个进程的标准输入、标准输出和标准错误都指向控制终端,进程从标准输入读出就是读用户的键盘输入,进程标准输出或标准错误输出是输出到显示器上。

在控制终端输入一些特殊的控制键可以给前台进程发信号,例如 CTRL+C 会产生SIGINT信号,Ctrl+\会产生SIGQUIT信号。

echo $$ 可以查看当前终端的进程id
.\a.out & 以后台进程运行

2. 进程组

进程组:很多进程的集合。

进程组和会话在进程之间形成了一种两级层次关系;进程组是一组相关进程的集合,会话是一组相关进程组的集合。
进程组和会话是为支持shell作业控制而定义的抽象概念,用户通过shell能够交互式地在前台或后台运行命令。

进程组由一个或多个共享同一进程组标识符PGID的进程组成。一个进程组拥有一个进程组首进程,该进程是创建该组的进程,其进程ID为该进程组的ID,新进程会继承其父进程所属的进程组ID。

进程组拥有一个生命周期,其开始时间为首进程创建组的时刻,结束时间为最后一个成员进程推出组的时刻。一个进程可能会因为终止而退出进程组,也可能会因为加入了另外一个进程组而退出进程组。进程组首进程无需是最后一个离开进程组的成员。

3. 会话

会话是一组进程组的集合。会话首进程是创建该新会话的进程,其进程ID称为会话ID,新进程会继承其父进程的会话ID(SID)。

一个会话中的所有进程共享单个控制终端。控制终端在会话首进程首先打开一个终端设备时被建立,一个终端最多可能会称为一个会话的控制终端。

在任一时刻,会话中的其中一个进程组会成为终端的前台进程组,其它进程组会成为后台进程组。只有前台进程组中的进程才能从控制终端中读取输入。当用户在控制终端中输入终端字符生成信号后,该信号会被发送到前台进程组中的所有成员。

当控制终端的连接建立起来之后,会话首进程会成为该终端的控制进程。

find / 2 > /dev/null | wc -l & >是重定向,&表示是后台运行
sort < longlist | uniq -c

4. 进程组、会话等相关操作函数

pid_t getpgrp(void);  // 获取当前调用进程的进程组id
int setpgid(pid_t pid,pid_t pgid);  // 设置pid的进程组id为pgid;如果pid=0,使用当前调用进程的id为pid;如果pgid=0,则pid指定的pgid与pid一致
// 建议使用上面两个函数设置/获取进程组id
pid_t getpgid(pid_t pid);  // 获取指定的进程组IDpid_t getsid(pid_t pid);  // 获取指定pid的会话id
pid_t setsid(void);  // 如果当前调用进程不是进程组首进程,则创建会话;默认创建的会话没有控制终端

5. 守护进程

守护进程,也就是Daemon进程/精灵进程,是Linux中的后台服务进程,是一个生存期较长的进程,通常独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件,一般采用以d结尾的名字。

守护进程具备下列特征:

  • 生命周期很长,守护进程会在系统启动的时候被创建并一直运行直至系统被关闭。
  • 它在后台运行并且不拥有控制终端。没有控制终端确保了内核永远不会为守护进程自动生成任何控制信号以及终端相关的信号。

Linux的大多数服务器就是用守护进程实现的。比如,internet服务器inetd,web服务器httpd等。

5.1 创建守护进程

创建守护进程步骤(其中后面打√表示该步骤是必须操作的):

  1. 执行一个fork(),然后父进程退出,子进程继续执行; √
    目的是为了

    • 防止父进程结束后显示~指示符 ;
    • 使用fork()可以确保子进程ID不是所在进程组的进程组ID;
  2. 子进程调用setsid()开启一个新会话; √
    • 避免创建有冲突的会话;
  3. 清除进程的umask以确保当守护进程创建文件和目录时拥有所需的权限;
  4. 修改进程的当前工作目录;
  5. 关闭守护进程从其父进程继承来的所有打开的文件描述符;
  6. 在关闭了文件描述符0、1、2之后,守护进程通常会打开/dev/null,并使用dup2()使所有这些文件描述符指向这个设备;
  7. 核心业务逻辑 √

5.2 创建守护进程示例

/*** @file daemon.c* @author zoya (2314902703@qq.com)* @brief 创建一个守护进程,每隔2s打印当前时间* @version 0.1* @@date: 2022-10-04** @copyright Copyright (c) 2022**/#define _XOPEN_SOURCE#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <time.h>void my_alarm(int signum)
{// 打印时间time_t t1 = time(NULL);struct tm *t = localtime(&t1);char *str = asctime(t);int fd = open("time.txt", O_CREAT | O_RDWR | O_APPEND, 0664);write(fd, str, strlen(str));close(fd);
}int main()
{// 创建子进程,父进程退出,子进程继续执行pid_t pid = fork();if (pid > 0){printf("父进程,退出!\n");exit(0);}// 子进程开启一个新会话pid_t sid = setsid();if (sid == (pid_t)-1){perror("setsid");exit(-1);}// 清除进程的umaskumask(022);//  修改进程的当前工作目录chdir("/home/zoya");// 关闭守护进程从其父进程继承来的所有打开的文件描述符// 父进程没有打开文件描述符// 重定向到/dev/nullint fd = open("/dev/null", O_RDWR);dup2(fd, STDIN_FILENO);dup2(fd, STDOUT_FILENO);dup2(fd, STDERR_FILENO);// 核心业务逻辑// 捕捉定时器信号struct sigaction act;act.sa_flags = 0;act.sa_handler = my_alarm;sigemptyset(&act.sa_mask);sigaction(SIGALRM, &act, NULL);// 设置定时器,1s后每隔2s定时一次struct itimerval new;new.it_interval.tv_sec = 2;new.it_interval.tv_usec = 0;new.it_value.tv_sec = 1;new.it_value.tv_usec = 0;setitimer(ITIMER_REAL, &new, NULL);// 进程持续运行while (1){sleep(5);}return 0;
}

三, 消息队列

1. 什么是消息队列

  • 消息队列也称为报文队列,也是Linux的一种通信机制。消息队列传递的数据具有某种结构,不是简单的字节流;每个数据结构被认为含有一个类型,接收进程可以独立地接收含有不同类型的数据结构
  • Linux使用宏MSGMAX和MSGMNB限制一条消息的最大长度和一个队列的最大长度。
  • 消息队列的本质是一个内核提供的链表,内核基于这个链表,实现了一个数据结构;
  • 向消息队列中写数据,实际是向这个数据结构中插入了一个新结点;从消息队列中读数据,实际是从这个数据结构中删除一个结点;
  • 消息队列提供了从一个进程向另外一个进程发送一块数据的方法;
  • 消息队列每个数据块的最大长度是有上限的,系统上的全体队列的最大总长度也有一个上限;

进程间使用消息队列进行通信时通过key队列标识码建立连接。

无论是发送数据的进程还是接收数据的进程,都需要在进程空间中用消息缓冲区来暂存消息。该消息缓冲区的结构定义如下:

struct msgbuf{long mtype;  // 消息的类型char mtext[1];  // 消息正文
};

mtype用来区分数据类型,同时判断mtext是否为需要接收的数据;
mtext为存放消息正文的数组,可以根据消息的大小定义该数组的长度;

2. 消息队列相关操作函数

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>int msgget(key_t key,int msgflg);
int msgsnd(int msgid, const void *msgp, size_t msgsz, int msgflg);
ssize_t msgrcv(int msgid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
int msgctl(int msgid,int command,struct msgid_ds *buf);

msgget()函数说明

头文件 #include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
函数声明 int msgget(key_t key,int msgflg);
函数功能 创建消息队列
参数key 根据key创建消息队列,key可以指定或者由ftok()函数获取
参数msgflg 创建的消息队列的权限,与文件的访问权限一样
返回值 成功返回非负整数,是创建的消息队列的标识码
失败返回-1

msgsnd()函数说明:

头文件 #include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
函数声明 int msgsnd(int msgid, const void *msgp, size_t msgsz, int msgflg);
函数功能 发送消息队列
参数msgid msgget函数返回的消息队列标识码
参数msg_ptr 指针,指向准备发送的消息,必须是以一个长整型成员变量开始的结构体,接收函数将用这个成员确定消息的类型,如上面的结构体msgbuf
参数msg_sz msg_ptr指向的消息长度,消息缓冲区结构体中text的大小,不包括数据的类型
参数msgflg 控制当前消息队列满或到达上限时将要发生的事情
IPC_NOWAIT表示队列满不等待,返回EAGAIN错误
返回值 成功返回0,消息数据会被放到消息队列中,失败返回-1

msgrcv()函数说明:

头文件 #include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
函数声明 ssize_t msgrcv(int msgid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
函数功能 从消息队列中获取消息
参数msgid msgget函数返回的消息队列标识码
参数msg_ptr 指针,指向准备接收的消息
参数msgsz msg_ptr指向的消息长度,消息缓冲区结构体中text的大小,不包括数据的类型
参数msgtyp 可以实现接收优先级的简单形式
=0 获取消息队列中的第一个消息
>0 获取具有相同消息类型的第一个信息
<0 获取类型等于或小于msgtype的绝对值的第一个消息
参数msgflg 控制队列中没有相应类型的消息可供接收时将要发生的事情
IPC_NOWAIT表示对于没有可读消息不等待,返回ENOMSG错误
MSG_NOERROR表示消息大小超过msgsz时被截断
返回值 成功返回接收的字节数,消息被复制到由msg_ptr指向的用户分配的缓存区中,然后删除消息队列中的对应消息
失败返回-1

msgctl()函数说明:

头文件 #include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
函数功能 控制消息队列,与共享内存的shmctl类似
函数声明 int msgctl(int msgid, int command, struct msgid_ds *buf);
参数msgid msgget函数返回的消息队列标识码
参数command 对消息队列要采取的动作
IPC_STAT:把msgid_ds结构中的数据设置为消息队列的当前关联值
IPC_SET:在进程有足够权限的前提下,把消息队列的当前关联值设置为msgid_ds数据结构中给出的值
IPC_RMID:删除消息队列,如果选择删除消息队列,第三个参数传递NULL
参数buf 指向msgid_ds结构的指针,表示消息队列模式和访问权限,msgid_ds结构如下
返回值 成功返回0,失败返回-1
struct msqid_ds {struct ipc_perm msg_perm;     /* Ownership and permissions */time_t          msg_stime;    /* Time of last msgsnd(2) */time_t          msg_rtime;    /* Time of last msgrcv(2) */time_t          msg_ctime;    /* Time of last change */unsigned long   __msg_cbytes; /* Current number of bytes inqueue (nonstandard) */msgqnum_t       msg_qnum;     /* Current number of messagesin queue */msglen_t        msg_qbytes;   /* Maximum number of bytesallowed in queue */pid_t           msg_lspid;    /* PID of last msgsnd(2) */pid_t           msg_lrpid;    /* PID of last msgrcv(2) */};
struct ipc_perm {key_t          __key;       /* Key supplied to msgget(2) */uid_t          uid;         /* Effective UID of owner */gid_t          gid;         /* Effective GID of owner */uid_t          cuid;        /* Effective UID of creator */gid_t          cgid;        /* Effective GID of creator */unsigned short mode;        /* Permissions */unsigned short __seq;       /* Sequence number */};

查看消息队列:
使用命令ipcs -q查看已经创建的消息队列,包括key值信息,id信息,拥有者信息,文件权限信息,已使用的字节数和消息条数。
使用命令ipcrm -Q加消息队列的key值用来删除一个消息队列。

3. 消息队列使用示例

read_msgqueue.c

/*** @file read_msgqueue.c* @author zoya (2314902703@qq.com)* @brief 从消息队列中读取消息* @version 0.1* @@date: 2022-10-04** @copyright Copyright (c) 2022**/#include <stdio.h>
#include <sys/types.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <fcntl.h>struct MSG
{long mtype;char mtext[2048];
};int main()
{// 获取keykey_t key = ftok("/home/zoya/Linux/chap2/lesson16/test", 'b');printf("key : 0x%x\n", key);// 创建消息队列int msgid = msgget(key, O_RDONLY);if (msgid == -1){perror("msgget");exit(-1);}// 从消息队列中读取消息struct MSG msgbuf;msgbuf.mtype = 1; // 消息类型while (1){msgrcv(msgid, &msgbuf, sizeof(msgbuf.mtext), msgbuf.mtype, 0);printf("receive msg : [%s]\n", msgbuf.mtext);printf("按enter键继续,按0退出\n");if ('0' == getchar())break;msgbuf.mtype++;msgbuf.mtype %= 4;if (msgbuf.mtype == 0)msgbuf.mtype++;}//  删除消息队列msgctl(msgid, IPC_RMID, NULL);return 0;
}

write_msgqueue.c

/*** @file read_msgqueue.c* @author zoya (2314902703@qq.com)* @brief 向消息队列发送消息* @version 0.1* @@date: 2022-10-04** @copyright Copyright (c) 2022**/#include <stdio.h>
#include <sys/types.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <fcntl.h>struct MSG
{long mtype;char mtext[2048];
};int main()
{// 获取keykey_t key = ftok("/home/zoya/Linux/chap2/lesson16/test", 'b');printf("key : 0x%x\n", key);// 创建消息队列int msgid = msgget(key, IPC_CREAT | O_WRONLY | 0777);if (msgid == -1){perror("msgget");exit(-1);}// 向消息队列发送消息struct MSG msgbuf;msgbuf.mtype = 1; // 消息类型char buf[2048];while (1){memset(buf,0,sizeof(buf));sprintf(buf, "key=0x%x, msgid=%d, type=%ld, msg : hello, i am writer!",key, msgid, msgbuf.mtype);printf("send msg: [%s]\n", buf);strcpy(msgbuf.mtext, buf);msgsnd(msgid, &msgbuf, sizeof(msgbuf.mtext), 0);printf("按enter键继续,按0退出\n");if ('0' == getchar())break;msgbuf.mtype++;msgbuf.mtype %= 4;if (msgbuf.mtype == 0)msgbuf.mtype++;}//  删除消息队列msgctl(msgid, IPC_RMID, NULL);return 0;
}

程序运行:

文章参考:

  1. https://blog.csdn.net/qq_42425882/article/details/105105209
  2. https://www.nowcoder.com/study/live/504/2/28

【Linux】Linux进程间通信——共享内存/消息队列/守护进程相关推荐

  1. Linux下进程间通信--共享内存:最快的进程间通信方式

    内存共享最新整理: Linux下进程间通信-共享内存 - 码到城攻共享内存可以说是最有用的进程间通信方式,也是最快的IPC形式https://www.codecomeon.com/posts/109/ ...

  2. 【Linux】进程间通信-共享内存

    前言 我们知道,在Linux中,进程是相互独立存在的,不存在直接让进程之间互相通信的方式.但是如果我们能让不同进程之间见到同一块内存,也就是都能读写这片区域是不是就能够达到进程间通信呢? 事实证明确实 ...

  3. 【Linux学习】进程间通信——system V(共享内存 | 消息队列 | 信号量)

  4. Linux无锁共享内存,优秀数据结构学习 - 共享内存无锁队列的实现(二)

    优秀数据结构学习 - 共享内存无锁队列的实现(二) 优秀数据结构学习 - 共享内存无锁队列的实现(二) 1 关键技术 操作系统提供的进程间通信机制有文件.socket.消息队列.管道.共享内存等.其中 ...

  5. Linux进程间通信(管道、消息队列、共享内存、信号、信号量)

    目录 Linux进程间通信概述 1.管道 无名管道(pipe) 有名管道(fifo) 2.消息队列(msg) 消息队列的通信原理 消息队列相关api 消息队列收发数据 键值生成 消息队列移除 3.共享 ...

  6. linux进程间通信:POSIX 消息队列 ----异步通信

    在上一篇中linux进程间通信:POSIX 消息队列我们知道消息队列中在消息个数达到了队列所能承载的上限,就会发生消息的写阻塞. 阻塞式的通信影响系统效率,进程之间在通信收到阻塞时并不能去做其他事情, ...

  7. linux进程间通信:POSIX 消息队列

    文章目录 基本介绍 相关编程接口 编程实例 消息队列通信实例 消息队列属性设置实例 基本介绍 关于消息队列的基本介绍,前面在学习system V的消息队列时已经有过了解,linux进程间通信:syst ...

  8. c++ fork 进程时 共享内存_c/c++ Linux 进程间通信------共享内存

    1. 什么是共享内存 共享内存(Shared Memory),指两个或多个进程共享一个给定的存储区.进程可以将同一段共享内存连接到它们自己的地址空间中,所有进程都可以访问共享内存中的地址,就好像它们是 ...

  9. Linux基础入门--进程间通信--共享内存

    Linux基础入门--进程间通信--共享内存 1.共享内存IPC原理 2.共享内存管理 1.共享内存IPC原理 共享内存进程间通信机制主要用于实现进程间大量的数据传输,共享内存是在内存单独开辟的一段内 ...

最新文章

  1. 微信开发文档笔记整理(一)
  2. JavScript中的循环
  3. .NET平台功能最强大,性能最佳的JSON库
  4. Canvas事件绑定
  5. Web前端技术历经的洗礼和蜕变
  6. LeetCode 250. 统计同值子树(递归)
  7. (2)树莓派挂载外部硬盘
  8. C++多继承的二义性
  9. maven 强制jdk的版本
  10. MySQL - 常见SQL笔试题整理(长期更新)
  11. 4.6 数值分析: P阶收敛的迭代法
  12. 测度论与概率论笔记6:符号测度
  13. 微信小程序开发者工具扫码成功但是进不去
  14. java base64转图片打不开_解决通过 Base64 解码得到的图片无法打开查看的问题
  15. scrcpy Device disconnected报错
  16. 二胎准生证办理流程--很有用。
  17. python 爬虫request ssl_Python request SSL证书问题
  18. 机器学习预测机动车摇号:神秘的第七位
  19. QueryRunner常用方法
  20. 项目管理100问 | NO.6 如何为项目制定里程碑?

热门文章

  1. 学习笔记のgetParametergetAttribute
  2. 华裔夫妇打造美国中式快餐王国
  3. 支付宝-转账到支付宝账号接口(新接口)
  4. [转载]虚拟主机上安装Mambo核心系统/组件/模块技巧
  5. 企业信息化管理软件定制开发
  6. Java后端学习笔记 -- JavaWeb(二):JavaScript
  7. 文献阅读——《元会的建构——中国古代帝国的朝政与礼仪》
  8. SQL Server——触发器
  9. 【Python】Windows如何在cmd中切换python版本
  10. MySQL新增字段报错:ERROR 1118 -- Row size too large. The maximum row size for the used table type