2013-12-24 14:28:46 fzy0201 阅读数 897

前言

研究IPMI源码,实现将IPMI获取的性能数据传递给另一独立进程,主要用到的IPC方法就是共享内存。现在来说,基本是对共享内存这一机制有了更加透彻的理解和掌握。记录下。

相关背景:

Linux中IPC有几种实现版本,例如共享内存就有Posix共享内存与System V共享内存,它们的联系与区别?
答:Linux中的进程间通信机制源自于Unix平台上的进程通信机制。Unix的两大分支AT&T Unix和BSD Unix在进程通信实现机制上的各有所不同,前者对Unix早期的IPC进行系统的改进和扩充,形成了运行在单个计算机上的System V IPC,后者则跳过单机限制,实现了基于socket的进程间通信机制。同时因为Unix版本的多样性,IEEE又单独制定了一套独立的Posix IPC标准。在这三者基础上,Linux所继承的IPC,如图示:

也就是说,Linux上的IPC版本之间只是实现不同,效果并没有区别。

共享内存区

关于共享内存机制具体的介绍我就不多说了,内容网上多的是,有书的话请详细阅读《网络编程,卷二》。这里,我先后使用过Mmap映射同一文件方式与System V API方式分别进行了实验,就其中遇到的一些问题说一说。


1,Mmap 映射

1)指定映射文件

适用于两个独立的任何进程,通过指定同一个文件来实现共享内存方式的进程间通信。需要注意的是,进程之间在共享内存时,共享内存会一直保持到通信完毕为止,数据内容会一直在内存中。在解除映射之后,内存上的内容才写回文件。

原理:所映射文件的实际数据成了被共享内存区的内容。那如何保证各个独立进程寻址到同一内存页面?

<1> page cache及swap cache中页面的区分:一个被访问文件的物理页面都驻留在page cache或swap cache中,一个页面的所有信息由struct page来描述。struct page中有一个域为指针mapping ,它指向一个struct address_space类型结构。page cache或swap cache中的所有页面就是根据address_space结构以及一个偏移量来区分的;

<2> 文件与address_space结构的对应:一个具体的文件在打开后,内核会在内存中为之建立一个struct inode结构,其中的i_mapping域指向一个address_space结构。这样,一个文件就对应一个address_space结构,一个address_space与一个偏移量能够确定一个page cache 或swap cache中的一个页面。因此,当要寻址某个数据时,很容易根据给定的文件及数据在文件内的偏移量而找到相应的页面;

<3> 进程调用mmap()时,只是在进程空间内新增了一块相应大小的缓冲区,并设置了相应的访问标识,但并没有建立进程空间到物理页面的映射。因此,第一次访问该空间时,会引发一个缺页异常;

<4> 对于共享内存映射情况,缺页异常处理程序首先在swap cache中寻找目标页(符合address_space以及偏移量的物理页),如果找到,则直接返回地址;如果没有找到,则判断该页是否在交换区(swap area),如果在,则执行一个换入操作;如果上述两种情况都不满足,处理程序将分配新的物理页面,并把它插入到page cache中。进程最终将更新进程页表;

<5> 所有进程在映射同一个共享内存区域时,情况都一样,在建立线性地址与物理地址之间的映射之后,不论进程各自的返回地址如何,实际访问的必然是同一个共享内存区域对应的物理页面;

以上原理出自参考的博文,对整个流程的理解,我用图表示:


实例仍然给出原博文例子,算是属于手抄版吧。。。反正是实例嘛,说明效果就好了。

/* example A */
#include <sys/mman.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>

typedef struct
{
  char name[4];
  int  age;
}people;

void main(int argc, char** argv) // map a normal file as shared mem:
{
        int fd,i;
        people *p_map;
        char temp;

        fd = open(argv[1], O_CREAT|O_RDWR|O_TRUNC, 00777); // 注意O_TRUNC选项参数
        lseek(fd, sizeof(people)*5-1, SEEK_SET);
        write(fd, "", 1); 
  
        p_map = (people*) mmap( NULL, sizeof(people)*10, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0 );
        close( fd );
        temp = 'a';
        for(i = 0; i < 10; i++)
        {
                temp += 1;
                memcpy( (p_map+i)->name, &temp, 1 );
                (p_map+i)->age = 20+i;
        }
        printf(" initialize over \n ");
        sleep(10);
        munmap( p_map, sizeof(people)*10 );
        printf( "umap ok \n" );
}
/* example B */
#include <sys/mman.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>

