Linux系统编程——进程间通信(System V IPC 对象)

基本查看命令

ipcs  -m查看共享内存        ipcs -s查看信号量        ipcs -q查看消息队列

ipcrm  -m  id 删除共享内存   -M+key值

ipcrm  -s  id 删除信号量

ipcrm  -q  id 删除消息队列

(1)共享内存。为了在多个进程间进行信息交换,内核专门留出了一块内存区,可以由需要访问的进程将其映射到自己的私有地址空间。共享内存允许两个或者更多进程共享一给定的存储区,是一种效率最高的进程间通信方式,因为数据不需要再服务端和客户端之间进行复制,进程可以直接读写内存。

由于多个进程共享一段内存,因此也要依靠某种同步机制,如互斥锁和信号量等。第一个运行的进程创建共享内存,多个进程进行映射访问,最后一个访问的进程关闭共享内存。

两个不同进程A、B共享内存的意思是:同一块物理内存被映射到进程A、B各自的进程地址空间。进程A可以即时看到进程B对共享内存中数据的更新,反之亦然。

 1 #include <signal.h>
 2 #include <sys/types.h>
 3 #include <sys/ipc.h>
 4 #include <errno.h>
 5 #include <sys/shm.h>
 6 typedef struct
 7 {
 8  pid_t pid;
 9  char text[1024];
10 }SHM;
11 void func(int sig_no)
12 {
13
14 }
15 int main(int argc, const char *argv[])
16 {
17  int shmid;
18  SHM *p;
19  key_t key;
20  pid_t peerpid;
21  signal(SIGUSR1,func);
22  key = ftok(".",‘s‘);
23  //shmget(,,0666);
24  //shmget(,,0666 | IPC_CREAT);
25  //shmget(,,0666 | IPC_CREAT | IPC_EXCL);
26  if((shmid = shmget(key,2048,IPC_CREAT | 0666 | IPC_EXCL)) < 0)
27  {
28   if(errno == EEXIST)
29   {
30    shmid = shmget(key,2048,0666);
31    if((p =(SHM *)shmat(shmid,NULL,0)) < (SHM *)0)
32    {
33     perror("fail to shmat");
34     return -1;
35    }
36    peerpid = p -> pid;
37    p->pid = getpid();
38    kill(peerpid,SIGUSR1);
39   }
40   else
41   {
42    perror("fail to shmget");
43    return -1;
44   }
45  }
46  else
47  {
48   if((p =(SHM *)shmat(shmid,NULL,0)) < (SHM *)0)
49   {
50    perror("fail to shmat");
51    return -1;
52   }
53   p->pid = getpid();
54   pause();
55   peerpid = p -> pid;
56  }
57
58 /**********************write.c***************************/
59  while(1)
60  {
61   fgets(p->text,1024,stdin);
62   kill(peerpid,SIGUSR1);
63   if(strncmp(p->text,"quit",4) == 0)break;
64   pause();
65  }
66 /*************************read.c************************/
67  while(1)
68  {
69   pause();
70   printf("read from shm:%s",p->text);
71   if(strncmp(p->text,"quit",4) == 0) break;
72   kill(peerpid,SIGUSR1);
73  }
74 /*************************************************/
75  if(shmdt(p) < 0)
76  {
77   perror("fail to shmdt");
78   return -1;
79  }
80  if(shmctl(shmid,IPC_RMID,NULL) < 0)
81  {
82   perror("fail to shmctl");
83   return -1;
84  }
85  return 0;

共享内存的使用步骤:

创建打开共享内存;

映射共享内存,即把指定的内存空间映射到进程的地址空间;

撤销共享内存映射;

删除共享内存对象;

采用共享内存通信的一个显而易见的好处是效率高,因为进程可以直接读写内存,而不需要任何数据的拷贝。对于像管道和消息队列等通信方式,则需要在内核和用户空间进行四次的数据拷贝,而共享内存则只拷贝两次数据:一次从输入文件到共享内存区,另一次从共享内存区到输出文件。

(2)消息队列。消息队列是一个消息的列表,存放在内核中并且由消息队列标识符标识,用户可以在消息队列中添加消息和读取消息等操作。

特点:是双向数据流,并且当没有读取到队列中的消息时,会阻塞等待。

消息队列克服了信号承载信息量少,管道只能承载无格式字节流以及缓冲区大小受限等缺点。

 1 #include <sys/types.h>
 2 #include <sys/ipc.h>
 3 #include <sys/msg.h>
 4 #define TYPEA 100
 5 #define TYPEB 200
 6 struct msgbuf
 7 {
 8  long mtype;
 9  char mtext[64];
10 };
11 int main(int argc, const char *argv[])
12 {
13  pid_t pid;
14  int len;
15  struct msgbuf msg;
16  key_t key;
17  int msgid;
18  len = sizeof(msg) - sizeof(long);
19  key = ftok(".",‘a‘);
20  msgid = msgget(key,0666 | IPC_CREAT);
21  pid = fork();
22  if(pid < 0)
23  {
24   perror("fail to fork");
25   return -1;
26  }
27  else if(pid == 0)
28  {
29   while(1)
30   {
31    fgets(msg.mtext,64,stdin);
32    msg.mtype = TYPEB;
33    msgsnd(msgid,&msg,len,0);
34    if(strncmp(msg.mtext,"quit",4) == 0)
35    {
36     kill(getppid(),SIGKILL);
37     exit(0);
38    }
39   }
40  }
41  else
42  {
43   while(1)
44   {
45    msgrcv(msgid,&msg,len,TYPEA,0);
46    printf("read from msg:%s",msg.mtext);
47    if(strncmp(msg.mtext,"quit",4) == 0)
48    {
49     kill(pid,SIGKILL);
50     wait(NULL);
51     msgctl(msgid,IPC_RMID,NULL);
52     exit(0);
53    }
54   }
55  }
56  return 0;
57 }

(3)信号灯集:是不同进程间或者是同一进程间不同线程间同步的机制。

共享资源的获取:

1.测试控制资源的信号量;

2.若信号量的值为正,则进程可以使用该资源。进程将信号量减一,表示它使用了一个资源单位。

3.若信号量的值为负,进程进入休眠状态,直到信号量值大于0 。进程被唤醒后,返回第一步。

当进程不再使用由一个信号量控制的共享资源时,该信号量减1.如果有进程正在休眠等待此信号,则唤醒他们。

为了正确的实现信号量,信号量的测试操作以及减1 操作都是原子操作。为此,信号量通常是在内核中实现。

常用的信号量形式被称为二元信号量或者是双态信号量。它控制单个资源,初始值为1 。

 1 #include <sys/types.h>
 2 #include <sys/ipc.h>
 3 #include <sys/shm.h>
 4 #include <sys/sem.h>
 5 #include <errno.h>
 6 union semun
 7 {
 8      int val;
 9 };
10 //0:write信号灯 1:read信号灯
11 int main(int argc, const char *argv[])
12 {
13  key_t key;
14  int semid,shmid;
15  union semun myun;
16  char * p;
17  struct sembuf buf;
18  key = ftok(".",‘b‘);
19  if((semid = semget(key,2,IPC_CREAT | IPC_EXCL | 0666)) < 0)
20  {
21   if(errno == EEXIST)
22   {
23    semid = semget(key,2,0666);
24   }
25   else
26   {
27    perror("fail to semget");
28    return -1;
29   }
30  }
31  else
32  {//先运行,对信号灯集进行初始化
33   myun.val = 1;//write
34   semctl(semid,0,SETVAL,myun);
35   myun.val = 0;//read
36   semctl(semid,1,SETVAL,myun);
37  }
38  shmid = shmget(key,64,IPC_CREAT | 0666);
39  p = (char *)shmat(shmid,NULL,0);
40
41  while(1)
42  {
43   //对read进行p操作(申请读资源)
44   buf.sem_num = 1;
45   buf.sem_op = -1;
46   buf.sem_flg = 0;
47   semop(semid,&buf,1);
48   printf("read from shm :%s",p);
49   //对write进行v操作(释放写资源)
50   buf.sem_num = 0;
51   buf.sem_op = 1;
52   buf.sem_flg = 0;
53   semop(semid,&buf,1);
54  }
55
56  return 0;
57 }

由于不同的进程运行在各自不同的内存空间中.一方对于变量的修改另一方是无法感知的.因此.进程之间的信息传递不可能通过变量或其它数据结构直接进行,只能通过进程间通信来完成。

根据进程通信时信息量大小的不同,可以将进程通信划分为两大类型:控制信息的通信和大批数据信息的通信.前者称为低级通信,后者称为高级通信。

低级通信主要用于进程之间的同步、互斥、终止、挂起等等控制信息的传递。

高级通信主要用于进程间数据块的交换和共享 常见的高级通信有管道(PIPE)、消息队列(MESSAGE)、共享内存(SHARED MEM0RY)等。

这里主要比较一下高级通信的这三种方式的特点。

 

管道通信(PIPE)
       两个进程利用管道进行通信时.发送信息的进程称为写进程.接收信息的进程称为读进程。管道通信方式的中间介质就是文件.通常称这种文件为管道文件.它就像管道一样将一个写进程和一个读进程连接在一起,实现两个进程之间的通信。写进程通过写入端(发送端)往管道文件中写入信息;读进程通过读出端(接收端)从管道文件中读取信息。两个进程协调不断地进行写和读,便会构成双方通过管道传递信息的流水线。
      利用系统调用PIPE()可以创建一个无名管道文件,通常称为无名管道或PIPE;利用系统调用MKNOD()可以创建一个有名管道文件.通常称为有名管道或FIFO。无名管道是一种非永
久性的管道通信机构.当它访问的进程全部终止时,它也将随之被撤消。无名管道只能用在具有家族联系的进程之间。有名管道可以长期存在于系统之中.而且提供给任意关系的进程使用,但是使用不当容易导致出错.所以操作系统将命名管道的管理权交由系统来加以控制管道文件被创建后,可以通过系统调用WRITE()和READ()来实现对管道的读写操作;通信完后,可用CLOSE()将管道文件关闭。

 

消息缓冲通信(MESSAGE)
      多个独立的进程之间可以通过消息缓冲机制来相互通信.这种通信的实现是以消息缓冲区为中间介质.通信双方的发送和接收操作均以消息为单位。在存储器中,消息缓冲区被组织成队列,通常称之为消息队列。消息队列一旦创建后即可由多进程共享.发送消息的进程可以在任意时刻发送任意个消息到指定的消息队列上,并检查是否有接收进程在等待它所发送的消息。若有则唤醒它:而接收消息的进程可以在需要消息的时候到指定的消息队列上获取消息.如果消息还没有到来.则转入睡眠状态等待。

 

共享内存通信(SHARED MEMORY)
      针对消息缓冲需要占用CPU进行消息复制的缺点.OS提供了一种进程间直接进行数据交换的通信方式一共享内存 顾名思义.这种通信方式允许多个进程在外部通信协议或同步,互斥机制的支持下使用同一个内存段(作为中间介质)进行通信.它是一种最有效的数据通信方式,其特点是没有中间环节.直接将共享的内存页面通过附接.映射到相互通信的进程各自的虚拟地址空间中.从而使多个进程可以直接访问同一个物理内存页面.如同访问自己的私有空间一样(但实质上不是私有的而是共享的)。因此这种进程间通信方式是在同一个计算机系统中的诸进程间实现通信的最快捷的方法.而它的局限性也在于此.即共享内存的诸进程必须共处同一个计算机系统.有物理内存可以共享才行。

三种方式的特点(优缺点):

1.无名管道简单方便.但局限于单向通信的工作方式.并且只能在创建它的进程及其子孙进程之间实现管道的共享:有名管道虽然可以提供给任意关系的进程使用.但是由于其长期存在于系统之中,使用不当容易出错。

2.消息缓冲可以不再局限于父子进程.而允许任意进程通过共享消息队列来实现进程间通信.并由系统调用函数来实现消息发送和接收之间的同步.从而使得用户在使用消息缓冲进行通信时不再需要考虑同步问题.使用方便,但是信息的复制需要额外消耗CPU的时间.不适宜于信息量大或操作频繁的场合。

3.共享内存针对消息缓冲的缺点改而利用内存缓冲区直接交换信息,无须复制,快捷、信息量大是其优点。但是共享内存的通信方式是通过将共享的内存缓冲区直接附加到进程的虚拟地址空间中来实现的.因此,这些进程之间的读写操作的同步问题操作系统无法实现。必须由各进程利用其他同步工具解决。另外,由于内存实体存在于计算机系统中.所以只能由处于同一个计算机系统中的诸进程共享。不方便网络通信。

时间: 2024-08-24 06:29:43

Linux系统编程——进程间通信(System V IPC 对象)的相关文章

进程间同步---system v ipc 对象信号灯集

一.信号灯简介 Linux支持System V的信号灯(semaphore),是一种进程间通信的方式,只不过它和管道.FIFO或者共享内存不一样,信号灯主要用于同步或者互斥对共享资源的访问,它的发明来源于火车运行系统中的"信号灯",利用信号灯可以实现"PV"操作这种进程间同步进制.P操作时获得资源,将信号灯的值减1,如果结果不为负则执行完毕,进程获得资源,否则进程睡眠以等待的进程释放;V操作则是释放资源,给信号灯的值加1, 唤醒一个因执行P操作而等待的进程. 二.信

linux进程间通讯-System V IPC 信号量

进程间通信的机制--信号量.注意请不要把它与之前所说的信号混淆起来,信号与信号量是不同的两种事物.有关信号的更多内容,可以阅读我的另一篇文章:Linux进程间通信--使用信号.下面就进入信号量的讲解. 一.什么是信号量 为了防止出现因多个程序同时访问一个共享资源而引发的一系列问题,我们需要一种方法,它可以通过生成并使用令牌来授权,在任一时刻只能有一个执行线程访问代码的临界区域.临界区域是指执行数据更新的代码需要独占式地执行.而信号量就可以提供这样的一种访问机制,让一个临界区同一时间只有一个线程在

Linux系统编程——进程间通信:消息队列

消息队列提供了一种在两个不相关的进程之间传递数据的简单高效的方法,其特点如下: 1)消息队列可以实现消息的随机查询.消息不一定要以先进先出的次序读取,编程时可以按消息的类型读取. 2)消息队列允许一个或多个进程向它写入或者读取消息. 3)与无名管道.命名管道一样,从消息队列中读出消息,消息队列中对应的数据都会被删除. 4)每个消息队列都有消息队列标识符,消息队列的标识符在整个系统中是唯一的. 5)消息队列是消息的链表,存放在内存中,由内核维护.只有内核重启或人工删除消息队列时,该消息队列才会被删

