7.【进程间 通信方式】

1. 概念

  1. Linux进程通信的方法一共有五种,分别是:
    1. 管道、信号、信号灯集、共享内存、消息队列、套接字
  1. 作用:
    1. 数据传输、资源共享、通知事件、控制进程

linux进程通信看这一篇就够了【管道、信号量、共享内存、消息队列】(超级详细、不骗人)_linux 共享内存 信号量_WolfOnTheWay的博客-CSDN博客

1.1. 发展历史(了解)

  1. 早期通信
  2. AT&T的贝尔实验室,对Unix早期的进程间通信进行了改进和扩充,形成了"system V IPC",其通信进程主要 局限 在单个计算机内。【IPC:InterProcess Communication 译为:进程间通信 】
  3. BSD(加州大学伯克利分校的伯克利软件发布中心),跳过了只能在同一计算机通信的限制,形成了基于套接字(socket)的进程间通信机制

1.2. 进程间通信方式

  1. 早期通信:无名管道(pipe)、有名管道(fifo)、信号(sem)
  2. system V IPC:共享内存(shared memory)、信号灯集(semaphore)、消息队列(message queue)
  3. BSD:套接字(socket)(在网络编程中专门学习)

1.3. 原理

每个进程各自有不同的用户地址空间,任何一个进程的全局变量在另一个进程中都看不到,所以进程之间要交换数据必须通过内核,在内核中开辟一块缓冲区,进程1把数据从用户空间拷到内核缓冲区,进程2再从内核缓冲区把数据读走,内核提供的这种机制称为进程间通信机制


2. 早期通信:管道

2.1. 对比:无名管道 有名管道

有名管道

无名管道

使用场景

不相关进程

具有亲缘关系的进程

特点

1)通过文件IO进行操作

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

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

1)半双工的通信方式

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

3)看做一种特殊文件,通过文件IO操作

函数

mkfifo()

先打开open,再读写read/write

pipe()

直接read write

注意事项

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

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

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

1)当管道中没有数据时,读阻塞

2)当管道写满数据时,写阻塞

3)当管道中无数据,关闭写端,读立即返回

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

2.2. 管道 共性特点

  • 管道 分为 命名管道 和 无名管道
  • 管道(文件) 的大小 永远为0
  • 管道存在于 内存之中,不会 永久保存
  • 管道的 传送方式 是半双工的(一边进,一边出)
  • 无名管道通过函数pipe创建,只能用于父子进程之间
  • 有名管道可以通过mkfifo+文件名的方式在终端进行创建,有名管道可以在任意两个进程之间进行通信
  • 都是一个有简单流控制先进先出的队列自带同步机制

2.3. 无名管道 pipe

2.3.1. 说明:

  1. 无名管道可以看成是一种特殊的文件,对于它的读写可以使用文件IO如read、write函数。
  2. 无名管道 基于文件描述符的通信方式。当一个无名管道建立时,它会创建两个文件描述符fd[0]和fd[1]。其中fd[0]固定用于读管道,而fd[1]固定用于写管道

2.3.2. 函数:

int pipe(int fd[2])
功能:创建无名管道
参数:文件描述符 fd[0]:读端  fd[1]:写端
返回值:
	成功 0
    失败 -1

2.3.3. 无名管道 使用特点:

  1. 当管道中 无数据时,读操作会阻塞;

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

  1. 管道中装满(默认管道大小64K)数据时,写阻塞,一旦余有4k空间,写继续,直到写满为止,再满再循环。

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

2.4. 有名管道 fifo

2.4.1. 说明:

  1. 有名管道可以使 互不相关的两个进程 互相通信。
  2. 有名管道可以通过路径名来指出,并且在文件系统中可见,但内容存放在内存中
  3. 进程通过文件IO来操作有名管道
  4. 有名管道 遵循 先进先出规则不支持lseek 操作
  5. 半双工通信

2.4.2. 函数:

int mkfifo(const char *filename,mode_t mode);
功能:创健有名管道
参数:filename:有名管道文件名
       mode:权限
返回值:成功:0
       失败:-1,并设置errno号

