Linux系统调用十、进程地址空间、文件描述符、errno错误码、dup()重定向

简介: Linux系统调用十、进程地址空间、文件描述符、errno错误码、dup()重定向

🥇1. 进程虚拟地址空间与文件描述符

首先我们看一下进程虚拟空间和文件描述符的示意图。

下面我们写一个程序来测试一下,一次性最多能打开的文件数量,来验证文件描述符的作用和范围。

/************************************************************
  >File Name  : openfilemax.c
  >Author     : QQ
  >Company    : QQ
  >Create Time: 2022年05月14日 星期六 10时25分25秒
************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <fcntl.h>
int main(int argc, char* argv[])
{
  int id = 3;
  char filename[128] = {0};
  while(1)
  {
    sprintf(filename, "file_%04d", id);
    if(open(filename, O_CREAT | O_RDONLY, 0644) < 0)
    {
      perror("open err:");
      break;  
    }
    id++;
  }
  printf("open file maxnum: %d\n", id);
  return 0;
}

编译运行,可以看到运行结果为1024,实际文件名最小是0003最大是1023。这是为什么呢?我们通过上面的文件描述符示意图可以看到,文件描述符最大是1023,从0到1023也就是总共1024个文件描述符。也就是说我们最多可以一次性打开1024个文件,再多的话就没有文件描述符可用了。这样的话,我们打开的文件从0003到1023,再加上标准输入0、标准输出1、标准错误2这三个文件,总共就是1024个文件。因为在开启一个进程的时候默认会打开标准输入输出和标准错误这三个文件,所以我们实际打开的文件只有1023-3+1=1021个文件,那么总共打开的文件个数就是1024个。

🥇2. errno错误码与strerror()函数

🥈2.1 什么是errno

errno可以理解为一个全局变量,它存储了出错信息。在下面三个路径可以看到errno相关的内容

/usr/include/errno.h
/usr/include/asm-generic/errno-base.h
/usr/include/asm-generic/errno.h

我们可以在这些文件中自己定义一些errno,这样可以做到我们自己知道原始错误信息,而打印出来给用户看的是我们希望用户看到的对原始错误的解释。

🥈2.2 strerror()函数说明

  • 包含头文件
#include <string.h>
  • 函数原型
char *strerror(int errnum);
  • 函数功能
    可以打印errno对应的详细错误信息。The strerror() function returns a pointer to a string that describes the error code passed in the argument errnum, possibly using the LC_MESSAGES part of the current locale to select the appropriate language. This string must not be modified by the application, but may be modified by a subsequent call to perror(3) or strerror(). No library function will modify this string.
  • 函数参数
  • errnum:错误编号
  • 函数返回值
    The strerror() functions return the appropriate error description string, or an “Unknown error nnn” message if the error number is unknown. 返回错误信息。

🥇3. dup()和dup2()函数

  • 包含头文件
#include <unistd.h>
  • 函数原型
int dup(int oldfd);
int dup2(int oldfd, int newfd);
#define _GNU_SOURCE
#include <unistd.h>
int dup3(int oldfd, int newfd, int flags);
  • 函数功能这两个函数主要用于重定向,它们两个的功能和区别就是:
  • dup(oldfd):复制文件描述符,返回一个当前空闲的最小文件描述符,并且让这个文件描述符指向oldfd所指向的文件;dup() uses the lowest-numbered unused descriptor for the new descriptor.
  • dup2(oldfd, newfd):重定向,关闭newfd对应的文件使文件描述符newfd空闲,然后让newfd指向oldfd所指向的文件;dup2() makes newfd be the copy of oldfd, closing newfd first if necessary, but note the following: If oldfd is not a valid file descriptor, then the call fails, and newfd is not closed. If oldfd is a valid file descriptor, and newfd has the same value as oldfd, then dup2() does nothing, and returns newfd.

  • 函数参数
  • oldfd:旧的文件描述符
  • newfd:新的文件描述符
  • 函数返回值
  • On success, these system calls return the new descriptor.
  • On error, -1 is returned, and errno is set appropriately.

示例:一句话打印两次,先打入文件,后打至屏幕

/************************************************************
  >File Name  : dup_test.c
  >Author     : QQ
  >Company    : QQ
  >Create Time: 2022年05月17日 星期二 16时09分41秒
************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <fcntl.h>
int main(int argc, char* argv[])
{
  if(argc < 2)
  {
    printf("not found string\n");
    return -1;
  } 
  /*每开启一个进程,默认打开 0 1 2 三个文件描述符*/
  /*首先备份标准输出*/
  int stdoutfd = dup(STDOUT_FILENO);
  /*打开一个文件*/
  int fd = open("hello.txt", O_WRONLY | O_CREAT, 0644);
  /*重定向标准输出1至文件*/
  dup2(fd, STDOUT_FILENO);  
  printf("first: %s\n", argv[1]);
    /*  ===========================
  printf会进行系统调用,需要刷新buffer
      ===========================
  fflush(stdout);
  */
  /*恢复标准输出*/
  dup2(stdoutfd, STDOUT_FILENO);
  printf("second: %s\n", argv[1]);
  close(fd);
  return 0;
}

