• 【Linux 应用编程】进程管理


    IPC(InterProcess Communication,进程间通信)是进程中的重要概念。Linux 进程之间常用的通信方式有:

    • 文件:简单,低效,需要代码控制同步
    • 管道:使用简单,默认阻塞
      • 匿名管道 pipe:只存在于内核缓冲区,只能用于有血缘关系的进程
      • 有名管道 FIFO:在文件系统中存在,可用于无血缘关系的进程
    • 信号量:使用复杂,但开销小,操作系统本身支持信号量
    • 内存映射区 mmap:进程有无血缘关系都可以
    • 本地套接字 socket:稳定可靠

    管道概念

    通过管道,可以把一个进程的输出作为另一个进程的输入。管道分为两种:

    • 匿名管道:主要用于两个进程间有父子关系的进程间通信
    • 命名管道:主要用于没有父子关系的进程间通信

    通常用的管道,都是匿名管道。管道实际上是一块内核缓冲区,不占用磁盘空间,但操作方式跟文件是一样的。

    在 Linux 的命令行终端中,管道是再常用不过的命令技巧了。通过管道可以把前一个命令的输出作为后一个命令的输入。在 Linux 命令中通常通过符号 | 来使用管道。

    例如,查看所有的进程,然后按关键字过滤:

    ps aux | grep mysql
    

    匿名管道

    匿名管道是不能在文件系统中以任何方式看到的半双工管道,不占磁盘空间。管道的特点有:

    • 一端只读或只写。读端、写端分别是一个文件描述符
    • 操作管道的进程退出后,管道自动释放
    • 管道默认是阻塞的
    • 半双工,数据在管道内只能单向传输

    匿名管道无法判断消息是否被读完,通常仅用于父进程向子进程传递数据,或子进程向父进程传递数据。此时可以在父子进程中关闭不用的读或写端。

    管道是基于环形队列这个数据结构实现的,数据先进先出。默认的缓冲区大小是 4 KB,大小会自动调整。

    • 单工:数据单向流动,例如遥控器
    • 双工:数据可以同时双向流动,例如手机
    • 半双工:数据可以双向流动,但不可同时双向流动,例如对讲机

    pipe 函数

    pipe() 函数用于创建匿名管道。.

    函数原型:

    #include <unistd.h>
    
    int pipe(int pipefd[2]);
    

    参数:返回文件描述符数组,对应管道的两端,往写端写的数据会被内核缓存起来,直到读端将数据读完。其中:

    • pipefd[0]:读端
    • pipefd[1]:写端

    返回值:成功返回 0,否则返回-1。

    示例:

    #include <unistd.h>
    #include <stdio.h>
    #include <stdlib.h>
    
    int main()
    {
        int fd[2];
        int ret, pid;
        char buf;
        ret = pipe(fd);
        if (ret == -1)
        {
            perror("pipe error");
            exit(1);
        }
    
        pid = fork();
        if (pid == 0)
        {
            close(fd[1]);
            while(read(fd[0], &buf, 1) > 0)
            {
    	        // 两种方式都可以实现输出
                printf("%c
    ", buf);
                write(STDOUT_FILENO, &buf, 1);
            }
            close(fd[0]);
        } 
        else if (pid > 0)
        {
            close(fd[0]);
            write(fd[1], "hello world", 12);
            close(fd[1]);
            wait(NULL);
        } 
        else
        {
            perror("fork error");
            exit(1);
        }
    
        close(fd[0]);
        close(fd[1]);
        return 0;
    }
    

    命名管道

    命名管道也叫 FIFO 文件,在文件系统中以文件名的形式存在,大小为0。匿名管道无法在无关进程之间通信,但 FIFO 可以借助文件系统中的文件,使得同一主机内的所有的进程都可以通信。

    进程通过 FIFO 通信时,首先要打开管道文件,然后使用 read 、write 函数通信。

    mkfifo 函数

    函数原型:

    #include <sys/types.h>
    #include <sys/stat.h>
    
    int mkfifo(const char *pathname, mode_t mode);
    

    参数:

    • pathname:指定文件名
    • mode:指定文件的读写权限

    返回值:
    函数成功返回 0,否则返回-1 并设置 errno,常见 errno 有:

    • EACCES:pathname 所在的目录不允许执行权限
    • EEXIST:pathname 已经存在
    • ENOENT:目录部分不存在
    • ENOTDIR:目录部分不一个目录
    • EROFS:路径指向一个只读的文件系统

    示例:
    创建两个进程,一个将文件内容读到 FIFO,另一个从 FIFO 读内容写到另一个文件。
    写 FIFO 的文件示例:

    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <fcntl.h>
    #include <sys/stat.h>
    #include <sys/types.h>
    #define BUFSIZE 1024
    
    int main(int argc, char *argv[]){
        int ret;
        int datafd, fifofd;
        int bytes;
        char buffer[BUFSIZE];
        const char *fifoname = "/tmp/fifo";
    
        if (argc != 2) {
            printf("please input filename
    ");
            exit(EXIT_FAILURE);
        }
    
        if (access(fifoname, F_OK) < 0) {
            ret = mkfifo(fifoname, 0777);
            if (ret < 0) {
                perror("mkfifo error");
                exit(EXIT_FAILURE);
            }
        }
        fifofd = open(fifoname, O_WRONLY);
        datafd = open(argv[1], O_RDONLY);
        if (fifofd < 0) {
            perror("open fifo error");
            exit(EXIT_FAILURE);
        }
        if (datafd < 0) {
            perror("open file error");
            exit(EXIT_FAILURE);
        }
    
        bytes = read(datafd, buffer, BUFSIZE);
        while (bytes > 0) {
            ret = write(fifofd, buffer, bytes);
            if (ret < 0) {
                perror("write fifo error");
                exit(EXIT_FAILURE);
            }
            bytes = read(datafd, buffer, BUFSIZE);
        }
        close(fifofd);
        close(datafd);
        return 0;
    }
    

    读 FIFO 的文件示例:

    #include <stdio.h>
    #include <unistd.h>
    #include <stdlib.h>
    #include <fcntl.h>
    #include <sys/stat.h>
    #include <sys/types.h>
    #define BUFSIZE 1024
    
    int main(int argc, char *argv[]) {
        char *fifoname = "/tmp/fifo";
        int fifofd, datafd;
        int bytes, ret;
        char buffer[BUFSIZE];
    
        if (argc != 2) {
            printf("please input a filename");
            exit(EXIT_FAILURE);
        }
    
        fifofd = open(fifoname, O_RDONLY);
        datafd = open(argv[1], O_WRONLY);
        if (fifofd < 0) {
            perror("open fifo error");
            exit(EXIT_FAILURE);
        }
        if (datafd < 0) {
            perror("open file error");
            exit(EXIT_FAILURE);
        }
    
        bytes = read(fifofd, buffer, BUFSIZE);
        while(bytes > 0) {
            ret = write(datafd, buffer, bytes);
            if (ret < 0) {
                perror("write file error");
                exit(EXIT_FAILURE);
            }
            bytes = read(fifofd, buffer, BUFSIZE);
        }
        close(datafd);
        close(fifofd);
        return 0;
    }
    
  • 相关阅读:
    NET中的类型和装箱/拆箱原理
    转 C# 装箱和拆箱[整理]
    理解线程同步
    IsBackground的理解
    赛马会面试题
    FTP上传类
    FTPS加密上传
    转载WPF SDK研究 之 AppModel
    SQL Server查看错误日志存档编号及其详情
    Hive基础编程入门(一)
  • 原文地址:https://www.cnblogs.com/kika/p/10851500.html
Copyright © 2020-2023  润新知