2.4.3. 有名管道:使用特点

  1. 某进程用只写方式打开:写阻塞,一直到另一个进程用open把读打开;
  2. 某进程用只读方式打开:读阻塞,一直到另一个进程用open把写打开;
  3. 某进程用可读可写方式打开,如果fifo管道中没有数据,读阻塞,直到有数据写进去。

3. 早期通信:信号(通知:模拟中断)

区别 信号灯集semaphore 和 线程互斥信号量

3.1. 概念

  1. 信号是在软件层次上对中断机制的一种模拟,是一种异步通信方式。
  2. 信号可以直接进行 用户空间进程 和 内核进程 之间的交互,内核进程也可以利用它来通知用户空间进程 发生了哪些系统事件
  3. 如果该进程当前并未处于执行态,则该信号就由内核保存起来,直到该进程恢复执行 再传递给它
  4. 如果一个信号被进程设置为阻塞,则该信号传递被延迟,直到其阻塞被取消时才被传递给进程。

3.2. 信号的响应方式

  1. 忽略信号:对信号不做任何处理,但是有两个信号不能忽略:即SIGKILL及SIGSTOP
  2. 捕捉信号:定义信号处理函数,当信号发生时,执行相应的处理函数。但是有两个信号不能捕捉:即SIGKILL及SIGSTOP
  3. 缺省操作:Linux对每种信号都规定了默认操作

3.3. 信号种类

2)SIGINT:结束进程,对应快捷方式ctrl+c

3)SIGQUIT:退出信号,对应快捷方式ctrl+\

9)SIGKILL:结束进程不能 被忽略 不能 被捕捉

15)SIGTERM:结束终端进程,kill 使用时不加数字默认是此信号

17)SIGCHLD:子进程状态改变时给父进程发的信号

19)SIGSTOP:暂停进程不能 被忽略 不能 被捕捉

20)SIGTSTP:暂停信号,对应快捷方式ctrl+z

26)SIGALRM:闹钟信号,alarm函数设置定时,当到设定的时间时,内核会向进程发送此信号结束进程。

3.4. 函数接口

  1. 发送信号
int kill(pid_t pid, int sig);
功能:信号发送
参数:
	pid:指定进程
    sig:要发送的信号
返回值:
	成功 0     
    失败 -1
    
int raise(int sig);
功能:进程向自己发送信号
参数:sig:信号
返回值:
	成功 0   
    失败 -1

  1. alarm定时
unsigned int alarm(unsigned int seconds)
功能:在进程中设置一个定时器
	  alarm倒计时结束,默认是结束进程,或者 设置捕捉操作
参数:
	seconds:定时时间,单位为秒
    传参为0,则取消定时
    
返回值:
	如果调用此alarm()前,进程中已经设置了闹钟时间,则
	返回上一个闹钟时间的剩余时间,否则返回0。
 
注意:
	一个进程只能有一个闹钟时间。如果在调用alarm时,
	已设置过闹钟时间,则之前的闹钟时间被新值所代替

  1. 信号捕捉
#include <signal.h>
typedef void (*sighandler_t)(int); //参数2的,函数原型

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

int pause(void);
功能:
	用于将调用进程挂起,直到收到信号为止。
	等待收到信号,因为挂起是 休眠暂停状态,需要被唤醒
 	pause收到一次信号后,就向下运行了;想循环使用要用while让其持续挂起

使用案例:

#include <stdio.h>
#include <signal.h>
#include <unistd.h>

// 信号处理函数:arg为 收到的信号宏
void handler(int arg)
{
    if (SIGINT == arg)
        puts("-----ctrl + c");
    else if (SIGQUIT == arg)
        puts("-----ctrl + \\");
}

int main(int argc, char *argv[])
{
    // 该进程,收到信号的处理方式

    // 处理方式1:忽略操作
    // signal(SIGINT, SIG_IGN); //ctrl + c

    // 处理方式2:缺省操作(默认操作)
    // signal(2, SIG_DFL); //2即SIGINT

    // 处理方式3:捕捉信号 自定义处理方式
    // 对收到的不同信号,设置处理函数
    // 这样,执行完语句后,不执行命令默认功能,pause后继续向下
    signal(SIGINT, handler);
    signal(SIGQUIT, handler);

    /* 进行挂起,收到信号才会 返回执行态 */
    while (1)    //pause收到一次信号后,就向下运行了,用while让其持续挂起
        pause(); //等待收到信号,因为挂起是 休眠暂停状态,需要被唤醒

    printf("process run end\n");
    return 0;
}

