嵌入式笔记Linux ——《进程间的通信》

基本概念

:两个/多个进程之间的数据交互叫做进程间的通信

进程间的通信方式

:采用开放系统接口函数来实现进程间通信

(1)文件
(2)管道
(3)信号
(4)共享内存
(5)消息队列(重点)
(6)信号量集
(7)网络(重点)

其中(4)(5)(6)三种通信方式统称为XSI IPC通信
(X/open System Interface Inter-Process Commucation)

进程之间通过文件来通信

:比如一个进程通过write()函数去向一个文件写入数据,另一个进程从该文件通过read()函数读取文件中的数据实现通信

管道pipe(又称无名管道)

:pipe是一种特殊的文件类型,在应用层表现为两个打开的文件描述符
=

特点:

  1. 半双工,数据在同一时刻只能在一个方向上流动
  2. 管道不是普通文件,不属于某个文件系统,其只存在与内存中
  3. 管道没有名字,只能在具有公共祖先的进程之间使用
  4. 管道的缓冲区是有限的,管道是一个固定大小的缓冲区(在Linux系统中,该缓冲区大小为4Kbyte)
  5. 管道所传输的数据是无格式的,需要(发送/接收)双方约定数据格式
  6. 数据只能从管道的一端写入,从另一端读出
  7. 从管道读数据是一次操作,数据一旦被读走,数据从管道中丢弃,释放空间以便写更多的数据
pipe()函数

#include <unistd.h>
int pipe(int filedes[2]);
功能:经由参数filedes返回两个文件描述符

参数:
filedes为int型数组的首地址,其存放了管道的文件描述符fd[0]、fd[1]。

filedes[0]为读而打开,filedes[1]为写而打开管道,filedes[0]的输出是filedes[1]的输入。

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

文件描述符概述


文件描述符是非负整数,是文件的标识。

:内核利用文件描述符(file descriptor)来访问文件。利用open打开一个文件时,内核会返回一个文件描
述符。

每个进程都有一张文件描述符的表,进程刚被创建时,其计录了默认打开的标准输入、标准输出、标准错误输出三个设备文件的文件描述符0、1、2。在进程中打开其他文件时,系统会返回文件描述符表中最小可用的文件描述符,并将此文件描述符记
录在表中。

管道pipe示例代码:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>

int main()
{
	int fd_pipe[2];//存放pipe创建的文件描述符
	
	char buf[] = "hello world";
	
	pid_t pid;//存放进程号


	/*pipe()创建fd_pipe[0]为读而打开,fd_pipe[1]为写而打开管道,*/
	if( pipe(fd_pipe) < 0 )
	{
		perror("pipe");//错误处理
	}
	
	pid = fork();
	
	if( pid < 0)//错误处理
	{
		perror("fork");
		exit(-1);
	}

	if(pid == 0)//子进程
	{
		write(fd_pipe[1],buf,strlen(buf));//向fd_pipe[1]写入
		_exit(0);
	}
	else//父进程
	{
		wait(NULL);//等待子进程的退出
		memset(buf,0,sizeof(buf));//清空buf
		read(fd_pipe[0],buf,sizeof(buf));//从fd_pipe[0]读出
		printf("buf = [%s]\n",buf);
	}
	
	return 0;
}
运行结果:


解析:
父进程创建子进程后,子进程使用write()函数向fd_pipe[1]文件描述符写入数据,然后退出。

父进程等待子进程的退出后,先把自己的buf区清空,然后使用read()函数从fd_pipe[0]文件描述符中读出数据,最后把读出的数据通过printf打印到终端上。

dup()和dup2() 是两个非常有用的系统调用,都是用来复制一个文件的描述符。

int dup(int oldfd);
int dup2(int oldfd, int newfd);
dup和dup2经常用来重定向进程的stdin、stdout和 stderr。

dup()函数

#include <unistd.h>
int dup(int oldfd);
功能:
复制oldfd文件描述符,并分配一个新的文件描述符,新的文件描述符是调用进程文件描述符表中最小可用的文件描述符。