typedef struct
{
        char name[4];
        int  age;
}people;

void main(int argc, char** argv)  // map a normal file as shared mem:
{
        int fd,i;
        people *p_map;
    
        fd=open( argv[1], O_CREAT|O_RDWR, 00777 );
        p_map = (people*)mmap(NULL, sizeof(people)*10, PROT_READ|PROT_WRITE,
                 MAP_SHARED, fd, 0); 
        for(i = 0; i < 10; i++)
        {   
                printf( "name: %s age %d;\n",(p_map+i)->name, (p_map+i)->age );
        }

        munmap( p_map, sizeof(people)*10 );
}

这里需要注意的是open()文件时O_TRUNC的参数。这个每次打开文件时清零文件的,也就是说,如果有三个以上的进程需要访问此文件,打开时就会把之前两个进程的数据给清零掉,这是我们不愿意看到的。这个问题在一开始时没注意,出了问题还以为是共享内存本身机制的问题,后来逐一对照代码,才发现原因所在。刚学习的同学尤其注意,实例代码也不能拿来就用,还是要自己搞明白才好。

另外需要说明的是,我没有遇到原文中提到的进程B在未解除映射时和解除映射后对共享内存的访问时候的差异,实在没找的原因。对文件大小和映射内存大小的限制也没有体现。贴上实例效果,我在想是否有可能是现在的内核版本中已经完善,就是只要不跨越内存页,超过文件大小的共享内存区域依然能够访问



2)匿名映射文件

适用于具有亲缘关系的父子进程。原理同上。

所谓的匿名映射文件,只不过指定文件映射的一种特殊形式,本质上并无不同。这种形式主要就是基于Linux中父子进程创建的方式。父进程通过在调用fork()函数创建子进程之前,通过mmap()创建共享内存区,之后fork的子进程继承父进程资源,自然也就共享父进程mmap后的映射空间了。

直接贴上手抄版实例:

/* fork example */
#include <sys/mman.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct
{
        char name[4];
        int  age;
}people;

void main(int argc, char** argv)
{
        int i;
        people *p_map;
        char temp;
        p_map = (people *)mmap(NULL, sizeof(people)*10, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0); 
        if (fork() == 0)
        {   
                sleep(2);
                for (i = 0;i < 5;i++)
                {
                        printf("child read: the %d people's age is %d\n", i+1, (p_map+i)->age);
                }
                p_map->age = 100;
                munmap(p_map, sizeof(people)*10); //实际上,进程终止时,会自动解除映射。
                exit(1);
        }
        temp = 'a';
        for (i = 0; i < 5;i++)
        {
                temp += 1;
                memcpy((p_map+i)->name, &temp, 1);
                (p_map+i)->age = 20+i;
        }
        sleep(5);
        printf("parent read: the first people,s age is %d\n", p_map->age);
        printf("umap\n");
        munmap(p_map,sizeof(people)*10);
        printf("umap ok\n");
}

3) 对Mmap()返回地址的访问

这一块主要东西我觉得还是看《卷二》吧,其中的内容其实也还好,容易理解。

2,System V API

System V学习起来相对很简单,因为你只要会用接口就成。原理很mmap()文件映射是一样一样的。


总结

总的来说,共享内存对于数据的存取效率是很高效的。但目前为止,我觉得对于数据类型比较多,且结构复杂,像数组嵌套之类的用这个太蛋疼了。。因为我只知道一个共享内存的首地址,所以对内存的访问只能一个萝卜一个坑的去找。很不爽。不过,也可能是目前见识有限,暂就这些吧。

另外,真心觉得写这种技术型文章很费精力啊。。远不是贴两个程序,说两句话就成的。这方面,我做的不够。


主要参考:

博文:Linux环境进程间通信(五): 共享内存(上)

《网络编程,卷二》

2014-01-05 08:55:51 Einstein_1110 阅读数 439

一.IPC背景

进程之间需要同步处理,同步需要通信,普通文件就是最基本的通信手段。普通文件IPC有一个进程改变另一个进程无法感知这种改变的问题。

解决办法:用特殊的文件:管道文件。