4. IPC system V:共享内存 shared memory

4.1. 特点

  1. 共享内存是一种最为高效进程间通信方式,进程可以直接读写内存,而不需要任何数据的拷贝
  2. 为了在多个进程间交换信息,内核专门留出 一块内存区,可以由需要访问的进程将其映射到自己的私有地址空间
  3. 进程就可以直接读写这一内存区而不需要进行数据的拷贝,从而大大提高的效率。
  4. 由于多个进程 共享一段内存,因此也需要依靠某种同步机制,如互斥锁和信号量等

4.2. 步骤

  1. 创建key值 ftok
  2. 创建或打开共享内存 shmget
  3. 映射共享内存到用户空间(拿地址)shmat
  4. 撤销映射 shmdt
  5. 删除共享内存 shmctl

4.3. 函数

4.3.1. 产生一个独一无二的key值 ftok

key_t ftok(const char *pathname, int proj_id);
功能:产生一个独一无二的key值
参数:
    Pathname:已经存在的可访问文件的名字
    Proj_id:一个字符(因为只用低8位)
返回值:
	成功:key值
    失败:-1

ftok的构成:十六进制下:字符为'a'

比如:0x61012cb9

'a'为oct:97,其hex:61

01为系统中对第一个这个的编号(不晓得 不必深究)

2cb9为文件inode号的hex格式

4.3.2. 创建或打开共享内存 shmget

int shmget(key_t key, size_t size, int shmflg);
功能:创建或打开共享内存
参数:
    key  	键值
    size    共享内存的大小
    shmflg  IPC_CREAT|IPC_EXCL(判错)|0666
返回值:
    成功   shmid
    出错    -1

4.3.3. 映射、撤销映射、删除共享内存

映射共享内存:shmat

void  *shmat(int  shmid,const  void  *shmaddr,int  shmflg);
功能:映射共享内存,即把指定的共享内存映射到进程的地址空间用于访问
参数:
    shmid  	  共享内存的id号
    shmaddr   一般为NULL,表示由系统自动完成映射
              如果不为NULL,那么由用户指定
    shmflg:  SHM_RDONLY就是对该共享内存只进行读操作
              为0:可读可写
              
返回值:
	成功:完成映射后的地址,
    失败:-1的地址
用法:
	if( (p = (char *)shmat(shmid,NULL,0)) == (char *)(-1) )

取消映射内存:shmdt

int shmdt(const void *shmaddr);
功能:取消映射
参数:要取消的地址
返回值:
	成功0  
    失败的-1

删除共享内存:shmctl

int  shmctl(int  shmid, int  cmd, struct  shmid_ds *buf);
功能:(删除共享内存),对共享内存进行各种操作
参数:
    shmid   共享内存的id号
    cmd     IPC_STAT 获得shmid属性信息,存放在第三个参数
            IPC_SET  设置shmid属性信息,要设置的属性放在第三个参数
            IPC_RMID:删除共享内存,此时 *第三个参数* 为NULL 即可
返回:
	成功0 
    失败-1
                
用法:	shmctl(shmid,IPC_RMID,NULL);

4.4. shell命令

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

ipcrm -m shmid:删除共享内存

4.5. 案例代码

#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#define Buff_Size 128
typedef char DataType;

int main(int argc, char *argv[])
{
    // 1、创建key
    key_t key = ftok("./DoNotRemove.KEY", 'a');
    if (key < 0)
    {
        perror("ftok err");
        return -1;
    }
    printf("Key:%d\n", key);

    // 2、创建或者打开 共享内存
    // key值、内存长度大小、IPC_EXCL(判错,带着别管)
    int shmid = shmget(key, Buff_Size, IPC_CREAT | IPC_EXCL | 0666);
    if (shmid < 0)
    {
        if (errno == shmid) //存在则,更新一下即可
            shmid = shmget(key, Buff_Size, 0666);
        else
        {
            perror("shmget err");
            return -1;
        }
    }

    // 3、映射 从内核到用户空间
    // id、自动映射、可读可写
    DataType *buf = shmat(shmid, NULL, 0);
    if (buf == (DataType *)(-1))
    {
        perror("shmat err");
        return -1;
    }

    // 4、操作 共享内存

    // 写: fgets连\n都读进去,scanf还有\n在stdin缓冲区
    if (fgets(buf, Buff_Size, stdin) != NULL)
        if (buf[strlen(buf) - 1] == '\n')
            buf[strlen(buf) - 1] = '\0';

    // 读:
    printf("buf:%s\n", buf);

    // 5、撤销映射
    shmdt(buf);

    // 6、删除共享内存
    shmctl(shmid, IPC_RMID, NULL);

    return 0;
}