参数:
oldfd:要复制的文件描述符oldfd。

返回值:
成功:新文件描述符。
失败:返回-1,错误代码存于errno中。

注意: 新的文件描述符和oldfd指向的是同一个文件,共 享oldfd所有的锁定、读写位置和各项权限或标志,但 文件描述符之间并不共享close-on-exec标志。

示例代码:
#include <stdio.h>
 #include <stdlib.h>
 #include <unistd.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <fcntl.h>
 
 int main(int argc,char *argv[])
 {
     int fd1, fd2;//存放文件描述符

/*在当前目录下以只写的方式打开test这个文件,如果没有这个文件就创建,权限是777*/	 
     fd1 = open("test",O_CREAT | O_WRONLY,0777); //成功:返回该文件描述符 fd = 3                            
     if(fd1 < 0)//错误处理
      {
         perror("open");
        exit(-1);
      }   
//分析文件描述符列表:0标准输入 1标准输出 2标准错误输出 3open后创建的文件描述符
     close(1);//1:标准输出文件描述符  关闭1
//分析文件描述符列表:0标准输入  2标准错误输出 3open后创建的文件描述符

     fd2 = dup(fd1);//复制新的文件描述符给fd2 = 1

     printf("fd2 = %d\n",fd2);
     
     return 0;
  }   
运行结果:

分析:在close(1)之前,存在1:标准输出文件描述符(输出到终端),在close(1)后,1的文件描述符关闭,此时该文件的文件描述符列表为 0 ,2 ,3

fd2通过dup()函数复制获得新的文件描述符,获得规则是该文件描述符列表中最小的非负整数(目前是 0 2 3 最小的非负整数不就是1嘛)

之前的1:标准输出是输出到终端,经过close,dup后使标准输出输出到了test文件 (重定向)

dup2()函数

#include <unistd.h>
int dup2(int oldfd, int newfd)
功能:
复制一份打开的文件描述符oldfd,并分配新的文
件描述符newfd,newfd也标识oldfd所标识的文件。

参数:
要复制的文件描述符oldfd
分配的新的文件描述符newfd

返回值:
成功:返回newfd
失败:返回-1,错误代码存于errno中
注意: newfd是小于文件描述符最大允许值的非负整数, 如果newfd是一个已经打开的文件描述符,则首先关闭 该文件,然后再复制。

示例代码:
  1  #include <stdio.h>
  2  #include <stdlib.h>
  3  #include <unistd.h>
  4  #include <sys/types.h>
  5  #include <sys/stat.h>
  6  #include <fcntl.h>
  7                                                                                                                                      
  8  int main(int argc,char *argv[])
  9 
 10 {
 11     int fd1,fd2;
 12 
 13     fd1=open("test.txt",O_CREAT|O_WRONLY,0777);//fd=3
 14 
 15     if( fd1 < 0)
 16     {
 17         perror("open");
 18         exit(-1);
 19     }
 20 
 21     fd2=dup2(fd1,1);//关闭1再复制 =》重定向
 22 
 23     printf("fd2=%d\n",fd2);
 24 
 25     return 0;
 26 }
~        
运行结果:

示例代码2:
  1  #include <stdio.h>                                                                                                                  
  2  #include <stdlib.h>
  3  #include <unistd.h>
  4  #include <sys/types.h>
  5  #include <sys/stat.h>
  6  #include <fcntl.h>
  7  
  8  int main(int argc,char *argv[])
  9 
 10 {
 11     int fd1;
 12     int fd2 = 3;
 13     int err = 0;
 14 
 15     err = dup2(1,fd2);//fd2 和 1 都指向标准输出 
 16     if(err < 0)
 17     {
 18         perror("dup2");
 19     }
 20     printf("fd2=%d,err=%d\n",fd2,err);
 21 //此时文件列表:0 1 2 3 
 22     fd1=open("test.txt",O_CREAT|O_RDWR,S_IRWXU);
    //此时文件列表:0 1 2 3 4
 23     dup2(fd1,1);//先关闭1再复制 =》 标准输出-重定向-test.txt
 24     printf("hello world\n");
 25 
 26     dup2(fd2,1);//先关闭1再复制 =》重定向到标准输出(终端)
 27 
 28     printf("I love you \n");
 29 
 30     return 0;
 31 }

