[linux]进程(七)——进程通信

进程间通信
一,管道,
管道的限制:
(1)半双工,数据只能在一个方向上流动
(2)管道一般只在具有公共祖先的进程之间使用,通常一个管道由一个进程创建,然后该进程调用fork()函数,此后父子进程可以使用该管道
管道的创建:

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
 
  1. #include <unistd.h>  
  2. int pipe(int fileds[2]);  //filedes[0]为读而打开,filedes[1]为写而打开  

向一个没有读进程关联的管道写数据,会产生SIGPIPE,内核对于SIGPIPE的默认动作是退出该进程,这个通常不是我们期望看到的,因此我们需要重载这个信号处理方法,signal(SIGPIPE,SIG_IGN);


二,FIFO
FIFO被称为命名管道,和PIPE不同的是,通过FIFO不相关的进程也能交换数据
创建FIFO的方式如下

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
 
  1. int mkfifo(const char*pathname,mode_t mode);  

在创建了fifo后,一般对文件的操作函数都可以用于它,open close read write等,
fifo被多个进程用于写很常见,所以应该考虑进程间的同步,并且多个写进程写的数据之间不会穿插。答:PIPE_BUF规定了内核中管道缓冲区的大小,所有写的进程写的大小必须要小于PIPE_BUF的大小
FIFO可以用于客户进程与服务进程之间的通信,如何保证同步?
答:服务进程创建一个众所周知的FIFO用来接收客户进程的请求,然后根据客户进程id不一样,再为每个客户进程创建一个fifo用来给客户进程发送服务进程的回应消息,这样的问题是服务进程如何知道客户进程已经退出了?
FIFO的缺点是不支持随机访问,因为PIPE和FIFO都是先入先出的特点


三,消息队列
查看linux系统共享内存和消息队列的情况:

ipcs [-m|-q|-s]
-m 输出有关共享内存(shared memory)的信息
-q 输出有关信息队列(message queue)的信息
ipcrm命令

用来手动解除linux使用的共享内存~