5. IPC system V:信号灯集(进程 信号量) semaphore

5.1. 概念

信号灯(semaphore),也叫信号量。

  1. 它是不同进程间,或一个给定进程内部不同线程间 同步的机制。
  2. System V的信号灯是 一个或者多个信号灯 的一个集合。
  3. 其中的每一个都是单独的计数信号灯。
  4. 而Posix信号灯指的是单个计数信号灯。
  5. 通过信号灯集实现共享内存的同步操作。

5.2. 步骤

  1. 创建key值 ftok
  2. 创建或打开信号灯集 semget
  3. 初始化信号灯 semctl
  4. pv操作 semop
  5. 删除信号灯集 semctl

5.3. 函数

常配合 共享内存使用 + (信号灯集)

5.3.1. 创建/打开信号灯(集) semget

int semget(key_t key, int nsems, int semflg);
功能:创建/打开信号灯
参数:
	key:   ftok产生的key值
    nsems: 信号灯集中包含的信号灯数目
    semflg:信号灯集的访问权限,通常为IPC_CREAT |IPC_EXCL |0666
    
返回值:
	成功:信号灯集ID
    失败:-1

注意:// 可能为0,系统原因,0不能正常使用,要避免
    
  	// 创建 或 打开 信号灯集
    // 灯集编号,里面几个灯,参数
    int semid = semget(key, 2, IPC_CREAT | IPC_EXCL | 0666);
    // 可能为0,系统原因,0不能正常使用,要避免
    if (semid <= 0)
    {
        if (errno == 17)
            semid = semget(key, 2, 0666);
    }
    else
    {
        perror("semget err");
        return -1;
    }

5.3.2. 信号灯:初始化、删除 semctl

int semctl ( int semid, int semnum,  int cmd…/*union semun arg*/);
功能:信号灯集合的控制(初始化/删除)
参数:
	semid: 信号灯集ID
    semnum: 要操作的集合中的信号灯编号
    cmd:
        GETVAL:获取信号灯的值,返回值是获得值
        SETVAL:设置信号灯的值,需要用到 第四个参数:共用体
        IPC_RMID:从系统中删除信号灯集合
返回值:
	成功 0
    失败 -1
        
用法:
	1.初始化:
        union semun //自己去定义,系统man提供了模版
        { 
            int val;
        } mysemun;
        mysemun.val = 10;
        semctl(/*灯集ID*/semid, /*哪个灯的编号*/0, /*哪个操作*/SETVAL, /*设置用*/mysemun);
    	semctl(semid, 0, SETVAL, mysemun);

	2.获取信号灯值:
        函数semctl(semid, 0, GETVAL)的返回值
        
	3.删除信号灯集:
		semctl(semid, 0, IPC_RMID);

5.3.3. 信号灯PV操作

int semop ( int semid, struct sembuf  *opsptr,  size_t  nops);
功能:
	对信号灯集合中的信号量进行PV操作
参数:
	semid: 信号灯集ID
    opsptr: 操作方式:对集合中的哪个灯
    nops:   要操作的信号灯的个数 1个
返回值:
	成功 : 0
    失败 :-1

操作方式的结构体:
    struct sembuf {
       short  sem_num;  // 要操作的信号灯的编号
       short  sem_op;   //    0 :  等待,直到信号灯的值变成0
                        //    1 :  释放资源,V操作
                        //   -1 :  申请资源,P操作                    
       short  sem_flg;  // 0(阻塞),IPC_NOWAIT, SEM_UNDO
                        // 0 申请不到就阻塞
    };