我们编译运行一下,你会发现两次都打印在了屏幕上,其实这就是我们在《系统API与C库函数的调用关系》中讲的系统调用问题,C库函数printf()会调用系统API函数write(),这是会用到一个文件指针,这里面有一个缓冲区buffer,要打印的内容会先放入到buffer中,如果我们在第一次调用printf()函数后不刷新这个buffer缓冲区的话,在第二次打印的时候,buffer就会保留有上次调用时放入缓冲区的内容,所以打印到标准输出时,打印了两句话。

注意:这里的 “hello\ linux” 中,"\ " 使通过转义符把空格的特殊含义去掉,如果不加转义符,shell会把空格分开的内容当作两个字符串,通过转义符就可以实现在字符串中写入空格,这是shell的知识。

解决方法就是在第二次打印前刷新一下缓冲区,将上面代码中的fflush()函数放出即可


相关文章
|
14天前
|
安全 Linux 虚拟化
网络名称空间在Linux虚拟化技术中的位置
网络名称空间(Network Namespaces)是Linux内核特性之一,提供了隔离网络环境的能力,使得每个网络名称空间都拥有独立的网络设备、IP地址、路由表、端口号范围以及iptables规则等。这一特性在Linux虚拟化技术中占据了核心位置🌟,它不仅为构建轻量级虚拟化解决方案(如容器📦)提供了基础支持,也在传统的虚拟机技术中发挥作用,实现资源隔离和网络虚拟化。
网络名称空间在Linux虚拟化技术中的位置
|
14天前
|
网络协议 安全 Linux
Linux网络名称空间之独立网络资源管理
Linux网络名称空间是一种强大的虚拟化技术🛠️,它允许用户创建隔离的网络环境🌐,每个环境拥有独立的网络资源和配置。这项技术对于云计算☁️、容器化应用📦和网络安全🔒等领域至关重要。本文将详细介绍在Linux网络名称空间中可以拥有的独立网络资源,并指出应用开发人员在使用时应注意的重点。
|
14天前
|
安全 网络协议 Linux
Linux网络名称空间概述
Linux网络名称空间是操作系统级别的一种虚拟化技术🔄,它允许创建隔离的网络环境🌐,使得每个环境拥有自己独立的网络资源,如IP地址📍、路由表🗺️、防火墙规则🔥等。这种技术是Linux内核功能的一部分,为不同的用户空间进程提供了一种创建和使用独立网络协议栈的方式。本文旨在全方面、多维度解释Linux网络名称空间的概念、必要性和作用。
Linux网络名称空间概述
|
1月前
|
存储 缓存 Linux
如何在Linux环境下对pip的缓存地址进行修改
如何在Linux环境下对pip的缓存地址进行修改
|
1月前
|
存储 Shell Linux
【Shell 命令集合 磁盘维护 】Linux 创建一个用作交换空间(swap space)的特殊文件或设备 mkswap命令使用教程
【Shell 命令集合 磁盘维护 】Linux 创建一个用作交换空间(swap space)的特殊文件或设备 mkswap命令使用教程
33 0
|
17天前
|
Linux 编译器 Windows
【Linux】10. 进程地址空间
【Linux】10. 进程地址空间
19 4
|
28天前
|
存储 Linux 程序员
【Linux C/C++ 堆内存分布】深入理解Linux进程的堆空间管理
【Linux C/C++ 堆内存分布】深入理解Linux进程的堆空间管理
72 0
|
30天前
|
存储 安全 Linux
进程地址空间
进程地址空间
16 2
|
1月前
|
存储 Linux Shell
【Shell 命令集合 磁盘维护 】Linux 关闭Linux系统中的交换空间 swapoff命令使用教程
【Shell 命令集合 磁盘维护 】Linux 关闭Linux系统中的交换空间 swapoff命令使用教程
29 1
|
1月前
|
网络协议 Linux C++
Linux C/C++ 网络编程中地址格式转换(inet_pton和inet_ntop函数)
Linux C/C++ 网络编程中地址格式转换(inet_pton和inet_ntop函数)
25 0