二.管道文件

1.创建管道文件

mkfifo()或使用linux命令mkfifo

2.管道文件的特点

read管道文件时,没有数据会阻塞,而且read后数据被删除

数据有序,先进先出

打开的描述符可以读写(two-way双工)shutdown可以关掉读写

管道文件的数据关闭后,数据不能持久,关闭后数据丢失。

管道的数据存储在内核的缓冲中

三,匿名管道

发现有名的管道的名字仅仅是内核是否返回同一个文件描述符的标识而已

所以当管道名失去标识作用的时候,实际可以不要名字。

在父子进程之间:打开文件描述符后创建进程

父子进程都有描述符号。管道文件没有价值

所以在父子进程中,引入一个没有名字的管道:匿名管道。

结论:匿名管道只能使用在父子进程之间。

1.创建匿名管道

int pipe(int fd[2])//创建管道,打开管道,拷贝管道,对二个fd这个关闭读,一个关闭写

fd[0]:只读

fd[1]:只写

注意:数据无边界

2.使用匿名管道

二.基于内存的通信

一组内核内存的工具 ipcs(-m -q-s)ipcrm

1.普通的父子进程之间的匿名内存内享映射

2.内核共享内存

编程模型

2.1创建共享内存,得到一个ID shmget

int shmget(key_tkey,//约定创建与访问的是同一个共享内存(ID唯一),可将目录(唯一)转化成整数,ftok()产生唯一的key_t

     int size,共享内存大小

int flags)//共享内存的属性与权限 创建(IPC_CREAT)、IPC_EXCL防止覆盖

返回:成功返回ID,失败:返回-1

2.2把ID映射成虚拟地址(挂载) shmat

void *shmat(intshmid,

const void *shmaddr,首地址,0表示系统指定首地址

int flags)//挂载方式,建议为零

返回(int *)-1挂载失败,成功返回虚拟地址

2.3使用虚拟地址访问内核共享内存 使用任何内存函数、运算符

str*** mem*** += ++ [] ->

2.4卸载虚拟地址 shmdt

2.5删除共享内存 shctl(修改/获取共享内存的属性)

int shmctl(int id,//被操作的共享内存ID

int    how,//操作方式

struct shmid_ds *buf)//共享内存属性

3.内核共享队列(有序)

编程模型:

3.1创建共享队列/得到消息队列msgget

3.2使用消息队列(发送msgsnd/接收消息msgrcv)

3.3删除队列msgctl

创建消息队列

int msgget(key_t,int)

发送消息

int msgsnd(

int id,//消息队列ID

const void *msg,//要发送的消息

size_t len,//消息的长度

int flags//发送消息的方式,建议为0

);

返回:-1失败0成功

第二个参数的消息有固定的格式

4字节:表示消息的类型

若干字节:消息内容

第三个参数:

消息的大小,不包含类型的4个字节

四.信号量

1.信号的不足:必须知道通信对方进程,信号是一对一的形式,缺乏多对多的支持

2.信号量(semaphore)信号灯/信号量

信号量是共享内存整数数组,根据需要定指定的数据长度

信号量就是根据数组中的值,决定阻塞还是解除阻塞

3.编程

3.1创建或者得到信号量 semget

int semget(key_tkey,

int nums, //信号量数组个数

int flags);//控制信号量的创建标记创建为IPC_CREAT|IP_EXCL |0666    打开信号量为0

返回: -1:失败

>=:成功返回信号量的ID

3.2初始化信号量中指定下标的值 semctl

3.3根据信号量阻塞或者解除阻塞 semop

int semop(

int semid,//信号量ID

struct sembuf*op,//对信号量的操作

size_t nums。//第二个参数的个数

);

返回值:-1:时失败

3.4删除信号量 semctl

int semctl(int semid,

int nums,//对IPC_RMID无意义

int cmd,//SETVALIPC_RMID

...);//对IPC_RMID无意义

struct sembuf

{

int sem_num;//下标

int sem_op;

int sem_flg;//建议为0

}

sem_op:

前提条件:信号量是unsignedshort int,不能小于0

-:够减,则semop马上返回,不够减,则阻塞

+:执行+操作

0:判定信号量大于0,则阻塞,直到为0

2017-03-23 22:19:39 u013427969 阅读数 146