用法:
    1.申请资源 P操作:
        mysembuf.sem_num = 0; //灯编号
        mysembuf.sem_op = -1; //P操作 资源量-1
        mysembuf.sem_flg = 0; //阻塞形式
        semop(semid, &mysembuf, 1);//灯集ID,结构体,操作几个灯数目

    2.释放资源 V操作:
        mysembuf.sem_num = 0;
        mysembuf.sem_op =  1;
        mysembuf.sem_flg = 0;
        semop(semid, &mysembuf, 1);

5.4. shell命令

  • ipcs -s:查看信号灯集
  • ipcrm -s semid:删除信号灯集

5.5. 基础用例

#include <stdio.h>
#include <sys/types.h>
#include <sys/sem.h>
#include <sys/ipc.h>
#include <errno.h>

int main(int argc, char *argv[])
{
    // 创建key
    key_t key = ftok("./DoNotRemove.KEY", 'a');
    if (key < 0)
    {
        perror("ftok err");
        return -1;
    }
    printf("key:%d\n", key);

    // 创建 或 打开 信号灯集
    // 灯集编号,里面几个灯,参数
    int semid = semget(key, 2, IPC_CREAT | IPC_EXCL | 0666);
    // 可能为0,系统原因,0不能正常使用,要避免
    if (semid <= 0)
    {
        if (errno == EEXIST)
            semid = semget(key, 2, 0666);
        else
        {
            perror("semget err");
            return -1;
        }
    }
    else
    {
        // 只有在 创建成功后 初始化
        union semun {
            int val; /* Value for SETVAL */
        } mysem;

        /* 灯集ID,哪个编号的灯,干嘛操作,操作的共用体 */
        mysem.val = 10;                  // 给灯赋值:资源数目
        semctl(semid, 0, SETVAL, mysem); // 给灯赋值初始化

        /* 灯的数目,从创建灯集的代码看到 */
        mysem.val = 0;
        semctl(semid, 1, SETVAL, mysem); // 给灯赋值初始化
    }
    printf("灯集的ID,semid:%d\n", semid);
    printf("[0]的资源值:%d\n", semctl(semid, 0, GETVAL)); //参数2:哪个灯的编号
    printf("[1]的资源值:%d\n", semctl(semid, 1, GETVAL)); //参数2:哪个灯的编号

    /* -----PV操作----- */
    struct sembuf mybuf; //操作灯集 用的结构体

    mybuf.sem_num = 0;       //具体哪个灯
    mybuf.sem_op = -1;       //Post -1
    mybuf.sem_flg = 0;       //0:申请不到就阻塞
    semop(semid, &mybuf, 1); //操作灯集里面的几个灯
    printf("havd: %d\n", semctl(semid, 0, GETVAL));

    mybuf.sem_num = 1;       //具体哪个灯
    mybuf.sem_op = 1;        //Wait +1
    mybuf.sem_flg = 0;       //0:这里不重要意义
    semop(semid, &mybuf, 1); //操作灯集里面的几个灯
    printf("havd: %d\n", semctl(semid, 1, GETVAL));

    // 删除灯集
    semctl(semid, 0, IPC_RMID); //第二个参数为0即可,不必深究

    return 0;
}

6. IPC system V:消息队列 message queue

6.1. 概念

  1. 消息队列 是IPC对象的一种
  2. 消息队列 由消息队列ID来唯一标识
  3. 消息队列 就是一个消息的列表。
    1. 用户可以在消息队列中添加消息、读取消息等。
  1. 消息队列 可以按照类型来发送(添加)/接收(读取)消息

6.2. 步骤

  1. 创建key值 ftok
  2. 创建或打开消息队列 msgget
  3. 添加消息 :按照消息类型将消息添加到消息队列末尾 msgsnd
  4. 读取消息 :按照类型将消息从消息队列中读走 msgrcv
  5. 删除消息队列 msgctl

6.3. 函数

6.3.1. 创建或打开一个消息队列 msgget

int msgget(key_t key, int flag);
功能:创建或打开一个消息队列
参数:  
	key值
    flag:创建消息队列的权限IPC_CREAT|IPC_EXCL|0666
返回值:
	成功:msgid
    失败:-1

6.3.2. 添加消息 msgsnd

