zoukankan      html  css  js  c++  java
  • APUE 学习笔记(十) 高级I/O

    1. Unix IPC(InterProcess Communication)

    同一主机的各个进程间的IPC:管道、FIFO、消息队列、信号量、共享存储器
    不同主机上的各个进程间IPC:socket套接字
     

    2. 管道

    管道进行IPC有两个局限:
    (1) 半双工,即数据只能在一个方向上流动
    (2) 只能在具有公共祖先的进程之间使用。通常,一个管道由一个进程创建,然后该进程调用fork,此后 父子进程之间可以使用该管道
     
    fstat函数对管道的每一端都返回一个FIFO类型的文件描述符,可以用S_ISFIFO宏来测试管道
     
    通常是一个进程调用pipe函数创建管道,紧接着调用fork,这样就创建了从父进程到子进程的IPC通道
     
    对于从父进程到子进程的管道,父进程关闭 管道的读端(fd[0])子进程关闭管道的写端(fd[1])
     
    popen、pclose两个函数实现的操作是:创建一个管道,调用fork产生一个子进程,关闭管道的不使用端,执行一个shell以运行命令,最后等待命令终止
     
    #include <stdio.h>
    
    FILE* popen(const char* cmdstring, const char* type);
    int   pclose(FILE* fp);

    函数popen先执行fork,然后调用exec以执行cmdstring,并且返回一个标准I/O文件指针

     

      

    type为“r”表示可读,为“w”表示可写
     
    简单实现一个popen:
     1 #include <unistd.h>
     2 #include <stdio.h>
     3 #include <errno.h>
     4 #include <sys/wait.h>
     5 #include <fcntl.h>
     6 
     7 /* pointer to array allocated at run-time */
     8 static pid_t* childpid = NULL;
     9 
    10 /* from our open_max() */
    11 static int maxfd;
    12 
    13 FILE* my_popen(const char* cmdstring, const char* type)
    14 {
    15     int pfd[2];
    16     pid_t pid;
    17 
    18     /* only allow type = "r" or "w" */
    19     if ((type[0] != 'r' && type[0] != 'w') || type[1] != 0) {
    20         errno = EINVAL;
    21         return NULL;
    22     }
    23        childpid = (pid_t*)calloc(maxfd, sizeof(pid_t));
    24         if (childpid == NULL) {
    25             return NULL;
    26         }
    27     }
    28 
    29     if (pipe(pfd) < 0) {
    30         return NULL;
    31     }
    32 
    33     if ((pid = fork()) < 0) {
    34         return NULL;
    35     } else if (pid == 0) {      /* child */
    36         if (*type == 'r') {
    37             close(pfd[0]);
    38             if (pfd[1] != STDOUT_FILENO) {
    39                dup2(pfd[1], STDOUT_FILENO);
    40                 close(pfd[1]);
    41             }
    42         } else {
    43             close(pfd[1]);
    44             if (pfd[0] != STDIN_FILENO) {
    45                 dup2(pfd[0], STDIN_FILENO);
    46                 close(pfd[0]);
    47             }
    48         }
    49 
    50         /* close all fds in childpid[] */
    51         for (int i = 0; i < maxfd; ++i) {
    52             if (childpid[i] > 0) {
    53                 close(i);
    54             }
    55         }
    56 
    57     }
    58 
    59     /* parent continue */
    60     FILE* fp;
    61     if (*type == 'r') {
    62         close(pfd[1]);
    63         if ((fp = fdopen(pfd[0], type)) == NULL) {
    64             return NULL;
    65         }
    66     } else {
    67         close(pfd[0]);
    68         if ((fp = fdopen(pfd[1], type)) == NULL) {
    69            return NULL;
    70         close(pfd[0]);
    71         if ((fp = fdopen(pfd[1], type)) == NULL) {
    72             return NULL;
    73         }
    74     }
    75 
    76     childpid[fileno(fp)] = pid;
    77     return fp;
    78 }
     

    3. 消息队列

    消息队列是消息的链接表,存放在内核中并由消息队列ID标识
    msgget用于创建一个新队列或打开一个现存的队列
    msgsnd将新消息添加到队列尾端
    msgrcv用户从队列中取消息
     

    4. 信号量

    信号量是一个计数器,用于多进程对共享数据对象的访问
     

    5. 共享存储器

    共享存储器允许多个进程共享一个给定的存储区,因为数据不需要在客户进程和服务器进程之间复制,所以这是最快的IPC
    使用共享存储区时必须确保 多个进程之间对给定的存储区的同步访问,通常,信号量被用来实现对共享存储区的同步访问
    #include <sys/shm.h>
    /* 获得共享存储标识符 */
    int shmget(key_t key, size_t size, int flag);
    
    /* 对共享存储区执行多种操作 */
    int shmctl(int shmid, int cmd, struct shmid_ds* buf);
    
    /* 进程将共享存储区连接到它的地址空间中 */
    void* shmat(int shmid, const void* addr, int flag);

    如果addr为0,则此段连接到内核选择的第一个可用地址上。一般将addr指定为0,以便由内核选择地址

    打印各种不同类型的数据所存放的位置:

     1 #include <unistd.h>
     2 #include <stdio.h>
     3 #include <stdlib.h>
     4 #include <sys/shm.h>
     5 
     6 #define ARRAY_SIZE 40000
     7 #define MALLOC_SIZE 100000
     8 #define SHM_SIZE 100000
     9 #define SHM_MODE 0600  /* user read/write */
    10 
    11 char array[ARRAY_SIZE];   /* uninitialized data = bss */
    12 
    13 int main(int argc, char* argv[])
    14 {
    15     int shmid;
    16     char* ptr = NULL;
    17     char* shmptr = NULL;
    18 
    19     fprintf(stdout, "array[] from %p to %p
    ", array, array + ARRAY_SIZE);
    20     fprintf(stdout, "stack around %p
    ", &shmid);
    21     ptr = (char*)malloc(MALLOC_SIZE);
    22     if (ptr == NULL) {
    23         fprintf(stderr, "malloc error
    ");
    24     }
    25 
    26     fprintf(stdout, "malloc from %p to %p
    ", ptr, ptr + MALLOC_SIZE);
    27 
    28     shmid = shmget(IPC_PRIVATE, SHM_SIZE, SHM_MODE);
    29     if (shmid < 0) {
    30         fprintf(stderr, "shmget error
    ");
    31     }
    32 
    33     shmptr = shmat(shmid, 0, 0);
    34     if (shmptr == (void*)-1) {
    35         fprintf(stderr, "shmat error
    ");
    36     }
    37 
    38     fprintf(stdout, "shared memory from %p to %p
    ", shmptr, shmptr + SHM_SIZE);
    39     if (shmctl(shmid, IPC_RMID, 0) < 0) {
    40         fprintf(stderr, "shmctl error
    ");
    41     }
    42     free(ptr);
    43     return 0;
    44 }

    在基于Intel的Linux系统上运行此程序,其输出如下:

     

     

    如果mmap函数指定了 MAP_SHARED标志,则此存储映射区可被多个进程共享
    匿名存储映射:调用mmap时指定MAP_ANON标志,并将文件描述符指定为-1,结果得到的映射区域是匿名的,因为并不与一个文件描述符相关联
  • 相关阅读:
    C#对象初始化器
    C#构造方法
    C#方法重载
    C#方法
    C#类 对象 字段和属性
    C#数组
    C#字符串
    C#原码反码补码
    字段、方法、属性
    单例模式、异常
  • 原文地址:https://www.cnblogs.com/wwwjieo0/p/3739107.html
Copyright © 2011-2022 走看看