Linux IPC 1 之 概述

背景:

 以前学习研究的Linux进程间通信,经常不用都要忘了,而且发现写的博客也有些不够完整,所以干脆推倒重来,温故知新而已:)

进程的含义

进程是操作系统的概念,每当我们执行一个程序时,对于操作系统来讲就创建了一个进程,在这个过程中,伴随着资源的分配和释放。可以认为进程是一个程序的一次执行过程。

IPC的含义


IPC进程间通信(Inter-Process Communication)就是指多个进程之间相互通信,交换信息的方法。Linux IPC基本上都是从Unix平台上继承而来的。主要包括最初的Unix IPC,System V IPC以及基于Socket的IPC。另外,Linux也支持POSIX IPC。

三个名词

System V,BSD,POSIX

**System V**是Unix操作系统最早的商业发行版之一。它最初由AT&T(American Telephone & Telegraph)开发,最早在1983年发布。System V主要发行了4个版本,其中SVR4(System V Release 4)是最成功的版本。
**BSD**(Berkeley Software Distribution,有时也被称为Berkeley Unix)是加州大学于1977至1995年间开发的。
在19世纪八十年代至九十年代之间,System V和BSD代表了Unix的两种主要的操作风格。它们的主要区别如下:

系统                      System V           BSD
root脚本位置            /etc/init.d/       /etc/rc.d/
默认shell                 Bshell             Cshell
文件系统数据            /etc/mnttab     /etc/mtab
内核位置                  /UNIX             /vmUnix
打印机设备                lp                  rlp
字符串函数                memcopy       bcopy
终端初始化设置文件    /etc/initab       /etc/ttys
终端控制                  termio            termios

Linux系统的操作风格往往介于这两种风格之间。

**POSIX**(Portable Operating System Interface [for Unix])是由IEEE(Institute of Electrical and Electronics Engineers,电子电气工程协会)开发的。现有的大部分Unix都遵循POSIX标准,而Linux从一开始就遵循POSIX标准。
故Linux的POSIX标准包含了上面两种unix like操作系统的IPC方式。对于继承过来的除socket外的5种方式,具体有何不同之处暂未研究。

IPC的6种方式

1.管道 (from BSD)
2.信号 (from BSD)
3.信号量 (from System V)
4.共享内存(from System V)
5.消息队列 (from System V)
6.套接字

● 管道(Pipe)及有名管道(Named Pipe): 管道可用于具有”血缘”关系进程间(也就是父子进程或者兄弟进程)的通信。有名管道除具有管道所具有的功能外,还允许无”血缘”关系进程间的通信。

● 信号(Signal): 信号是在软件层次上对中断机制的一种模拟,它是比较复杂的通信方式,用于通知进程有某事件发生。

● 信号量(Semaphore): 主要作为进程之间及同一进程的不同线程之间的同步和互斥手段。

● 共享内存(Shared Memory): 可以说这是最有效的进程间通信方式。它使得多个进程可以访问同一块内存空间,不同进程可以及时看到对方进程中对共享内存中数据的更新。这种通信方式需要依靠某种同步机制,如互斥锁和信号量等。

● 消息队列(Messge Queue): 消息队列是消息的链表,包括 Posix 消息队列和 System V 消息队列。它克服了前两种通信方式中信息量有限的缺点,具有写权限的进程可以按照一定的规则向消息队列中添加消息;对消息队列具有读权限的进程则可以从消息队列中读取消息。

● 套接字(Socket): 这个绝对是一种更为一般的进程间通信机制,它可用于网络中不同机器之间的进程间通信


2017-11-26 21:19:00 weixin_30832143 阅读数 9

Linux下IPC机制

实践要求

研究Linux下IPC机制:原理,优缺点,每种机制至少给一个示例,提交研究博客的链接

  • 共享内存
  • 管道
  • FIFO
  • 信号
  • 消息队列

    IPC

    进程间通信(IPC,InterProcess Communication)是指在不同进程之间传播或交换信息。

IPC的方式通常有管道(包括无名管道和命名管道)、消息队列、信号量、共享存储、Socket、Streams等。其中 Socket和Streams支持不同主机上的两个进程IPC。

共享内存

