zoukankan      html  css  js  c++  java
  • 〖Linux〗Linux高级编程

    [转自: http://blog.csdn.net/Paradise_for_why/article/details/5550619]

    这一章就是著名的IPC,这个东西实际的作用和它的名字一样普及。例如我们浏览网页,打印文章,等等。

        IPC总共有五种类型:

    1. 共享内存(Shared Memory):最容易理解的一种,就像一个特工把情报放在特定地点(内存),另一个特工再过来取走一样。

    2. 内存映射(Mapped Memory):和共享内存几乎相同,除了特工们把地点从内存改成了文件系统。

    3. 管道(Pipes):从一个进程到另一个进程的有序通信,用电话来比喻再恰当不过了。

    4. FIFOs:和管道和类似,唯一的区别是FIFOs比管道更神通一些,允许没有关系的进程之间的有序通信。

    5. 套接字(Sockets):为什么说浏览网页也是IPC?就是因为它。 

    5.1 共享内存(Shared Memory)

    • 共享内存是最快捷的进程间通信方式。访问共享内存的效率和访问进程自己的非共享内存的效率是相同的,而且这种通信方式不需要任何额外的系统调用。
    • 系统不会自动为共享内存处理同步问题,这个问题必须由用户自己解决。
    • 共享内存的步骤通常是:
      • 一个进程申请一块共享内存,即在它的页表中加入新的一项
      • 所有进程Attach该共享内存,即从申请内存的进程中拷贝对应的页表
      • 使用该内存进行通讯
      • 结束后所有进程detach该共享内存
      • 申请共享内存的进程在确定所有进程都detach后,释放该内存
    • 由于共享内存是通过页表来实现的,我们可以得出一个结论:共享内存的大小是页面大小的整数倍,页面的大小可以通过getpagesize()来得到,通常在Linux下该值是4KB
    • 相关的API函数:
      • 申请共享内存:shmget,返回共享内存segment的id
      • Attach,Detach函数:shmat,shmdt。需要共享内存segment的id
      • 释放申请的内存:shmctl。一定要记得释放!调用exit和exec会自动detach,但不会自动释放。
    • 使用 ipcs -m来观看当前系统存在的共享内存

      例子:原程序链接,依据这个例子进行简单修改一下

      1 /*
      2  * =============================================================================
      3  *
      4  *       Filename:  sharememory_read.c
      5  *
      6  *    Description:  
      7  *
      8  *        Version:  1.0
      9  *        Created:  2014年11月04日 19时52分28秒
     10  *       Revision:  none
     11  *       Compiler:  gcc
     12  *
     13  *         Author:  lwq (28120), scue@vip.qq.com
     14  *   Organization:  
     15  *
     16  * =============================================================================
     17  */
     18 #include <stdlib.h>
     19 
     20 /**********************************************************
     21 *实验要求:   创建两个进程,通过共享内存进行通讯。
     22 *功能描述:   本程序申请和分配共享内存,然后轮训并读取共享中的数据,直至
     23 *           读到“end”。
     24 *日    期:   2010-9-17
     25 *作    者:   国嵌
     26 **********************************************************/
     27 #include <unistd.h>
     28 #include <stdlib.h>
     29 #include <stdio.h>
     30 #include <string.h>
     31 #include <sys/types.h>
     32 #include <sys/ipc.h>
     33 #include <sys/shm.h>
     34 #include <getopt.h>
     35 #include "sharememory.h"
     36 
     37 void read_shm(struct shared_use_st *shared_stuff);
     38 void write_shm(struct shared_use_st *shared_stuff);
     39 void del_shm();
     40 
     41 void usage(){
     42     fprintf(stderr, "
    usage: %s -r|-w
    
    "
     43             "-r read mode
    "
     44             "-w write mode
    "
     45             "
    ", "shared_memory");
     46     exit(1);
     47 }
     48 
     49 #define READ (1)
     50 #define WRITE (2)
     51 #define OPTNONE (0)
     52 
     53 // 全局变量
     54 void *shared_memory=(void *)0;
     55 
     56 /*
     57  * 程序入口
     58  * */
     59 int main(int argc, char **argv)
     60 {
     61     int running=RUNNING;
     62     struct shared_use_st *shared_stuff;
     63     int shmid;
     64     int operation=OPTNONE;                      /* 读/写操作 */
     65 
     66     /*-----------------------------------------------------------------------------
     67      *  getopt start
     68      *----------------------------------------------------------------------------*/
     69     int choice;
     70     while (1)
     71     {
     72         static struct option long_options[] =
     73         {
     74             /* Use flags like so:
     75             {"verbose",    no_argument,    &verbose_flag, 'V'}*/
     76             /* Argument styles: no_argument, required_argument, optional_argument */
     77             {"version", no_argument,    0,    'v'},
     78             {"help",    no_argument,    0,    'h'},
     79             {"read",    no_argument,    0,  'r'},
     80             {"write",   no_argument,    0,  'w'},
     81             {0,0,0,0}
     82         };
     83     
     84         int option_index = 0;
     85     
     86         /* Argument parameters:
     87             no_argument: " "
     88             required_argument: ":"
     89             optional_argument: "::" */
     90     
     91         choice = getopt_long( argc, argv, "vhrw",
     92                     long_options, &option_index);
     93     
     94         if (choice == -1)
     95             break;
     96     
     97         switch( choice )
     98         {
     99             case 'v':
    100                 
    101                 break;
    102     
    103             case 'h':
    104                 usage();
    105                 break;
    106 
    107             case 'r':
    108                 operation=READ;
    109                 break;
    110 
    111             case 'w':
    112                 operation=WRITE;
    113                 break;
    114     
    115             case '?':
    116                 /* getopt_long will have already printed an error */
    117                 usage();
    118                 break;
    119     
    120             default:
    121                 /* Not sure how to get here... */
    122                 return EXIT_FAILURE;
    123         }
    124     }
    125     if (operation == OPTNONE) {
    126         usage();
    127     }
    128     /*-----------------------------------------------------------------------------
    129      *  getopt end
    130      *----------------------------------------------------------------------------*/
    131 
    132     /*创建共享内存*/
    133     shmid=shmget((key_t)1234,sizeof(struct shared_use_st),0666|IPC_CREAT);
    134     if(shmid==-1) {
    135         fprintf(stderr,"shmget failed
    ");
    136         exit(EXIT_FAILURE);
    137     }
    138 
    139     /*映射共享内存*/
    140     shared_memory=shmat(shmid,(void *)0,0);
    141     if(shared_memory==(void *)-1) {
    142         fprintf(stderr,"shmat failed
    ");
    143         exit(EXIT_FAILURE);
    144     }
    145 
    146     printf("Memory attached at 0%08x
    ",(int)((intptr_t)shared_memory));
    147 
    148     /*让结构体指针指向这块共享内存*/
    149     shared_stuff=(struct shared_use_st *)shared_memory;
    150 
    151     /*控制读写顺序*/
    152     // lwq: 使之能读取上一条消息
    153     if (operation == READ && shared_stuff->written_by_you != HADWROTE)
    154         shared_stuff->written_by_you=HADREAD;
    155 
    156     switch(operation) {
    157         case READ:
    158             read_shm(shared_stuff);
    159             break;
    160     
    161         case WRITE:
    162             write_shm(shared_stuff);
    163             break;
    164 
    165         default:
    166             usage();
    167             break;
    168     }
    169 
    170     del_shm();
    171     exit(EXIT_SUCCESS);
    172 }
    173 
    174 // 读取共享内存
    175 void read_shm(struct shared_use_st *shared_stuff){
    176     while(1) {
    177        if(shared_stuff->written_by_you == HADWROTE) {
    178            printf("You wrote:%s",shared_stuff->some_text);
    179            shared_stuff->written_by_you=HADREAD;
    180            if(strncmp(shared_stuff->some_text,"end",3)==0) {
    181                break;
    182            }
    183        }
    184        else {
    185            usleep(100000);
    186        }
    187     }
    188 }
    189 
    190 // 写入共享内存
    191 void write_shm(struct shared_use_st *shared_stuff){
    192     char buffer[BUFSIZ] = {0};
    193     while(1) {
    194         while(shared_stuff->written_by_you!=HADREAD); /* 等待读写完成 */
    195         printf("Enter some text:");
    196         fgets(buffer,BUFSIZ,stdin);
    197         strncpy(shared_stuff->some_text,buffer,TEXT_SZ); /* 复制进去 */
    198         shared_stuff->written_by_you=HADWROTE;
    199         if(strncmp(buffer,"end",3)==0) {
    200             break;
    201         }
    202     }
    203 }
    204 
    205 // 删除共享内存
    206 void del_shm(){
    207     /*删除共享内存*/
    208     if(shmdt(shared_memory)==-1) {
    209         fprintf(stderr,"
    shmdt failed
    ");
    210         exit(EXIT_FAILURE);
    211     }
    212     else {
    213         fprintf(stderr, "
    delete shared_memory: 0x%08x
    ", (int)((intptr_t)shared_memory));
    214     }
    215 }
    sharememory.c

      编译:gcc sharememory.c -o sharememory

      执行:

        1. 以读取模式打开程序(进程1): ./sharememory -r

        2. 以写入模式打开程序(进程2): ./sharememory -w

      更新介绍:http://www.cs.cf.ac.uk/Dave/C/node27.html 

    5.2 进程信号量

    • 信号量(Semaphore)的概念前面已经介绍过了。Linux对用来同步进程的信号量采取了一种特别的实现方式。这些信号量也就被称为进程信号量(Process Semaphore)。(这一节下面所提到的所有信号量默认都是指进程信号量)
    • 相关的API函数:
      • 申请:semget
      • 释放:semctl。需要注意的是信号量不会被自动释放,我们必须显式释放它。
      • Wait和Post:semop
    • 使用ipcs -s来观看当前系统存在的信号量

      例子:原程序链接

      1 /*
      2  * =============================================================================
      3  *
      4  *       Filename:  semaphore_simple.c
      5  *
      6  *    Description:  
      7  *
      8  *        Version:  1.0
      9  *        Created:  2014年11月04日 21时21分25秒
     10  *       Revision:  none
     11  *       Compiler:  gcc
     12  *
     13  *         Author:  lwq (28120), scue@vip.qq.com
     14  *   Organization:  
     15  *
     16  * =============================================================================
     17  */
     18 #include <stdlib.h>
     19 
     20 #include <unistd.h>
     21 #include <sys/types.h>
     22 #include <sys/stat.h>
     23 #include <fcntl.h>
     24 #include <stdlib.h>
     25 #include <stdio.h>
     26 #include <string.h>
     27 #include <sys/sem.h>
     28 
     29 union semun
     30 {
     31     int val;
     32     struct semid_ds *buf;
     33     unsigned short *arry;
     34 };
     35 
     36 static int sem_id = 0;
     37 
     38 static int set_semvalue();
     39 static void del_semvalue();
     40 static int semaphore_p();
     41 static int semaphore_v();
     42 
     43 int main(int argc, char *argv[])
     44 {
     45     char msg[1024] = {0};
     46     int i = 0;
     47     int mypid=getpid();
     48 
     49     //创建信号量
     50     sem_id = semget((key_t)12345, 1, 0666 | IPC_CREAT);
     51 
     52     if(argc > 1)
     53     {
     54         //程序第一次被调用,初始化信号量
     55         if(!set_semvalue()) {
     56             fprintf(stderr, "Failed to initialize semaphore
    ");
     57             exit(EXIT_FAILURE);
     58         }
     59     }
     60     for(i = 0; i < 10; ++i) {
     61         //进入临界区
     62         if(!semaphore_p())
     63             exit(EXIT_FAILURE);
     64         //向屏幕中输出数据
     65         snprintf(msg, sizeof(msg)-1, "%s, index: %03d, pid: %05d", argv[1], i, mypid);
     66         printf("%s
    ", msg);
     67         fflush(stdout);
     68         usleep(100000);
     69         //离开临界区,休眠随机时间后继续循环
     70         if(!semaphore_v())
     71             exit(EXIT_FAILURE);
     72     }
     73 
     74     if(argc > 1)
     75     {
     76         //如果程序是第一次被调用,则在退出前删除信号量
     77         sleep(3);
     78         del_semvalue();
     79     }
     80     exit(EXIT_SUCCESS);
     81 }
     82 
     83 static int set_semvalue()
     84 {
     85     //用于初始化信号量,在使用信号量前必须这样做
     86     union semun sem_union;
     87 
     88     sem_union.val = 1;
     89     if(semctl(sem_id, 0, SETVAL, sem_union) == -1)
     90         return 0;
     91     return 1;
     92 }
     93 
     94 static void del_semvalue()
     95 {
     96     //删除信号量
     97     union semun sem_union;
     98 
     99     if(semctl(sem_id, 0, IPC_RMID, sem_union) == -1)
    100         fprintf(stderr, "Failed to delete semaphore
    ");
    101 }
    102 
    103 static int semaphore_p()
    104 {
    105     //对信号量做减1操作,即等待P(sv)
    106     struct sembuf sem_b;
    107     sem_b.sem_num = 0;
    108     sem_b.sem_op = -1;//P()
    109     sem_b.sem_flg = SEM_UNDO;
    110     if(semop(sem_id, &sem_b, 1) == -1)
    111     {
    112         fprintf(stderr, "semaphore_p failed
    ");
    113         return 0;
    114     }
    115     return 1;
    116 }
    117 
    118 static int semaphore_v()
    119 {
    120     //这是一个释放操作,它使信号量变为可用,即发送信号V(sv)
    121     struct sembuf sem_b;
    122     sem_b.sem_num = 0;
    123     sem_b.sem_op = 1;//V()
    124     sem_b.sem_flg = SEM_UNDO;
    125     if(semop(sem_id, &sem_b, 1) == -1)
    126     {
    127         fprintf(stderr, "semaphore_v failed
    ");
    128         return 0;
    129     }
    130     return 1;
    131 }
    semaphore_simple.c

      编译:gcc semaphore_simple.c -o semaphore_simple

      执行:./semaphore_simple XXX &; ./semaphore_simple YYY

    5.3 内存映射

    • 内存映射使得不同的进程可以通过一个共享文件来互相通信。
    • 相关的API函数:
      • 映射:mmap
      • 同步:msync。用来指定对文件的修改是否被buffer。
      • 释放:munmap。在程序结束的时候会自动unmap
    • mmap的其他用法:
      • 可以替代read和write,有时使用内存映射后的效率比单纯使用I/O操作来的更快
      • 在内存映射文件中构建structure,修改structure再次将文件映射到内存中可以快速的将structure恢复到原来的状态
      • 把/dev/zero文件映射到内存中。该文件可以提供无限的0,并且写到该文件的所有内容将被直接丢弃

    5.4 管道(Pipes)

    • 管道是单向的,即一个线程写,另一个线程读,无法互换
    • 如果写的速度太快,造成管道满了,那么写的线程就会被block;如果读的速度太快,造成管道空了,那么读的进程就会被block。因此事实上我们可以说管道自动实现了同步机制
    • 我们可以通过调用pipe函数来生成一对pipe file description。(为什么是一对?因为一个读一个写)。可是,生成的pipe file description无法传送给不相关的进程(因为做为file descriptor即使它拿到了也没法用)。但是我们注意到fork之后父进程所有的file descriptor在子进程中依然有效,因此管道最大的作用是在父子进程之间通信。或者更确切的说,是在有共同祖先的进程之间通信。
    • 典型的创建管道的流程如下:
      • 用pipe生成2个pipe file description(简称fds)。然后调用fork
      • 在父进程关闭fds[0](或fds[1]),并以只读(或只写)方式打开fds[1](或fds[0])。在子进程中关闭fds[1](或fds[0]),并以只写(或只读)方式打开fds[0](或fds[1])。打开的函数是fdopen。
      • 开始通信。结束后用close函数关闭剩下的fds。
    • 这里有一个技巧:可以利用管道来达成重定向stdin, stdout和stderr。注意到dup2这个API可以把一个file descriptor复制到另一个上。
    • 事实上,我们有一对更为简洁的函数popen/pclose来完成上面的一系列复杂的操作。popen有两个参数:
      • 第一个参数接受一个exec,子进程将执行这个exec
      • 第二个参数为”w”或者”r”,”w”表示父进程写子进程读,”r”则反之
      • 返回值为管道的一端,也就是一个file descriptor
      • pclose用来关闭popen返回的file descriptor
    • FIFO(First In First Out)文件事实上是一个有名字的管道,换句话说,他可以用来让“不相干”的程序互相通信。
      • 我们使用mkfifo函数来创建一个FIFO文件
      • 我们可以使用任何的低级I/O函数(open, write, read, close等)以及C库I/O函数(fopen, fprintf, fscanf, fclose等)来操作FIFO文件。
    • Linux的管道和Windows下的命名管道(Named Pipes)的区别
      • Windows的命名管道更像一个套接字(sockets),它可以通过网络让不同主机上的程序进行通信
      • Linux的管道允许有多个reader和writer,每个reader和writer进行读/写的最大容量为 PIPE_BUF(4KB),如果有多个writer同时写,他们写的东西会被分为一个一个的chunk(每个4KB)并允许交错写。(例如进程A有两个 chunk,A1,A2。进程B也有两个chunk,B1,B2。A和B同时写,则顺序可能为A1,B1,A2,B2) Windows的管道允许在同一个管道上有多个reader/writer对,他们之间读写的数据没有交叉。

    5.5 套接字(Sockets)

    • 套接字的特点:
      • 它是双向通信的
      • 它是进程间通信的,包括其他机器上的进程
    • 套接字有三个参数:
      • 通讯类型(communication style)
        • 连接(connection)类型:保证所有的包按发送的顺序到达接受方。(类似于电话)如果包丢失或者抵达顺序错误,会自动重发。
        • datagram类型:所有包单独发送,可能会出现丢失或者晚发早到的现象。(类似于邮寄)
      • 命名空间(namespace):描述套接字的地址是如何表示的,例如本地就是文件名,internet上就是ip地址。
      • 协议(protocol):通讯协议,常用的有TCI/IP,AppleTalk等。
    • 相关的API(套接字也是通过file descriptor来表示的):
      • socket:创建一个socket
      • closes:销毁一个socket
      • connect:在两个socket之间创建一个连接。这个API通常由客户端调用。
      • bind:给服务器的一个套接字绑定一个地址,服务器端调用。
      • listen:让一个套接字开始侦听,准备接受请求,服务器端调用。
      • accept:接受一个连接请求,并且为该连接创建一个新的套接字,服务器端调用。
    • 服务器端的生命流程:
      • 创建一个connection类型的socket
      • 给该socket绑定一个地址
      • 调用listen来enable该socket(listen可以指定最多有多少个请求在等待队列中,如果等待队列满了,又有新的请求到达的时候,则该请求被拒绝)
      • 对于收到的连接请求调用accept来接受
      • 关闭socket
    • 本地socket(local socket)
      • 如果是同一台电脑上的两个进程需要通信的话,可以使用本地socket。这种情况下socket的地址是文件路径。注意进程必须对该路径拥有可写权限,否则无法建立连接
      • 完成之后使用unlink来关闭一个socket
  • 相关阅读:
    ok~加油!
    解析window.open链接的参数
    Arrya数组添加过滤条件
    Oracle 查询今天、昨日、本周、本月和本季度的所有记录
    Sql Server日期查询-SQL查询今天、昨天、7天内、30天
    Lua 中 pairs 和 ipairs 的区别
    关于SignalR连接数量问题的记录
    IceStorm示例运行步骤
    从 OPC 到 OPC UA
    SQL Server 2008 R2 Express Profiler
  • 原文地址:https://www.cnblogs.com/scue/p/4075088.html
Copyright © 2011-2022 走看看