四,共享内存
以下共享内存大部分来自以下网址点击打开链接
共享内存适合比较大的数据集,因为它使用内存,支持快速的随机访问,也是最快的IPC形式共享内存并不是从某一进程拥有的内存中划分出来的;进程的内存总是私有的
shm_open():创建共享内存段或连接到现有的已命名内存段。这个系统调用返回一个文件描述符。
shm_unlink():根据(shm_open() 返回的)文件描述符,删除共享内存段。实际上,这个内存段直到访问
它的所有进程都退出时才会删除,这与在 UNIX 中删除文件很相似。但是,调用 shm_unlink() (通常由原来创建共享内存段的进程调用)之后,其他进程就无法访问这个内存段了。
mmap():把共享内存段映射到进程的内存。这个系统调用需要 shm_open() 返回的文件描述符,它返回指向内存的指针。(在某些情况下,还可以把一般文件或另一个设备的文件描述符映射到内存。mmap的实现也是基于上述原理,在使用mmap映射某个文件(或者文件的一部分)到进程的地址空间时,并没有加载文件的数据,而只是在进程的虚拟地址空间划分出一块区域,标记这块区域用于映射到文件的数据区域,mmap的操作就完成了。
void* mmap ( void * addr , size_t len , int prot , int flags , int fd , off_t offset ) 
munmap():作用与 mmap() 相反。
msync():用来让共享内存段与文件系统同步 — 当把文件映射到内存时,这种技术有用。
使用共享内存的步骤:
使用共享内存的过程是,用 shm_open() 创建内存段,用 write() 或 ftruncate() 设置它的大小,用 mmap() 把它映射到进程内存,执行其他参与者需要的操作。当使用完时,原来的进程调用 munmap() 和 shm_unlink(),然后退出。

示例程序

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
 
  1. #include <stdio.h>  
  2. #include <string.h>  
  3. #include <stdlib.h>  
  4. #include <unistd.h>  
  5. #include <sys/file.h>  
  6. #include <sys/mman.h>  
  7. #include <sys/wait.h>  
  8. void error_and_die(const char *msg) {  
  9.   perror(msg);  
  10.   exit(EXIT_FAILURE);  
  11. }  
  12.   
  13. int main(int argc, char *argv[]) {  
  14.   int r;  
  15.   
  16.   const char *memname = "sample";  
  17.   const size_t region_size = sysconf(_SC_PAGE_SIZE);  
  18.   
  19.   int fd = shm_open(memname, O_CREAT | O_TRUNC | O_RDWR, 0666);  
  20.   if (fd == -1)  
  21.     error_and_die("shm_open");  
  22.   
  23.   r = ftruncate(fd, region_size);  
  24.   if (r != 0)  
  25.     error_and_die("ftruncate");  
  26.   
  27.   void *ptr = mmap(0, region_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);  
  28.   if (ptr == MAP_FAILED)  
  29.     error_and_die("mmap");  
  30.   close(fd);  
  31.   
  32.   pid_t pid = fork();  
  33.   
  34.   if (pid == 0) {  
  35.     u_long *d = (u_long *) ptr;  
  36.     *d = 0xdbeebee;  
  37.     exit(0);  
  38.   }  
  39.   else {  
  40.     int status;  
  41.     waitpid(pid, &status, 0);  
  42.     printf("child wrote %#lx ", *(u_long *) ptr);  
  43.   }  
  44.   r = munmap(ptr, region_size);  
  45.   if (r != 0)  
  46.     error_and_die("munmap");  
  47.   r = shm_unlink(memname);  
  48.   if (r != 0)  
  49.     error_and_die("shm_unlink");  
  50.   return 0;  
  51. }  


以下为另外一个示例程序

点击打开链接

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
 
  1. /*-------------map_normalfile1.c-----------*/  
  2. #include <sys/mman.h>  
  3. #include <sys/types.h>  
  4. #include <fcntl.h>  
  5. #include <unistd.h>  
  6. typedef struct{  
  7.   char name[4];  
  8.   int  age;  
  9. }people;  
  10. main(int argc, char** argv) // map a normal file as shared mem:  
  11. {  
  12.   int fd,i;  
  13.   people *p_map;  
  14.   char temp;  
  15.     
  16.   fd=open(argv[1],O_CREAT|O_RDWR|O_TRUNC,00777);  
  17.   lseek(fd,sizeof(people)*5-1,SEEK_SET);  
  18.   write(fd,"",1);  
  19.     
  20.   p_map = (people*) mmap( NULL,sizeof(people)*10,PROT_READ|PROT_WRITE,  
  21.         MAP_SHARED,fd,0 );  
  22.   close( fd );  
  23.   temp = 'a';  
  24.   for(i=0; i<10; i++)  
  25.   {  
  26.     temp += 1;  
  27.     memcpy( ( *(p_map+i) ).name, &temp,2 );  
  28.     ( *(p_map+i) ).age = 20+i;  
  29.   }  
  30.   printf(" initialize over   ");  
  31.   sleep(10);  
  32.   munmap( p_map, sizeof(people)*10 );  
  33.   printf( "umap ok  " );  
  34. }  
  35. /*-------------map_normalfile2.c-----------*/  
  36. #include <sys/mman.h>  
  37. #include <sys/types.h>  
  38. #include <fcntl.h>  
  39. #include <unistd.h>  
  40. typedef struct{  
  41.   char name[4];  
  42.   int  age;  
  43. }people;  
  44. main(int argc, char** argv)  // map a normal file as shared mem:  
  45. {  
  46.   int fd,i;  
  47.   people *p_map;  
  48.   fd=open( argv[1],O_CREAT|O_RDWR,00777 );  
  49.   p_map = (people*)mmap(NULL,sizeof(people)*10,PROT_READ|PROT_WRITE,  
  50.        MAP_SHARED,fd,0);  
  51.   for(i = 0;i<10;i++)  
  52.   {  
  53.   printf( "name: %s age %d; ",(*(p_map+i)).name, (*(p_map+i)).age );  
  54.   }  
  55.   munmap( p_map,sizeof(people)*10 );  
  56. }  

共享内存的限制:

点击打开链接

正常大小为32M,可以通过shmmax更改~

/proc/sys/kernel/shmmax


 



五,信号
软中断信号(signal,又简称为信号)用来通知进程发生了异步事件。进程之间可以互相通过系统调用kill发送软中断信号。
信号只是用来通知某进程发生了什么事件,并不给该进程传递任何数据。
在进程表的表项中有一个软中断信号域,该域中每一位对应一个信号,当有信号发送给进程时,对应位置位。由此可以看出,进程对不同的信号可以同时保留,但对于同一个信号,进程并不知道在处理之前来过多少个。
信号处理函数如下

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
 
  1. void (*signal(int signo,void (*func)(int)))(int)  

示例如下

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
 
  1. if(signal(SIGUSR1,sig_usr)==SIG_ERR)  
  2.     printf("can not catch SIG_USR1");  

kill函数用来将信号发送给进程或者进程组,raise函数则允许进程向自身发送信号

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
 
  1. int kill(pid_t pid,int signo);  
  2. int raise(int signo);  

信号的缺点是:不能用来传输数据,一般用来在进程之间的异常通知


六:socket通信



七,文件

有点是可以共享大量的数据,缺点是共享速度慢,因为涉及到了磁盘的读写,磁盘的速度远比不上内存,另外文件本身不安全,有些特权用户可以将文件删除。

原文地址:https://www.cnblogs.com/zhiliao112/p/4051370.html