计算机网络------fcntl实现非阻塞IO,dup2实现重定向

本文详细介绍了如何在Linux系统中使用fcntl将文件描述符设置为非阻塞模式,以及如何利用dup2进行文件重定向。通过示例展示了在非阻塞IO下,当没有数据时进程不会被阻塞,同时讲解了dup和dup2的区别及其在文件重定向中的应用。

        在五种IO模型中有提到在Linux中,默认所有的套接字都是阻塞的。所以要实现非阻塞的套接字,就要执行特定的系统调用来实现。

fcntl

        函数原型:

#include <unistd.h>
#include <fcntl.h>
int fcntl(int fd, int cmd, ... /* arg */ );

        参数说明:

fd:表示对哪个文件描述符进行操作

cmd:表示对文件描述符进行什么操作

arg:根据操作的不同而又不同的取值

        函数功能:该函数的功能有很多,对应的cmd取值也有很多,但这里实现的只是将文件描述符设置为非阻塞。所以只介绍该部分的功能。

cmd = F_GETFL:获取文件状态标记,此时状态标记由返回值带回,参数arg为空。返回值为负数,说明获取失败。

cmd = F_SETFL:设置文件状态标记,此时新设置的文件状态标记由参数arg标识,返回值表示设置成功与否。

        上述的文件状态其实是一个位图。不同的比特位表示不同的状态。不同的状态信息由不同的宏来标识。不同的宏之代表的数字之间只有一个比特位不同。O_NONBLOCK表示的就是文件的非阻塞状态。

        下面通过将标准输入设置为非阻塞来使用该函数:

        如果标准输入为阻塞状态,在对标准输入进行读取数据时,如果没有数据,此时进程就会被挂起等待,阻塞在相应的系统调用处,且该系统调用不返回。

        如果标准输入为非阻塞状态,在对其进行读取数据时,如果没有数据,此时进程不会被挂起,而是系统调用返回后,进程可以做其他的事情。

        首先编写将文件描述符设置为非阻塞的函数:

void SetNoBlock(int fd) 
{
    //首先利用fcntl函数获取文件描述符的文件标记状态
    int flag = fcntl(fd,F_GETFL);
    if(flag < 0)
    {
        perror("fcntl");
        return;
    }

    //设置文件描述符的标记状态,在原有的基础上再加一个非阻塞的标记状态
    fcntl(fd,F_SETFL,flag | O_NONBLOCK);
    return;                                                                                                                           
}

        通过将标准输入设置为阻塞和非阻塞来进行演示(标准输入默认是阻塞的):

int main()
{
    
在Linux系统下,基于IO实现进程间相互通信的方法主要有管道和命名管道。 ### 管道(匿名管道管道是一种最基本的进程间通信机制,本质是让不同的进程看到同一份文件缓冲区资源。它只能用于具有亲缘关系的进程之间的通信,如父子进程。其实现步骤如下: - 利用`pipe`函数创建管道- 利用`fork`函数创建子进程,此时父子进程会共享管道- 在父进程中将标准输出重定向管道写端,在子进程中将标准输入重定向管道读端,这可以通过`dup2`函数实现- 父子进程调用系统命令时可使用`execlp`等函数。 - 父进程使用`wait`函数回收子进程资源。 示例代码如下: ```c #include <stdio.h> #include <unistd.h> #include <stdlib.h> int main() { int fd[2]; pid_t pid; // 创建管道 if (pipe(fd) == -1) { perror("pipe"); return 1; } // 创建子进程 pid = fork(); if (pid == -1) { perror("fork"); return 1; } if (pid == 0) { // 子进程:将标准输入重定向管道读端 close(fd[1]); dup2(fd[0], STDIN_FILENO); close(fd[0]); // 子进程执行命令 execlp("wc", "wc", "-l", NULL); perror("execlp"); exit(1); } else { // 父进程:将标准输出重定向管道写端 close(fd[0]); dup2(fd[1], STDOUT_FILENO); close(fd[1]); // 父进程执行命令 execlp("ls", "ls", NULL); perror("execlp"); exit(1); } return 0; } ``` ### 命名管道(FIFO) 命名管道是一种特殊类型的文件,它可以实现不具有亲缘关系的进程之间的通信。通过`mkfifo`函数创建命名管道文件,不同的进程可以通过读写这个文件来进行通信。 示例代码如下: ```c // 写进程代码 #include <stdio.h> #include <stdlib.h> #include <fcntl.h> #include <unistd.h> #include <string.h> #define FIFO_NAME "myfifo" int main() { int fd; char message[] = "Hello, named pipe!"; // 创建命名管道 if (mkfifo(FIFO_NAME, 0666) == -1) { perror("mkfifo"); return 1; } // 打开命名管道以写模式 fd = open(FIFO_NAME, O_WRONLY); if (fd == -1) { perror("open"); return 1; } // 写入数据 write(fd, message, strlen(message)); // 关闭命名管道 close(fd); return 0; } // 读进程代码 #include <stdio.h> #include <stdlib.h> #include <fcntl.h> #include <unistd.h> #include <string.h> #define FIFO_NAME "myfifo" #define BUFFER_SIZE 100 int main() { int fd; char buffer[BUFFER_SIZE]; // 打开命名管道以读模式 fd = open(FIFO_NAME, O_RDONLY); if (fd == -1) { perror("open"); return 1; } // 读取数据 ssize_t bytes_read = read(fd, buffer, BUFFER_SIZE); if (bytes_read > 0) { buffer[bytes_read] = '\0'; printf("Read from named pipe: %s\n", buffer); } // 关闭命名管道 close(fd); // 删除命名管道文件 unlink(FIFO_NAME); return 0; } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值