运行结果:

命名管道(FIFO)

命名管道(FIFO)和管道(pipe)基本相同,但也有一些显著的不同

其特点是:

  1. FIFO在文件系统中作为一个特殊的文件而存在
  2. 虽然FIFO文件存在于文件系统中,但FIFO中的内容却存放在内存中,在Linux中,该缓冲区的大小为4Kbyte
  3. FIFO有名字,不同的进程可以通过该命名管道进行通信
  4. FIFO所传送的数据是无格式的
  5. 从FIFO读数据是一次性操作,数据一旦被读,它就从FIFO中被抛弃,释放空间以便写更多的数据。
  6. 当共享FIFO的进程执行完所有的I/O操作以后,FIFO将继续保存在文件系统中以便以后使用。
FIFO文件的创建

#include <sys/types.h>
#include <sys/stat.h>

int mkfifo( const char *pathname, mode_t mode);
参数:

  1. pathname:FIFO的路径名+文件名
  2. mode:mode_t类型的权限描述符

返回值:

  1. 成功:返回 0
  2. 失败:如果文件已经存在,则会出错且返回-1
FIFO文件的读写

因为使用pipe的进程通过继承获得了pipe的文件描述符,所以pipe仅需要创建而不需要打开。

但是FIFO则需要打开,因为使用它们的进程可以没有任何关系。

一般文件的I/O函数都可以作用于FIFO,如open、close、read、write等。

注意: 当打开FIFO时,非阻塞标志(O_NONBLOCK)产生下列影响:
不指定O_NONBLOCK(即不写|O_NONBLOCK):

  1. 只读open要阻塞到某个其他进程为写而打开此FIFO
  2. 只写open要阻塞到某个其他进程为读而打开此FIFO
fifo_read示例代码:
  1 #include<stdio.h>
  2 #include<string.h>
  3 #include<unistd.h>
  4 #include<sys/types.h>
  5 #include<sys/stat.h>                                                                                                   
  6 #include<fcntl.h>
  7 
  8 int main(int argc,char *argv[])
  9 {
 10     int fd;
 11     int ret;
 12     char recv[100];     
 13 
 14     ret = mkfifo("my_fifo",S_IRUSR|S_IWUSR);
 15 
 16     if(ret != 0)
 17     {
 18         perror("mkfifo");   
 19     }
 20     
 21     printf("before open\n");
 22 
 23     fd = open("my_fifo",O_RDONLY);
 24     if(fd<0)
 25     {
 26         perror("open fifo");
 27     }
 28     printf("after open and before read\n");
 29     
 30     bzero(recv,sizeof(recv));
 31     read(fd,recv,sizeof(recv));
 32     
 33     printf("read form my_fifo buf[%s]\n",recv);
 34     return 0;
 35 }  

fifo_write示例代码:
  1 #include<stdio.h>                                                                                                      
  2 #include<string.h>
  3 #include<unistd.h>
  4 #include<sys/types.h>
  5 #include<sys/stat.h>
  6 #include<fcntl.h>
  7 
  8 int main()
  9 {
 10     int fd;
 11     int ret;
 12 
 13     char send[100] = "Hello I Love You !";
 14 
 15     ret = mkfifo("my_fifo",S_IRUSR|S_IWUSR);
 16 
 17     if(ret != 0)
 18     {
 19         perror("mkfifo");
 20     }
 21 
 22     printf("before open\n");
 23 
 24     fd = open("my_fifo",O_WRONLY);
 25     if(fd<0)
 26     {
 27         perror("open fifo");
 28     }
 29 
 30     printf("after open and before write \n");
 31     
 32     write(fd,send,sizeof(send));
 33     
 34     printf("wirte form my_fifo buf=[%s]\n",send);
 35 
 36     return 0;
 37 }  
