美文网首页
五、进程间通信

五、进程间通信

作者: zhile_doing | 来源:发表于2018-08-30 14:24 被阅读0次
  1. 信号

    • 信号是linux操作系统进程间通信的一种方式,一个应用进程可以接受、发送信号给另一个进程,当进程捕获到某个信号时,可以执行某些特定的动作。
    • 与信号操作有关的函数位于signal.h头文件中,一个重要的函数声明如下



      该函数设置sig表示的信号由func表示的函数来处理,返回一个与func同类型的函数,或以下两个信号之一


    • 一个简单的例子
      #include<stdio.h>
      #include<signal.h>
      #include<stdlib.h>
      #include<unistd.h>
      void ouch(int sig){
          printf("Oh, I got signal %d\n", sig);
          (void) signal(SIGINT, SIG_DFL);
      }
      int main(){
          (void) signal(SIGINT, ouch);
          while(1){
              printf("hello world!\n");
              sleep(1);
          }
      }
      
    • 可以使用kill函数向指定进程发送信号



      看一个简单的例子

      #include<stdio.h>
      #include<stdlib.h>
      #include<signal.h>
      #include<sys/types.h>
      #include<unistd.h>
      static int alarm_handled = 0;
      void ding(int sig){
          alarm_handled = 1;
      }
      int main(){
          printf("process %d starting...\n", getpid());
          (void) signal(SIGALRM, ding);
          switch(fork()){
              case -1:
                  perror("create process failed!");
                  break;
              case 0:
                  sleep(2);
                  kill(getppid(), SIGALRM);
                  exit(0);
                  break;
          }
          printf("waiting for SIGALRM...\n");
          pause();
          if(alarm_handled){
              printf("catched the alarm sig!\n");
          }
          return 0;
      }
      
    • 使用更加健壮的signal接口,sigaction




      使用sigaction改写第一个例子

      #include<stdio.h>
      #include<stdlib.h>
      #include<sys/types.h>
      #include<unistd.h>
      #include<signal.h>
      void ouch(int sig){
          printf("Oh, I got sig %d\n", sig);
          (void)signal(SIGINT, SIG_DFL);
      }
      int main(){
          struct sigaction act;
          act.sa_handler = ouch;
          act.sa_flags = 0;
          sigemptyset(&act.sa_mask);
          sigaction(SIGINT, &act, 0);
          while(1){
              printf("Hello world!\n");
              sleep(1);
          }
          return 0;
      }
      
    • 还需要了解一些操作sigset_t的一些函数






  2. 管道-pipe
    管道是应用进程之间的单向数据通道,进程可以通过管道发送数据给另一个进程,通过popen系统调用,可以打开一个连接某个程序的管道,可以是r,也可以是w



    一个读取信息的例子

    #include<stdio.h>
    #include<stdlib.h>
    int main(){
        char info[50];
        FILE* fp = popen("uname -a", "r");
        if(fp == NULL){
            perror("create pipe to uname failed!");
            exit(0);
        }
        int num = fread(info, sizeof(char), 50, fp);
        printf("got information: - \n%s\n", info);
        return 0;
    }
    

    一个写数据的例子

    #include<stdio.h>
    #include<stdlib.h>
    #include<string.h>
    int main(){
        FILE* fp_write = popen("od -c", "w");
        char info[50];
        sprintf(info, "once upon a time, there was ...");
        if(fp_write != NULL){
            fwrite(info, sizeof(char), strlen(info), fp_write);
            pclose(fp_write);
        }
        return 0;
    }
    

    一个读取更多数据的例子

    #include<stdio.h>
    #include<stdlib.h>
    int main(){
        FILE* fp_read = popen("ps -ax", "r");
        if(fp_read == NULL){
            perror("create pipe to ps failed");
            exit(EXIT_FAILURE);
        }
        char info[1024];
        int num_char = fread(info, sizeof(char), 1024, fp_read);
        while(num_char>0){
            info[num_char - 1] = '\0';
            printf("got info: - \n%s\n", info);
            num_char = fread(info, sizeof(char), 1024, fp_read);
        }
        pclose(fp_read);
        exit(EXIT_SUCCESS);
    }
    

    一个重要的系统调用是pipe,如同shell中的管道操作符



    初始化两个文件描述符组成的数组,一个用于读,一个用于写,一个简单的例子如下

    #include<stdio.h>
    #include<stdlib.h>
    #include<unistd.h>
    #include<string.h>
    #include<sys/types.h>
    int main(){
        int fd[2];
        char buf[200];
        int chpid;
        int len;
        //使用pipe系统调用创建一个管道,fd[0]用于读取,fd[1]用于写入
        if(pipe(fd) == -1){
            perror("创建管道出错:");
            exit(1);
        }
        if((chpid = fork()) == 0){
            close(fd[1]);
            //从fd[0]中读取数据到buf缓冲区
            len = read(fd[0], buf, sizeof(buf));
            buf[len] = 0;
            printf("子进程从管道读取数据:%s\n", buf);
            exit(0);
        }else{
            close(fd[0]);
            //sprintf函数将格式化数据写入缓冲区中
            sprintf(buf, "父进程为子进程(pid=%d)创建的数据!\n", chpid);
            //使用write系统调用将buf中的数据写入fd[1]指向的管道中
            write(fd[1], buf, strlen(buf));
            exit(0);
        }
        return 0;
    }
    

    如上,pipe创建的管道操作符仅仅用于相互有联系的一组进程,比如,父子进程。当我们在没有特殊联系的进程中想要相互联系,可以通过命名管道fifo,fifo是linux操作系统中一类特殊的文件,用于进程间通信



    mode_t可以取



    看一个生产者与消费者的例子
    fifo_producer.c
    #include<stdio.h>
    #include<unistd.h>
    #include<stdlib.h>
    #include<sys/types.h>
    #include<sys/stat.h>
    #include<string.h>
    #include<fcntl.h>
    #include<limits.h>
    #define BUF_SIZE 4096
    #define TEN_SEG (1024 * 10)
    
    #define FIFO_NAME "/tmp/my_fifo"
    int main(){
        int open_mode = O_WRONLY;
        int fifo_fd;
        int res;
        char buf[BUF_SIZE + 1];
        if(access(FIFO_NAME, F_OK) == -1){
            res = mkfifo(FIFO_NAME, 0777);
            if(res == -1){
                fprintf(stderr, "can't create fifo file %s\n", FIFO_NAME);
                exit(EXIT_FAILURE);
            }
        }
        printf("process %d open fifo O_WRONLY!\n", getpid());
        fifo_fd = open(FIFO_NAME, open_mode);
        printf("process %d result %d\n", getpid(), fifo_fd);
        int bytes_sent = 0;
        if(fifo_fd != -1){
            do{
                res = write(fifo_fd, buf, BUF_SIZE);
                if(res == -1){
                    perror("error write data to fifo file");
                    exit(EXIT_FAILURE);
                }
                bytes_sent += res;
            }while(bytes_sent < TEN_SEG);
            (void)close(fifo_fd);
        }else{
            exit(EXIT_FAILURE);
        }
        printf("process %d finished\n%d bytes sent!", getpid(), bytes_sent);
        exit(EXIT_SUCCESS);
    }
    

    fifo_consumer.c

    #include<stdio.h>
    #include<unistd.h>
    #include<stdlib.h>
    #include<string.h>
    #include<sys/types.h>
    #include<sys/stat.h>
    #include<limits.h>
    #include<fcntl.h>
    #define BUF_SIZE 4096
    #define FIFO_NAME "/tmp/my_fifo"
    int main(){
        int res;
        int pipe_fd;
        int mode = O_RDONLY;
        if(access(FIFO_NAME, F_OK) == -1){
            perror("fifo file not exists! exiting...");
            exit(EXIT_FAILURE);
        }
        char recv[BUF_SIZE + 1];
        int bytes_recv;
        printf("process %d open fifo file with mode O_RDONLY!\n", getpid());
        pipe_fd = open(FIFO_NAME, mode);
        printf("open fifo file with result %d\n", pipe_fd);
        if(pipe_fd != -1){
            do{
                res = read(pipe_fd, recv, BUF_SIZE);
                bytes_recv += res;
            }while(res > 0);
            printf("%d bytes received!\n", bytes_recv);
            close(pipe_fd);
        }else{
            exit(EXIT_FAILURE);
        }
        exit(EXIT_SUCCESS);
    }
    

    result



    命名管道可用于多个进程间相互通信,客户机服务器模式程序示例如下
    client.h

    #include<stdio.h>
    #include<stdlib.h>
    #include<unistd.h>
    #include<string.h>
    #include<limits.h>
    #include<fcntl.h>
    #include<sys/types.h>
    #include<sys/stat.h>
    
    #define SERVER_FIFO_NAME "/tmp/server_fifo"
    #define CLIENT_FIFO_NAME "/tmp/cli_%d_fifo"
    
    #define BUF_SIZE 50
    struct data_to_pass_st{
        pid_t pid;
        char some_data[BUF_SIZE - 1];
    };
    

    server.c

    #include"client.h"
    #include<ctype.h>
    int main(){
        int serv_fifo_fd, cli_fifo_fd;
        struct data_to_pass_st data;
        char buf[BUF_SIZE];
        char cli_fifo_name[50];
        int mode_serv;
        int mode_cli;
        serv_fifo_fd = mkfifo(SERVER_FIFO_NAME, 0777);
        if(serv_fifo_fd == -1){
            perror("create server fifo failed!");
            exit(EXIT_FAILURE);
        }
        puts("create server fifo");
    
        mode_serv = O_RDONLY;
        serv_fifo_fd = open(SERVER_FIFO_NAME, mode_serv);
        if(serv_fifo_fd == -1){
            perror("open server fifo failed!");
            exit(EXIT_FAILURE);
        }
        puts("open server fifo");
    
        sleep(1);
    
        printf("read to receive data!\n");
        int read_bytes;
        char* tmp_ptr;
        do{
            read_bytes = read(serv_fifo_fd, &data, sizeof(data));
            if(read_bytes > 0){
                tmp_ptr = data.some_data;
                while(*tmp_ptr){
                    *tmp_ptr = toupper(*tmp_ptr);
                    tmp_ptr++;
                }
                sprintf(cli_fifo_name, CLIENT_FIFO_NAME, data.pid);
                cli_fifo_fd = open(cli_fifo_name, O_WRONLY);
                if(cli_fifo_fd != -1){
                    write(cli_fifo_fd, &data, sizeof(data));
                    close(cli_fifo_fd);
                }
            }
        }while(read_bytes > 0);
        close(serv_fifo_fd);
        unlink(SERVER_FIFO_NAME);
        exit(EXIT_SUCCESS);
    }
    

    client.c

    #include"client.h"
    #include<ctype.h>
    int main(){
        struct data_to_pass_st data;
        char buf[BUF_SIZE];
        char cli_fifo_name[50];
        int serv_fifo_fd, cli_fifo_fd;
    
    
        serv_fifo_fd = open(SERVER_FIFO_NAME, O_WRONLY);
        puts("client : open server fifo");
        if(serv_fifo_fd == -1){
            perror("open server fifo failed");
            exit(EXIT_FAILURE);
        }
    
        data.pid = getpid();
        sprintf(cli_fifo_name, CLIENT_FIFO_NAME, data.pid);
        mkfifo(cli_fifo_name, 0777);
        puts("client : create client fifo");
    
        int read_bytes;
    
        sprintf(data.some_data, "data from %d", data.pid);
        printf("%d sent: %s, ", data.pid, data.some_data);
        write(serv_fifo_fd, &data, sizeof(data));
    
        cli_fifo_fd = open(cli_fifo_name, O_RDONLY);
        if(cli_fifo_fd == -1){
            perror("open client fifo failed");
            exit(EXIT_FAILURE);
        }
        if(cli_fifo_fd != -1){
            read_bytes = read(cli_fifo_fd, &data, sizeof(data));
            printf("received:%s\n", data.some_data);
        }else{
            perror("open client fifo to read failed");
            exit(EXIT_FAILURE);
        }
    
        close(cli_fifo_fd);
        close(serv_fifo_fd);
        exit(EXIT_SUCCESS);
    }
    

    result


  3. 信号量,有时使用信号量完成进程或线程同步
    了解四个信号量操作函数
    sem_init\sem_post\sem_wait\sem_destroy
    example

    #include<stdio.h>
    #include<stdlib.h>
    #include<semaphore.h>
    #include<string.h>
    #include<pthread.h>
    char work_area[1024];
    sem_t semaphore;
    void* thread_func(void* arg){
        sem_wait(&semaphore);
        while(strncmp(work_area, "end", 3) != 0){
            printf("len:%d\n", strlen(work_area) - 1);
            sem_wait(&semaphore);
        }
        pthread_exit(EXIT_SUCCESS);
    }
    int main(){
        int res;
        pthread_t thread;
        void* thread_res;
        res = sem_init(&semaphore, 0, 0);
        if(res != 0){
            perror("初始化信号量失败!");
            exit(EXIT_FAILURE);
        }
        if((res = pthread_create(&thread, NULL, thread_func, NULL)) != 0){
            perror("线程创建失败");
            exit(EXIT_FAILURE);
        }
        printf("please input a string:\n");
        while(strncmp(work_area, "end", 3) != 0){
            fgets(work_area, 1024, stdin);
            sem_post(&semaphore);
        }
        printf("wait for the thread exit!\n");
        if((res = pthread_join(thread, &thread_res)) != 0){
            perror("等待线程结束出错!");
            exit(EXIT_FAILURE);
        }
        printf("线程已退出!\n");
        sem_destroy(&semaphore);
        exit(EXIT_SUCCESS);
    }
    
  4. 共享内存
    顾名思义,就是一块两者都可以访问的内存,进程通过原子性的写入操作保证数据完整性。
    要点:理解创建与操作共享内存的系统调用,使用manual



    看一个服务进程与客户进程的例子
    server.c

        #include"common.h"
    int main(){
        int running = 1;
        void *share_mem;
        struct share_mem_st *data;
        int shmid;
        char buf[50];
    
        shmid = shmget((key_t)1234, sizeof(struct share_mem_st), 0666 | IPC_CREAT);
        if(shmid == -1){
            perror("create share_memory failed");
            exit(EXIT_FAILURE);
        }
    
        share_mem = shmat(shmid, (void*)0, 0);
        if(share_mem == (void*)-1){
            perror("attach the mem failed");
            exit(EXIT_FAILURE);
        }
        printf("share memory attached at %X\n", share_mem);
        
        data = (struct share_mem_st*)share_mem;
        data->controller = 0;
        while(running){
            while(data->controller == 1){
                printf("wait for client...\n");
                sleep(1);
            }
            puts("type the message:");
            //fgets(buf, 50, stdin);
            memset(buf, '\0', 50);
            scanf("%s", buf);
            memset(data->message, '\0', TEXT_SIZE);
            strncpy(data->message, buf, strlen(buf));
            data->controller = 1;
            if(strncmp(buf, "end", 3) == 0){
                running = 0;
            }
            
        }
    
        printf("exiting...\n");
        if(shmdt(share_mem) == -1){
            perror("error between unttach the share_meme");
            exit(EXIT_FAILURE);
        }
        exit(EXIT_SUCCESS);
    }
    

    client.c

    #include"common.h"
    int main(){
        int shmid;
        struct share_mem_st* data;
        void* share_mem;
        int running = 1;
        char buf[50];
    
        if((shmid = shmget((key_t)1234, sizeof(struct share_mem_st), 0666 | IPC_CREAT)) == -1){
            perror("create share mem failed");
            exit(EXIT_FAILURE);
        }
    
        if((share_mem = shmat(shmid, (void*)0, 0)) == (void*)-1){
            perror("attach the share mem failed");
            exit(EXIT_FAILURE);
        }
        printf("share mem attach at %X\n", share_mem);
        data = (struct share_mem_st*)share_mem;
        data->controller = 1;
        while(running){
            if(data->controller == 1){
                strncpy(buf, data->message, strlen(data->message));
                printf("message:%s\n", buf);
                sleep(2);
                data->controller = 0;
                if(strncmp(buf, "end", 3) == 0){
                    running = 0;
                }
            }else{
                printf("waiting for server...\n");
                sleep(1);
            }
        }
        if(shmdt(share_mem) == -1){
            perror("unattach the share mem failed");
            exit(EXIT_FAILURE);
        }
        if(shmctl(shmid, IPC_RMID, 0) == -1){
            perror("error with release the share mem");
            exit(EXIT_FAILURE);
        }
        exit(EXIT_SUCCESS);
    
    }
    
  5. 消息队列
    队列是一种先进先出的数据结构,用于进程间通信时较为灵活,可以设计为分布式,十分适合与网络应用进程通信,爬虫程序就是一个很好的例子
    首先需要理解创建和使用消息队列的一些系统调用-manual



    一个使用消息队列相互通信的例子
    common.h

    #include<stdio.h>
    #include<unistd.h>
    #include<stdlib.h>
    #include<string.h>
    #include<errno.h>
    
    #include<sys/types.h>
    #include<sys/ipc.h>
    #include<sys/msg.h>
    
    #define BUF_SIZE 1024
    struct msg_st{
        long int msg_type;
        char data[BUF_SIZE];
    };
    

    server.c

    #include"common.h"
    int main(){
        int running = 1;
        struct msg_st msg_data;
        int msg_id;
        long int msg_type_receive = 1;
    
        if((msg_id = msgget((key_t)1234, 0666| IPC_CREAT)) == -1){
            perror("server:can't create message queue");
            exit(EXIT_FAILURE);
        }
        printf("get message queue with id:%d\n", msg_id);
    
        while(running){
            if(msgrcv(msg_id, (void*)&msg_data, BUF_SIZE, msg_type_receive, 0) == -1){
                perror("recv data from queue failed");
                exit(EXIT_FAILURE);
            }
            printf("you wrote:%s\n", msg_data.data);
            if(strncmp(msg_data.data, "end", 3) == 0){
                printf("exiting...\n");
                running = 0;
    
            }
        }
        msgctl(msg_id, IPC_RMID, 0);
        exit(EXIT_SUCCESS);
    }
    

    client.c

    #include"common.h"
    int main(){
        int running = 1;
        struct msg_st msg_data;
        long int msg_type_receive = 0;
        char buf[BUF_SIZE];
        int msg_id;
    
        if((msg_id = msgget((key_t)1234, 0666 | IPC_CREAT)) == -1){
            perror("client:get message queue failed");
            exit(EXIT_FAILURE);
        }
        printf("get message queue with id:%d\n", msg_id);
        int length = 0;
        while(running){
            memset(msg_data.data, '\0', BUF_SIZE);
            printf("input some text:");
            fgets(buf, BUFSIZ, stdin);
            length = strlen(buf);
            msg_data.msg_type = 1;
            strncpy(msg_data.data, buf, length -1);
            msg_data.data[length] = '\0';
            if(msgsnd(msg_id, (void*)&msg_data, BUF_SIZE, 0) == -1){
                perror("error with send data");
                exit(EXIT_FAILURE);
            }
            printf("you send:%s\n", msg_data.data);
            if(strncmp(buf, "end", 3) == 0){
                printf("exiting...\n");
                running = 0;
            }
        }
    
        exit(EXIT_SUCCESS);
    
    }
    

    client



    server


  6. 总结
    信号、管道、命名管道、共享内存、消息队列以及信号量是进程间通信的重要手段,我们需要了解他们分别适用于何种情形,优缺点,如何使用,如何处理错误,碰到不记得的函数用法,可以通过manual查看该函数声明,数据结构等等信息。