共享内存(Shared Memory)实际就是文件映射的一种特殊情况。进程在创建文件映射对象时用0xFFFFFFFF来代替文件句柄(HANDLE),就表示了对应的文件映射对象是从操作系统页面文件访问内存,其它进程打开该文件映射对象就可以访问该内存块。由于共享内存是用文件映射实现的,所以它也有较好的安全性,也只能运行于同一计算机上的进程之间。

定义

共享内存使得多个进程可以访问同一块内存空间,是最快的可用IPC形式。是针对其他通信机制运行效率较低而设计的。往往与其它通信机制,如信号量结合使用,来达到进程间的同步及互斥。

实例

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

int main()
{
 int fd[2];  // 两个文件描述符
 pid_t pid;
 char buff[20];

 if(pipe(fd) < 0)  // 创建管道
     printf("Create Pipe Error!\n");

 if((pid = fork()) < 0)  // 创建子进程
    printf("Fork Error!\n");
 else if(pid > 0)  // 父进程
 {
     close(fd[0]); // 关闭读端
     write(fd[1], "hello world\n", 12);
}
 else
 {
     close(fd[1]); // 关闭写端
    read(fd[0], buff, 20);
     printf("%s", buff);
 }

 return 0;
 }

管道

管道,通常指无名管道,是 UNIX 系统IPC最古老的形式。

特点:

  • 它是半双工的(即数据只能在一个方向上流动),具有固定的读端和写端。

  • 它只能用于具有亲缘关系的进程之间的通信(也是父子进程或者兄弟进程之间)。

  • 它可以看成是一种特殊的文件,对于它的读写也可以使用普通的read、write 等函数。但是它不是普通的文件,并不属于其他任何文件系统,并且只存在于内存中。

    实例

    ```` 1 #include<stdio.h>
    2 #include<unistd.h>
    3
    4 int main()
    5 {
    6 int fd[2]; // 两个文件描述符
    7 pid_t pid;
    8 char buff[20];
    9
    10 if(pipe(fd) < 0) // 创建管道
    11 printf("Create Pipe Error!\n");
    12
    13 if((pid = fork()) < 0) // 创建子进程
    14 printf("Fork Error!\n");
    15 else if(pid > 0) // 父进程
    16 {
    17 close(fd[0]); // 关闭读端
    18 write(fd[1], "hello world\n", 12);
    19 }
    20 else
    21 {
    22 close(fd[1]); // 关闭写端
    23 read(fd[0], buff, 20);
    24 printf("%s", buff);
    25 }
    26
    27 return 0;
    28 }
###FIFO
FIFO,也称为命名管道,它是一种文件类型。
####特点

- FIFO可以在无关的进程之间交换数据,与无名管道不同。

- FIFO有路径名与之相关联,它以一种特殊设备文件形式存在于文件系统中。
####实例

1 #include<stdio.h>
2 #include<stdlib.h> // exit
3 #include<fcntl.h> // O_WRONLY
4 #include<sys/stat.h>
5 #include<time.h> // time
6
7 int main()
8 {
9 int fd;
10 int n, i;
11 char buf[1024];
12 time_t tp;
13
14 printf("I am %d process.\n", getpid()); // 说明进程ID
15
16 if((fd = open("fifo1", O_WRONLY)) < 0) // 以写打开一个FIFO
17 {
18 perror("Open FIFO Failed");
19 exit(1);
20 }
21
22 for(i=0; i<10; ++i)
23 {
24 time(&tp); // 取系统当前时间
25 n=sprintf(buf,"Process %d's time is %s",getpid(),ctime(&tp));
26 printf("Send message: %s", buf); // 打印
27 if(write(fd, buf, n+1) < 0) // 写入到FIFO中
28 {
29 perror("Write FIFO Failed");
30 close(fd);
31 exit(1);
32 }
33 sleep(1); // 休眠1秒
34 }
35
36 close(fd); // 关闭FIFO文件
37 return 0;
38 }

###信号
信号是进程间通信机制中唯一的异步通信机制,可以看作是异步通知,通知接收信号的进程有哪些事情发生了。信号机制经过POSIX实时扩展后,功能更加强大,除了基本通知功能外,还可以传递附加信息。
####实例

include <signal.h>

include <sys/types.h>

include <unistd.h>