运行结果:

消息队列(message queue)

:消息队列是消息的链表,存放在内存中,由内核维护

消息队列的特点:

  1. 消息队列允许一个或多个进程向它写入或者读取消息,并且每条消息都有类型。
  2. 消息队列可以实现消息的随机查询,消息不一定要以先进先出的次序读取,编程时可以按消息的类型读取。
  3. 与无名管道、有名管道一样,从消息队列中读出消息,消息队列中数据会被删除。
  4. 同样消息队列中的消息是有格式的。
  5. 只有内核重启或人工删除时,该消息才会被删除,若不人工删除消息队列,消息队列会一直存在于内存中
  6. 消息队列标识符,来标识消息队列。消息队列在整个系统中是唯一的。
  7. 在Linux操作系统中消息队列限制值如下:
    消息队列个数最多为16个
    消息队列总容量最多为16384字节
    每个消息内容最多为8192字节
ftok ()函数

#include <sys/types.h>
#include <sys/ipc.h>
key_t ftok(const char *pathname, int proj_id);

功能:

  1. 获得项目相关的唯一的IPC键值。

参数:

  1. pathname:路径名
  2. proj_id:项目ID,非0整数(只有低8位有效)

返回值:
4.成功返回key值,失败返回 -1。

创建消息队列msgget()函数:

#include <sys/msg.h>
int msgget(key_t key, int msgflg);

功能:

创建一个新的或打开一个已经存在的消息队列。不同的进程调用此函数,只要用相同的key值就能得到同一个消息队列的ID。

参数:

  1. key:IPC键值
  2. msgflg:标识函数的行为:IPC_CREAT(创建)或IPC_EXCL(如果已经存在则返回失败)。

返回值:

成功:消息队列的标识符,失败:返回-1。

使用shell命令操作消息队列:

查看消息队列:ipcs -q

删除消息队列:ipcrm -q msqid

消息队列的消息的格式