int msgsnd(int msqid, const void *msgp, size_t size, int flag); 
功能:添加消息
参数:
	msqid:消息队列的ID
    msgp:指向消息的指针。
        	常用消息结构msgbuf如下:自己去定义
              struct msgbuf{
                long mtype;       //消息类型 必须是long
                char mtext[N];	  //消息正文 可以是其他
                }; 	   
    size:发送的 消息正文 的字节数
    flag:
   		 IPC_NOWAIT消息没有发送完成函数也会立即返回    
         0:直到发送完成函数才返回
返回值:
	成功:0
    失败:-1
        
使用:
        msgsnd(msgid, &msg,sizeof(msg)-sizeof(long), 0)
注意:
	消息结构除了第一个成员 必须 为long类型外,其他成员可以根据应用的需求自行定义。

6.3.3. 读取消息 msgrcv

int msgrcv(int msgid,  void* msgp,  size_t  size,  long msgtyp,  int flag);
功能:读取消息
参数:
	msgid:消息队列的ID
    msgp: 存放读取消息的空间
    size: 接受的消息正文的字节数
    msgtype:
    	0:接收消息队列中第一个消息。
        大于0:接收消息队列中第一个类型为msgtyp的消息.
        小于0:接收消息队列中类型值不小于msgtyp的绝对值且类型值又最小的消息。
    flag:
    	0:若无消息函数会一直阻塞
        IPC_NOWAIT:若没有消息,进程会立即返回ENOMSG
返回值:
	成功:接收到的消息的长度
    失败:-1

6.3.4. 删除消息队列 msgctl

int msgctl ( int msgqid, int cmd, struct msqid_ds *buf );
功能:对消息队列的操作,删除消息队列
参数:
	msqid:消息队列的队列ID
    cmd:
        IPC_STAT:读取消息队列的属性,并将其保存在buf指向的缓冲区中。
        IPC_SET:设置消息队列的属性。这个值取自buf参数。
        IPC_RMID:从系统中删除消息队列。
    buf:消息队列缓冲区
返回值:
	成功:0
    失败:-1
用法:
    msgctl(msgid, IPC_RMID, NULL);

6.4. shell命令

ipcs -q :查看消息队列

ipcrm -q msgid :删除消息队列

6.5. 代码用例:

#include <stdio.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <unistd.h>
#include <string.h>
#define N 32

struct msgbuf
{
    /* 消息类型 必须是long */
    long mtype; /* message type, must be > 0 */

    /* 消息正文 可以是其他类型 */
    char mtext[32]; /* message data */
    int age;
};

int main(int argc, char *argv[])
{
    // 创建key值
    key_t key = ftok("./DoNotRemove.KEY", 'a');
    if (key < 0)
    {
        perror("ftok err");
        return -1;
    }
    printf("key:%d\n", key);

    // 创建 或者 打开 消息队列
    int msgid = msgget(key, IPC_CREAT | IPC_EXCL | 0666);
    if (msgid <= 0) //和信号等集一样,系统对编号0的,不能用
    {
        if (errno == EEXIST)
            msgid = msgget(key, 0666);
    }
    else
    {
        perror("msgget err");
        return -1;
    }
    printf("msgid:%d\n", msgid);

    /* 添加消息 */
    struct msgbuf mymsg_send;

    // 消息结构体  赋值
    mymsg_send.mtype = 1;
    mymsg_send.age = 22;
    strcpy(mymsg_send.mtext, "name_1");

    // 添加消息到队列:哪个队列,信息结构体,消息正文长度,格式用0即可
    msgsnd(msgid, &mymsg_send, sizeof(struct msgbuf) - sizeof(long), 0);

    /* 读取消息 */
    struct msgbuf mymsg_read;
    // 从哪个消息队列读取,读到哪里,正文长度,
    // msgtype 可以实现一种简单的接收优先级:0是同类型,即结构体long的值代表类型
    // 格式用0即可
    msgrcv(msgid, &mymsg_read, sizeof(struct msgbuf) - sizeof(long), 1, 0);
    printf("****%s %d\n", mymsg_read.mtext, mymsg_read.age);

    // 删除消息队列
    // 哪个消息队列,操作,空就行 别管
    msgctl(msgid, IPC_RMID, NULL);

    return 0;
}

7. IPC system V:BSD 套接字(socket)(这里不谈)

在网络编程中专门学习

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值