《UNIX环境高级编程》笔记--管道

1.pipe

管道是UNIX系统IPC的最古老形式,并且所有的UNIX系统都提供此种通信机制。管道有下列两种局限性:

a.历史上,它们是半双工的,现在某些系统提供全双工管道,但是为了最佳的可移植性,我们决不应预先假定系统使用此

特性。

b.它们只能在具有公共祖先的进程之间使用。通常一个管道由一个进程创建,然后该进程调用fork,此后父子进程之间可以

使用该管道。

(FIFO没有第二种局限性,UNIX域套接字和命名流没有这两种局限性)

管道是由调用pipe函数而创建的:

#include<unistd.h>
int pipe(int filedes[2]);
//成功则返回0,出错则返回-1.
经由参数filedes返回两个文件描述符:filedes[0]为读而打开,filedes[1]为写而打开。filedes[1]的输出是filedes[0]的输入。


调用fork之后做什么取决于我们想要有的数据流的方向,对于从父进程到子进程的管道,父进程关闭管道的读端fd[0],子

进程则关闭写端fd[1]。

调用fork之后的半双工管道:


从父进程到子进程的管道:


为了构造从子进程到父进程的管道,父进程关闭fd[1],子进程关闭fd[0]。

当管道的一端被关闭后,下列两条规则其作用:

a.当读一个写端已经被关闭的管道时,在所有数据都被读取后,read返回0,指示达到了文件结束处。

b.如果写一个读端已经被关闭的管道,则产生信号SIGPIPE。如果忽略该信号或者捕获该信号并从其处理程序返回,则

write返回-1,errno设置为EPIPE。

在写管道是,常量PIPE_BUF规定了内核中管道缓冲区的大小,如果对管道调用write,而且要求写的字节数小于等于

PIPE_BUF,则此操作不会与其他进程对同一管道的write操作穿插进行。但是,若有多个进程同时写一个管道,而且

进程要求写的字节数超过PIPE_BUF字节数时,写操作的数据可能穿插。


实践:

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>

int main(void){
        int result,n;
        int fd[2];
        pid_t pid;
        char line[256];

        if(pipe(fd) < 0){
                perror("pipe");
                return -1;
        }

        if((pid = fork()) < 0){
                perror("fork");
                return -1;
        }else if(pid > 0){ //parent
                close(fd[0]);
                if(fd[1] != STDOUT_FILENO){
                        dup2(fd[1],STDOUT_FILENO);
                }
                execl("/bin/ls","ls",(char*)0);
        }else{ //child
                close(fd[1]);
                while((n =read(fd[0],line,256)) > 0){
                        if(write(STDOUT_FILENO,line,n) != n){
                                perror("write");
                                exit(-1);
                        }
                }
                close(fd[0]);
        }
        return 0;
}

运行结果:

WinVNC.log
a.out
a.txt
b.txt
chlock
dtest
lock
lock.txt

在父进程中将管道描述符赋值为标准输出,所以在执行ls后,原本是写到标准输出的,现在写到fd[1]中。而子进程将读到

的内容显示到标准输出上。

2.popen和pclose函数

常见的操作是创建一个管道连接到另一个进程,然后读取其输出或向其输入端发送数据,为此,标准IO提供了两个函数

popen和pclose。这两个函数实现的操作是:创建一个管道,调用fork产生一个子进程管道的不使用端,执行一个shell

以运行命令,然后等待命令终止。

#include<stdio.h>
FILE *popen(const char* cmdstring, const char *type);  //若成功则返回文件指针,出错则返回NULL。
int pclose(FILE *fp); //返回cmdstring的终止状态,若出错则返回-1。
函数popen先执行fork,然后调用exec以执行cmdstring,并返回一个标准IO文件指针,如果type是r,则文件指针连接到

cmdstring的标准输出,如果type是w,则文件指针连接到cmdstring的标准输入。

pclose函数关闭标准IO流,等待命令执行结束,然后返回shell的终止状态。如果shell不能被执行,则pclose返回的终止状

态与shell已执行exit(127)一样。


实践:

#include<stdio.h>

int main(void){
        char line[256];
        FILE* fpin;
        int n;

        if((fpin = popen("/bin/ls","r")) == NULL){
                perror("popen");
                return -1;
        }

        while(fgets(line, 256, fpin) != NULL){
                if(fputs(line,stdout) == EOF){
                        perror("fputs");
                        return -1;
                }
        }

        return 0;
}
运行结果:

[root@yanPC apue]# ./a.out
a.out
abort.c
core.16354
core.16355
fork.c
popen.c
sigsetjmp.c
sigsuspend.c
sleep.c
template.c
vfork.c
wait.c

3.命名管道

FIFO被称为命名管道。匿名管道只能由相关进程使用,这些相关进程的共同祖先进程创建了管道,但是通过FIFO,不相关的

进程也能交换数据。

FIFO是一种文件类型。创建FIFO类似于创建文件,确实,FIFO的路径名存在于文件系统中。

#include<sys/stat.h>
int mkfifo(const char *pathname, mode_t mode); //成功则返回0,出错则返回-1.
mkfifo函数中mode参数的规格说明与open函数中的mode相同,一旦已经用mkfifo创建一个FIFO,就可用open打开它,其实,

一般的文件IO函数都可用于FIFO(close,read,write,unlink)。

实践:

#include<stdio.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<stdlib.h>


#define MYFIFO "myfifo"


int main(void){
        char buffer[256];
        pid_t pid;
        int fd;


        unlink(MYFIFO);
        if(mkfifo(MYFIFO,0666) < 0){
                perror("mkfifo");
                return -1;
        }


        if((pid = fork())<0){
                perror("fork");
                return -1;
        }else if(pid > 0){
                char s[] = "hello world.";
                fd = open(MYFIFO,O_WRONLY);
                write(fd,s,sizeof(s));
                close(fd);
        }else{
                fd = open(MYFIFO,O_RDONLY);
                read(fd,buffer,256);
                printf("%s\n",buffer);
                close(fd);
                exit(0);
        }


        waitpid(pid,NULL,0);
        return 0;
}
运行结果:

[root@yanPC apue]# ./a.out
hello world.

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
UNIX环境高级编程笔记是关于在UNIX系统中进行高级编程的一些笔记和技巧的记录。这些笔记主要涉及文件I/O和进程管理等方面的内容。在UNIX系统中,文件I/O是通过文件描述符来进行操作的。文件描述符是一个整数,用来标识打开的文件。为了实现跨平台的兼容性,可以使用POSIX标准来进行文件操作。POSIX是一个操作系统接口的标准,它以UNIX为基础,但并不限于UNIX类系统。此外,Single UNIX Specification简称SUS,它是POSIX.1标准的一个超集,定义了UNIX系统的实现标准。在UNIX系统中,进程的初始化是由init进程来完成的。init进程会读取文件/etc/ttys,并根据其中定义的终设备进行处理。对于每个允许登录的终设备,init进程会调用fork函数生成一个子进程,并通过exec函数执行getty程序来处理该终设备。通过这些技巧和方法,可以实现在UNIX环境下进行高级编程的需求。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [UNIX环境高级编程笔记](https://blog.csdn.net/qq_55537010/article/details/127837953)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* *3* [《UNIX环境高级编程》学习笔记](https://blog.csdn.net/qq_42526420/article/details/123143423)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值