zoukankan      html  css  js  c++  java
  • [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通信



    七,文件

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

  • 相关阅读:
    常用资源
    printf打印颜色
    Vue开发中踩坑-Day3
    Vue开发踩坑-Day2
    Vue开发中的踩坑-day1
    Python中virtualenv的使用
    第十一章:Python高级编程-协程和异步IO
    第十章:Python高级编程-多线程、多进程和线程池编程
    第九章:Python高级编程-Python socket编程
    自定义Element父子不关联的穿梭树
  • 原文地址:https://www.cnblogs.com/zhiliao112/p/4051370.html
Copyright © 2011-2022 走看看