Linux系统编程——进程间通信:信号中断处理

什么是信号? 信号是 Linux 进程间通信的最古老的方式.信号是url=474nN303T2Oe2ehYZjkrggeXCaJPDSrmM5Unoh4TTuty4wSgS0nl4-vl43AGMFbo0_5uH5OQFr_vaRJaZ-3lq_" style="color:rgb(202,0,0); text-decoration:none">软件中断,它是在软件层次上对中断机制的一种模拟,是一种异步通信的方式 . 信号能够导致一个正在执行的进程被还有一个正在执行的异

Linux系统编程——进程间通信:共享内存

概述 共享内存是进程间通信中最简单的方式之一.共享内存允许两个或更多进程访问同一块内存,就如同 malloc() 函数向不同进程返回了指向同一个物理内存区域的指针.当一个进程改变了这块地址中的内容的时候,其它进程都会察觉到这个更改. 共享内存的特点: 1)共享内存是进程间共享数据的一种最快的方法. 一个进程向共享的内存区域写入了数据,共享这个内存区域的所有进程就可以立刻看到其中的内容. 2)使用共享内存要注意的是多个进程之间对一个给定存储区访问的互斥. 若一个进程正在向共享内存区写数据,则在它做

Linux系统编程——进程间通信:管道(pipe)