typedef struct _msg
{
long mtype; /* 消息类型 * /
char mtext[100]; / * 消息正文 * /
… / * 消息的正文可以有多个成员 * /
}MSG;

:消息类型必须是长整型的,而且必须是结构体类型的第一个成员,类型下面是消息正文,正文可以有多个成员(正文成员可以是任意数据类型的)。

发送消息msgsnd()函数:

#include <sys/msg.h>
int msgsnd(int msqid, const void *msgp,size_t msgsz, int msgflg);

功能:
将新消息添加到消息队列。

参数:
msqid:消息队列的队列ID。
msgp:待发送消息结构体的地址。
msgsz:消息正文的字节数。
msgflg:函数的控制属性

  1. 0:msgsnd调用阻塞直到条件满足为止。
  2. IPC_NOWAIT: 若消息没有立即发送则调用该函数的进程会立即返回。

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

接收消息msgrcv()函数:

#include <sys/msg.h>
ssize_t msgrcv(int msqid, void *msgp, size_t
msgsz, long msgtyp, int msgflg);

功能:
从ID为msqid的消息队列中接收一个消息。一旦接收消息成功,则消息在消息队列中被删除。

参数:

  1. msqid:消息队列的ID,代表要从哪个消息列中获取消息。
  2. msgp:存放消息结构体的地址。
  3. msgsz:消息正文的字节数。
  4. msgtyp:消息的类型、可以有以下几种类型
    msgtyp = 0:返回队列中的第一个消息
    msgtyp > 0:返回队列中消息类型为msgtyp的消息
    msgtyp < 0:返回队列中消息类型值小于或等于
    msgtyp绝对值的消息,如果这种消息有若干个,则取类型值最小的消息。

注意:若消息队列中有多种类型的消息,msgrcv获取消息的时候按消息类型获取,不是先进先出的。
在获取某类型消息的时,若队列中有多条此类型的消息,则获取最先添加的消息,即先进先出原则。

  1. msgflg:函数的控制属性
    0:msgrcv调用阻塞直到接收消息成功为止。
    MSG_NOERROR:若返回的消息字节数比nbytes字节数多,则消息就会截短到nbytes字节,且不通知消息发送进程。
    IPC_NOWAIT:调用进程会立即返回。若没有收到消息则立即返回-1。

返回值:成功返回读取消息的长度,失败返回-1。

msgctl函数

#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/msg.h>
int msgctl(int msqid,int cmd,struct msqid_ds* buf);

函数功能:主要用于对指定的消息队列执行指定的操作

第一参数:消息队列的ID,msgget函数返回值
第二个参数:操作的命令
IPC_RMID–删除指定的消息队列,此时第三个参数给NULL
第三个参数:结构体指针

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

message_queue_write示例代码:
  1 #include<stdio.h>                                                                                                      
  2 #include<unistd.h>
  3 #include<stdlib.h>
  4 #include<sys/types.h>
  5 #include<sys/ipc.h>
  6 #include<sys/msg.h>
  7 #include<string.h>
  8 
  9 typedef struct _msg
 10 {
 11     long mtype;
 12     char mtext[50];
 13 }MSG;
 14 
 15 int main(int argc,char *argv[])
 16 {
 17     key_t key;
 18     int msgqid;
 19     MSG msg;
 20 
 21     key = ftok(".",2020);
 22 
 23     msgqid = msgget(key,IPC_CREAT|0666);
 24     if(msgqid == -1)
 25     {
 26         perror("msgget");
 27         exit(-1);
 28     }
 29 
 30     msg.mtype = 10;
 31     
 32     strcpy(msg.mtext,"Hello world");
 33     
 34     msgsnd(msgqid,&msg,sizeof(msg.mtext),0);
 35 
 36     return 0;
 37 } 
message_queue_read示例代码:
  1 #include<stdio.h>                                                                                                      
  2 #include<stdlib.h>
  3 #include<unistd.h>
  4 #include<sys/types.h>
  5 #include<sys/ipc.h>
  6 #include<sys/msg.h>
  7 #include<string.h>
  8 
  9 typedef struct _msg
 10 {
 11     long mtype;
 12     char mtext[50];
 13 }MSG;
 14 
 15 int main(int argc,char *argv[])
 16 {
 17     key_t key;
 18     int msgqid;
 19     MSG msg;
 20 
 21     key = ftok(".",2020);
 22 
 23     msgqid = msgget(key,IPC_CREAT|0666);
 24     if(msgqid == -1)
 25     {
 26         perror("msgget");
 27         exit(-1);
 28     }
 29 
 30     msgrcv(msgqid,&msg,sizeof(msg.mtext),10,0);
 31 
 32     printf("msg.mtext=%s\n",msg.mtext);
 33 
 34     msgctl(msgqid,IPC_RMID,NULL);
 35 
 36     return 0;
 37 }         

运行结果:

示例代码2发送(重点):
//使用消息队列实现进程间的通信                                              
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/msg.h>
#include<signal.h>
//定义消息的数据类型
typedef struct
{
   long mtype; //消息的类型
   char buf[20]; //消息的内容
}Msg;
//定义全局变量,表示消息队列的ID
int msgid;
void fa(int signo)
{
   printf("正在删除消息队列,请稍后...\n");
   sleep(3);
   int res = msgctl(msgid,IPC_RMID,NULL);
   if(-1 == res)
  {
    perror("msgctl"),exit(-1);
  }
   printf("删除消息队列成功\n");
   exit(0); //终止当前进程
}
int main(void)
{
  //1.获取key值,使用ftok函数
  key_t key = ftok(".",100); //100是随便给的,.是当前路径
  if(-1 == key)
 {
    perror("ftok"),exit(-1);
 }
  printf("key = %#x\n",key);
  //2.创建消息队列,使用msgget函数
  msgid = msgget(key,IPC_CREAT|IPC_EXCL|0644);
  if(-1 == msgid)
 {
   perror("msgget"),exit(-1);
 }
  printf("msgid = %d\n",msgid);
 //3.发送消息,使用msgsnd函数
  Msg m1,m2;
  m1.mtype = 2;
  strcpy(m1.buf,"hello2");
  m2.mtype = 1;
  strcpy(m2.buf,"hello1");
  int res = msgsnd(msgid,&m1,sizeof(m1.buf),0); //第一条消息
  if(-1 == res)
 {
    perror("msgsnd"),exit(-1);
 }
  msgsnd(msgid,&m2,sizeof(m2.buf),0); //第二条消息
  printf("发送消息到消息队列成功\n");
 //4.如果不再使用,删除消息队列,msgctl函数
  printf("删除消息队列请按Ctrl+c...\n");
  signal(SIGINT,fa);
  while(1);
  return 0;
}  
示例代码2接收(重点):
//消息队列的消息读取                                                        
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/msg.h>
//定义消息的数据类型
typedef struct
{
  long mtype;  //消息类型
  char buf[20]; //消息内容
}Msg;
int main(void)
{
   //1.获取key值,使用ftok函数
   key_t key = ftok(".",100); //实参必须和01msgA.c中一样
   if(-1 == key)
  {
    perror("ftok"),exit(-1);
  }
   printf("key = %#x\n",key);
  //2.获取消息队列,使用msgget函数
  int msgid = msgget(key,0);
  if(-1 == msgid)
 {
    perror("msggid"),exit(-1);
 }
  printf("msgid = %d\n",msgid);
  //3.读取消息队列中消息,使用msgrcv函数
  Msg msg = {};
  //始终读取消息队列中的第一个消息
  int res = msgrcv(msgid,&msg,sizeof(msg.buf),0,0);   //最后一个参数0代表没有消息时默认阻塞
  //始终读取消息队列中第一个类型为1的消息
  //int res = msgrcv(msgid,&msg,sizeof(msg.buf),1,0);
  //始终读取 <= 2的消息,优先读取类型为1的消息
  //int res = msgrcv(msgid,&msg,sizeof(msg.buf),-2,0);
  if(-1 == res)
 {
    perror("msgrcv"),exit(-1);
 }
  printf("读取到的消息是:%ld,%s\n",msg.mtype,msg.buf);                
  return 0;
}

运行结果:


共享内存(shared memory重点)

:共享内存允许两个或者多个进程共享给定的存储区域。

共享内存是进程间共享数据的一种最快的方法,一个进程向共享的内存区域写入了数据,共享这个内存区域的所有进程就可以立刻看到其中的内容。

注意:多个进程之间对一个给定存储区访问的互斥。若一个进程正在向共享内存区写数据,则在它做完这一步操作前,别的进程不应当去读、写这些数据。

共享内存示意图:

shmget()函数

功能:获得一个共享存储标识符

#include <sys/ipc.h>
#include <sys/shm.h>
int shmget(key_t key, size_t size,intshmflg)

功能:创建或打开一块共享内存区

参数:

  1. key:IPC键值
  2. size:该共享存储段的长度(字节)
  3. shmflg:用来标识函数的行为:
    IPC_CREAT:如果不存在就创建
    IPC_EXCL:如果已经存在则返回失败
    IPC_NOWAIT:调用进程会立即返回。若发生错误则返回-1。
    SHM_R:可读
    SHM_W:可写

返回值:
4. 成功:返回共享内存标识符
5. 失败:返回-1。

使用shell命令操作共享内存:
查看共享内存:ipcs -m
删除共享内存:ipcrm -m shmid

共享内存映射(attach)

#include <sys/types.h>
#include <sys/shm.h>

void *shmat(int shmid, const void *shmaddr,int shmflg);

功能:将一个共享内存段映射到调用进程的数据段中。

参数:

  1. shmid:共享内存标识符。
  2. shmaddr:共享内存映射地址(若为NULL则由系统自动指定),推荐使用NULL。
  3. shmflg:共享内存段的访问权限和映射条件
    0:共享内存具有可读可写权限。
    SHM_RDONLY:只读。
    SHM_RND:(shmaddr非空时才有效)
    没有指定SHM_RND则此段连接到shmaddr所指定的地址上(shmaddr必需页对齐)。
    指定了SHM_RND则此段连接到shmaddr-shmaddr%SHMLAB 所表示的地址上。

返回值:
成功:返回共享内存段首地址
失败:返回 -1

注意: shmat函数使用的时候第二个和第三个参数一般设为NULL和0,即系统自动指定共享内存地址,并且共享内存可读可写。

解除共享内存映射(detach)

#include <sys/types.h>
#include <sys/shm.h>
int shmdt(const void *shmaddr);

功能:
将共享内存和当前进程分离(仅仅是断开联系并不删除共享内存)。

参数:
shmaddr:共享内存映射地址。

返回值:
成功返回 0,失败返回 -1。

共享内存控制

#include <sys/ipc.h>
#include <sys/shm.h>
int shmctl(int shmid, int cmd,struct shmid_ds *buf);

功能:共享内存空间的控制。
参数:

  1. shmid:共享内存标识符。
  2. cmd:函数功能的控制。
    IPC_RMID:删除。
    IPC_SET:设置shmid_ds参数。
    IPC_STAT:保存shmid_ds参数。
    SHM_LOCK:锁定共享内存段(超级用户)。
    SHM_UNLOCK:解锁共享内存段。
  3. buf:shmid_ds数据类型的地址,用来存放或更改消息队列的属性。

返回值:成功返回 :0 错误返回:-1

注意:SHM_LOCK用于锁定内存,禁止内存交换。并不代表共享内存被锁定后禁止其它进程访问。其真正的意义是:被锁定的内存不允许被交换到虚拟内存中。

这样做的优势在于让共享内存一直处于内存中,从而提高程序性能。

示例代码:
//shared_memory_write.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#define BUFSZ 2048

int main(int argc,char *argv[])
{
	int shmid;//共享内存id号
	int ret;
	key_t key;//key值
	char *shmadd;

	key = ftok(".",2012);//创建key值
	if(key == -1)
	{
		perror("ftok");
	}
	
	/*创建共享内存*/
	shmid = shmget(key,BUFSZ,SHM_R|SHM_W|IPC_CREAT);//可读 可写 创建
	if(shmid < 0)
	{
		perror("shmget");
		exit(-1);
	}

	/*共享内存映射*/
	shmadd = shmat(shmid,NULL,0);
	if(shmadd < 0)
	{
		perror("shmat");
		_exit(-1);
	}
	
	printf("copy data to shared-memory");

	bzero(shmadd,BUFSZ);//清空共享内存

	/*拷贝数据到共享内存区*/
	strcpy(shmadd,"data in shared memory");

	return 0;
}
//shared_memory_read.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>