void new_op(int,siginfo_t,void);
int main(int argc,char**argv)
{
struct sigaction act;
int sig;
sig=atoi(argv[1]);
sigemptyset(&act.sa_mask);
act.sa_flags=SA_SIGINFO;
act.sa_sigaction=new_op;
if(sigaction(sig,&act,NULL) < 0)
{
printf("install sigal error\n");
}
while(1)
{
sleep(2);
printf("wait for the signal\n");
}
}
void new_op(int signum,siginfo_t info,void myact)
{
printf("receive signal %d", signum);
sleep(5);

###消息队列
- 消息队列,是消息的链接表,存放在内核中。一个消息队列由一个标识符(即队列ID)来标识。

- 特点:

消息队列是面向记录的,其中的消息具有特定的格式以及特定的优先级。

消息队列独立于发送与接收进程。进程终止时,消息队列及其内容并不会被删除。

消息队列可以实现消息的随机查询,消息不一定要以先进先出的次序读取,也可以按消息的类型读取。
####实例

#include <stdio.h>
2 #include <stdlib.h>
3 #include <sys/msg.h>
4
5 // 用于创建一个唯一的key
6 #define MSG_FILE "/etc/passwd"
7
8 // 消息结构
9 struct msg_form {
10 long mtype;
11 char mtext[256];
12 };
13
14 int main()
15 {
16 int msqid;
17 key_t key;
18 struct msg_form msg;
19
20 // 获取key值
21 if((key = ftok(MSG_FILE,'z')) < 0)
22 {
23 perror("ftok error");
24 exit(1);
25 }
26
27 // 打印key值
28 printf("Message Queue - Server key is: %d.\n", key);
29
30 // 创建消息队列
31 if ((msqid = msgget(key, IPC_CREAT|0777)) == -1)
32 {
33 perror("msgget error");
34 exit(1);
35 }
36
37 // 打印消息队列ID及进程ID
38 printf("My msqid is: %d.\n", msqid);
39 printf("My pid is: %d.\n", getpid());
40
41 // 循环读取消息
42 for(;;)
43 {
44 msgrcv(msqid, &msg, 256, 888, 0);// 返回类型为888的第一个消息
45 printf("Server: receive msg.mtext is: %s.\n", msg.mtext);
46 printf("Server: receive msg.mtype is: %d.\n", msg.mtype);
47
48 msg.mtype = 999; // 客户端接收的消息类型
49 sprintf(msg.mtext, "hello, I'm server %d", getpid());
50 msgsnd(msqid, &msg, sizeof(msg.mtext), 0);
51 }
52 return 0;
53 }
复制代码
msg_client.c

复制代码
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <sys/msg.h>
4
5 // 用于创建一个唯一的key
6 #define MSG_FILE "/etc/passwd"
7
8 // 消息结构
9 struct msg_form {
10 long mtype;
11 char mtext[256];
12 };
13
14 int main()
15 {
16 int msqid;
17 key_t key;
18 struct msg_form msg;
19
20 // 获取key值
21 if ((key = ftok(MSG_FILE, 'z')) < 0)
22 {
23 perror("ftok error");
24 exit(1);
25 }
26
27 // 打印key值
28 printf("Message Queue - Client key is: %d.\n", key);
29
30 // 打开消息队列
31 if ((msqid = msgget(key, IPC_CREAT|0777)) == -1)
32 {
33 perror("msgget error");
34 exit(1);
35 }
36
37 // 打印消息队列ID及进程ID
38 printf("My msqid is: %d.\n", msqid);
39 printf("My pid is: %d.\n", getpid());
40
41 // 添加消息,类型为888
42 msg.mtype = 888;
43 sprintf(msg.mtext, "hello, I'm client %d", getpid());
44 msgsnd(msqid, &msg, sizeof(msg.mtext), 0);
45
46 // 读取类型为777的消息
47 msgrcv(msqid, &msg, 256, 999, 0);
48 printf("Client: receive msg.mtext is: %s.\n", msg.mtext);
49 printf("Client: receive msg.mtype is: %d.\n", msg.mtype);
50 return 0;
51 }
````

转载于:https://www.cnblogs.com/l97----/p/7901003.html

LINUX中IPC信号灯

阅读数 19

linux系统IPC浅谈

阅读数 637

linux ipc

阅读数 630

linux各种IPC机制

阅读数 1347

没有更多推荐了,返回首页