fcntl,非阻塞模式,CLOEXEC和管道pipe,execlp-del

文章目录


前言

介绍fcntl函数的两种基本用法,包括File descriptor flags和File status flags

管道使用的注意事项


1. fcntl

cpp 复制代码
#include <unistd.h>
#include <fcntl.h>

int fcntl(int fd, int cmd, ... /* arg */ );

fcntl() 函数可对打开的文件描述符 fd 执行多种操作。具体操作有 cmd 参数指定。

该函数有可选的第三个参数,根据cmd决定是否有第三个参数。

不同的Linux内核版本只支持某些操作。建议通过下面的方法检查宿主机内核是否支持特定操作可通过如下方法: 调用fcntl()函数,传入对应的cmd参数,测试调用是否失败并EINVAL。

带(int)代表需要第三个参数,(void)代表不需要参数

复制文件描述符

  • FD_DUPFD (int)
    复制文件描述符 fd , 成功,返回新的文件描述符,ret >= arg,ret是大于等与arg的最小值
    和dup2有些相似,但又不一样
  • F_DUPFD_CLOEXEC (int)
    同上,但复制出的文件描述符增加close-on-exec标志

文件描述符标志

以下命令操纵与文件描述符相关的标志。当前只有FD_CLOEXEC标志

  • F_GETFD(void)
    会忽略arg(如果有)
  • F_SETFD (int)
    设置已经打开的文件描述符为close-on-exec

文件状态标志

每个打开的文件描述都有与之相关的状态标志,该标志由open初始化以及或可由fcntl修改。

  • F_GETFL (void)
    返回文件访问权限和文件状态标志,忽略arg
  • F_SETFL (int)
    设置文件状态标识,该命令仅支持O_APPEND, O_ASYNC, O_DIRECT, O_NOATIME, and O_NONBLOCK flags

2. 管道

cpp 复制代码
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <assert.h>


#include <stdio.h>
#include <string>
#include <exception>

class StringE: public std::exception
{
private:
    std::string msg;
public:
    StringE(const std::string& str):msg(str){}
    const char* what() const throw()
    {
        return msg.c_str();
    }
};
void throwStringException(const std::string str)
{
    fprintf(stderr, "\033[31m%s\033[0m\n", str.c_str());
    throw StringE(str);
    // try
    // {
    //     throw StringE(str);
    // }
    // catch(const std::exception& e)
    // {
    //     fprintf(stderr, "\033[31m%s\033[0m\n", e.what());
    //     throw StringE(str);
    // }
}
#ifndef NDEBUG
#define ERROR(msg,...) fprintf(stderr, "\033[33m" msg "\033[0m\n", ##__VA_ARGS__)
#else
#define ERROR(msg,...)
#endif
#define STRINGIFY(x) #x
#define TOSTRING(x) STRINGIFY(x)
#define THROWSTR(str)  throwStringException(str " " __FILE__ ":" TOSTRING(__LINE__) )

class Pipe
{
public:
    enum {pread = 0, pwrite = 1, father_read, child_write, father_write, child_read};

private:
    int p1[2]; // 0 read 1 write father read     child write
    int p2[2];                   //father write  child read
public:
    Pipe()
    {
        p1[pread] = p1[pwrite] = p2[pread] = p2[pwrite] = -1;
        bool rc = (pipe(p1) == 0) && (pipe(p2) == 0);
        if (false == rc)
        {
            perror("pipe");
            this->~Pipe();
            THROWSTR("pipe");
        }
    }
    ~Pipe()
    {
        if (p1[pread] != -1)  { ::close(p1[pread]); p1[pread] = -1; }
        if (p1[pwrite] != -1) { ::close(p1[pwrite]);p1[pwrite] = -1;}
        if (p2[pread] != -1)  { ::close(p2[pread]); p2[pread] = -1; }
        if (p2[pwrite] != -1) { ::close(p2[pwrite]);p2[pwrite] = -1;}
        
    }
    int getfd(int type)
    {
        switch (type)
        {
        case father_read:
            return p1[pread];
            break;
        case child_write:
            return p1[pwrite];
            break;
        case father_write:
            return p2[pwrite];
            break;
        case child_read:
            return p2[pread];
            break;
        
        default:
            break;
        }
        return -1;
    }
    void pclose(int type)
    {
        switch (type)
        {
        case father_read:
            ::close(p1[pread] ); p1[pread] = -1;
            break;
        case child_write:
            ::close(p1[pwrite]); p1[pwrite] = -1;
            break;
        case father_write:
            ::close(p2[pwrite]); p2[pwrite] = -1;
            break;
        case child_read:
            ::close(p2[pread] );  p2[pread] = -1;
            break;
        
        default:
            assert(0);
            break;
        }
    }
};
  1. 管道属于操作系统对象,不受进程控制

  2. 当没有引用指向管道时,系统会回收该对象

    匿名管道:
    阻塞模式下:
    在读写端都不关闭时,阻塞式读写;如果管道内可暂存下写的数据,则write可返回,否则阻塞,若write阻塞中时读端关闭则write立即收到SIGPIPE信号
    管道写端write完后关闭,read读端会立即返回0,表示EOF
    write管道时如果读端已经关闭了,write立即收到SIGPIPE信号.
    非阻塞模式:
    读写端都打开,write发数据,read读数据;当管道满时,write返回-1,errno==EAGAIN代表Resource temporarily unavailable;
    当管道空时,read返回-1,errno==EAGAIN代表Resource temporarily unavailable;
    当写端关闭时,read返回0表示EOF
    当读端关闭时,write收到SIGPIPE信号,返回-1,errno==EPIPE
    有名管道:
    阻塞式:
    先open的一方会阻塞在open处,只有当有名管道的两端都open了才会继续执行
    阻塞式写,直到所有数据都写完,返回写入数据的字节数,若write时对端关闭,则收到SIGPIPE信号
    阻塞读,返回读取到的字节数,若对段关闭则立即返回0表示EOF
    非阻塞式:
    读写都不会阻塞在open处;先打开读read立即返回0,表示EOF;先打开写端,以只写方式打开写端,open失败;
    只能先打开读端
    read返回即返回0代表EOF,
    <open写段>, read==0,errno==EAGAIN,
    <write一帧数据后read到数据>,
    然后read返回-1,errno==0
    写端关闭后,read==0
    read返回-1,errno==EAGAIN代表对端已经打开,需要再次尝试接收数据
    write成功立即返回写入的字节数,当写入速度较快管道的暂存区满时,返回-1,errno==EAGAIN
    write时对端关闭,收到SIGPIPE信号返回-1,errno==EPIPE