#define BUFSZ 2048

int main(int argc,char *argv[])
{
	int shmid;//共享内存id号
	int ret;
	key_t key;//key值
	char *shmadd;

	key = ftok(".",2012);//创建key值
	if(key == -1)
	{
		perror("ftok");
	}
	
	system("ipcs -m");//在系统查看共享内存信息
	
	/*打开共享内存*/
	shmid = shmget(key,BUFSZ,SHM_R|SHM_W);
	if( shmid < 0)
	{
		perror("shmget");
		exit(-1);
	}
	
	/*内存映射*/
	shmadd = shmat(shmid,NULL,0);
	if(shmadd < 0)
	{
		perror("shmat");
		exit(-1);
	}

	/*映射*/
	shmadd = shmat(shmid,NULL,0);
	if(shmadd < 0)
	{
		perror("shmat");
		exit(-1);
	}

	/*读共享内存区数据*/
	printf("copy data form shared-memory\n");
	printf("data = [%s]\n",shmadd);

	/*分离共享内存和当前进程*/
	ret = shmdt(shmadd);//分离并不删除
	if(ret < 0)
	{
		perror("shmdt");
		exit(1);
	}
	else
	{
		printf("deleted shared-memory\n");
	}

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

	system("ipc -m");//在系统查看共享内存信息

	return 0;
}
运行结果:

有点长啦~为了大家更好的理解!!!
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

数字梦想家

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值