管道的概述 管道也叫无名管道,它是是 UNIX 系统 IPC(进程间通信) 的最古老形式,所有的 UNIX 系统都支持这种通信机制. 无名管道有如下特点: 1.半双工,数据在同一时刻只能在一个方向上流动. 2.数据只能从管道的一端写入,从另一端读出. 3.写入管道中的数据遵循先入先出的规则. 4.管道所传送的数据是无格式的,这要求管道的读出方与写入方必须事先约定好数据的格式,如多少字节算一个消息等. 5.管道不是普通的文件,不属于某个文件系统,其只存在于内存中. 6.管道在内存中对应一个缓冲区.

Linux系统编程——进程间通信(一)

基本操作命令: ps -ajx/-aux/-ef 查看进程间状态/的相互关系 top 动态显示系统中的进程 nice 按照指定的优先级运行 /renice 改变正在运行的进程的优先级 kill -9杀死进程 jobs 查看后台进程数 进程的结构.类型.状态.模式 0.Linux中进程包括三段: (1)数据段.存放的是全局变量,常量以及动态内存的数据空间. (2)正文段.存放的是程序中的代码. (3)堆栈段.存放的是函数的返回地址,函数的参数以及局部变量. 进程类型:交互进程,既可以在前台运行,也

Linux系统编程——进程间通信:命名管道(FIFO)

命名管道的概述 无名管道,由于没有名字,只能用于亲缘关系的进程间通信(更多详情,请看<无名管道>).为了克服这个缺点,提出了命名管道(FIFO),也叫有名管道.FIFO 文件. 命名管道(FIFO)不同于无名管道之处在于它提供了一个路径名与之关联,以 FIFO 的文件形式存在于文件系统中,这样,即使与 FIFO 的创建进程不存在亲缘关系的进程,只要可以访问该路径,就能够彼此通过 FIFO 相互通信,因此,通过 FIFO 不相关的进程也能交换数据. 命名管道(FIFO)和无名管道(pipe)有一

Linux程序设计学习笔记----System V进程间通信(信号量)

关于System V Unix System V,是Unix操作系统众多版本中的一支.它最初由AT&T开发,在1983年第一次发布,因此也被称为AT&T System V.一共发行了4个System V的主要版本:版本1.2.3和4.System V Release 4,或者称为SVR4,是最成功的版本,成为一些UNIX共同特性的源头,例如"SysV 初始化脚本"(/etc/init.d),用来控制系统启动和关闭,System V Interface Definitio