管道小结  速记

write的对端关闭,收到SIGPIPE信号返回-1,errno==EPIPE

read的对端关闭,立即返回0表似乎EOF

在write完少量数据返回后立即关闭写端,读端打开但不read,此时数据会暂时放在管道中

3. execlp

cpp 复制代码
int execlp(const char *file, const char *arg, ...
                       /* (char  *) NULL */);

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

int main() {
    // 调用 ls 命令并传递参数
    if (execlp("ls", "ls", "-l", "/etc/", (char *)NULL) == -1) {
        perror("execlp failed");
    }
    return 0;
}

在这个例子中,execlp 调用 ls -l /etc/ 命令。

第二个参数一般填入执行的命令名本身,参数为可变长度但必须以NULL结尾

无论arg有几个,main的argc始终>=1; argv[0]就是第二个参数

若第二个参数直接是NULL,则main的argc==1, argv[0]为空

cpp 复制代码
int main(int argc, char* argv[]) {
    printf("--:[%d] [%s]\n", argc, argv[0]);
    return 0;

    // 调用 ls 命令并传递参数
    if (execlp("./b.out", "dsf", "sjdf", NULL) == -1) {
        perror("execlp failed");
    }
    return 0;
}

总结

设置文件描述符为非阻塞和 close-on-exec

cpp 复制代码
int fd = 0;
int ret;
// File descriptor flags
ret = fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC); assert(ret == 0);
    
// File status flags
ret = fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK); assert(ret == 0);

// 在文件打开时
open("file", O_RDWR | O_CLOEXEC | O_NONBLOCK);
相关推荐
时光の尘2 小时前
C语言菜鸟入门·关键字·int的用法
c语言·开发语言·数据结构·c++·单片机·链表·c
C++忠实粉丝2 小时前
计算机网络socket编程(6)_TCP实网络编程现 Command_server
网络·c++·网络协议·tcp/ip·计算机网络·算法
禊月初三2 小时前
LeetCode 4.寻找两个中序数组的中位数
c++·算法·leetcode
程序员与背包客_CoderZ3 小时前
C++设计模式——Abstract Factory Pattern抽象工厂模式
c语言·开发语言·c++·设计模式·抽象工厂模式
fancc椰4 小时前
C++基础入门篇
开发语言·c++
晚安,cheems5 小时前
c++(入门)
开发语言·c++
人才程序员5 小时前
详解Qt QStorageInfo 存储信息类
c语言·开发语言·c++·后端·qt·界面
ZHOUPUYU5 小时前
最新‌VSCode保姆级安装教程(附安装包)
c语言·开发语言·c++·ide·windows·vscode·编辑器
kcwqxx6 小时前
day23|leetCode 39. 组合总和 , 40.组合总和II , 131.分割回文串
c++·算法·leetcode
机器视觉知识推荐、就业指导6 小时前
基于Qt/C++/Opencv实现的一个视频中二维码解析软件
c++·qt·opencv