相关文章

  • linux进程间通信(1)

    一、进程通信概述 1、什么是进程间通信?什么是线程间通信? 进程间通信: 进程间通信就指的是用户空间中进程A与进程...

  • 五、进程间通信

    信号信号是linux操作系统进程间通信的一种方式,一个应用进程可以接受、发送信号给另一个进程,当进程捕获到某个信号...

  • Android 系统复习面试系列(五)进程间通信

    Android 系统复习面试系列(五)进程间通信 本篇主要总结 Binder 原理,顺带提下其他进程间通信方式 A...

  • 第二十三章 进程间通信介绍(一)

    本章目标: 进程同步与进程互斥 进程间通信目的 进程间通信发展 进程间通信分类 进程间共享信息的三种方式 IPC对...

  • 进程间的通信

    进程间的通信主要分为本机器进程间的通信和不同机器间进程的通信。本文主要描述本机进程间的通信。 一、传统Linux的...

  • 进程间通信

    进程间通信 进程空间相对独立,资源无法相互获取,此时在不同进程间通信需要专门方法 进程间通信就是在不同的进程间进行...

  • 进程间通信,线程间通信

    进程间通信 进程间通信又称IPC(Inter-Process Communication),指多个进程之间相互通信...

  • Android IPC机制

    IPC 即Inter-Process-Communication,含义是进程间通信/跨进程通信。是指多个进程间通信...

  • 进程管理(五)进程间通信、死锁

    (一)进程间通信 除了同步和互斥外,进程间还有其他的通信手段。 进程间的通信 --> IPC (InterProc...

  • 6. 进程间通信

    参考链接:1. 进程间通信及使用场景2. 进程间通信机制IPC3. 看图理解进程间通信IPC==重点4. 进程间通...

网友评论

      本文标题:五、进程间通信

      本文链接:https://www.haomeiwen.com/subject/cikvbftx.html