首页 数据库 mysql教程 HDFS读文件过程分析:获取文件对应的Block列表

HDFS读文件过程分析:获取文件对应的Block列表

Jun 07, 2016 pm 04:38 PM
Block hdfs 分析 列表 文件 获取 过程

在使用Java读取一个文件系统中的一个文件时,我们会首先构造一个DataInputStream对象,然后就能够从文件中读取数据。对于存储在HDFS上的文件,也对应着类似的工具类,但是底层的实现逻辑却是非常不同的。我们先从使用DFSClient.DFSDataInputStream类来读取HD

在使用Java读取一个文件系统中的一个文件时,我们会首先构造一个DataInputStream对象,然后就能够从文件中读取数据。对于存储在HDFS上的文件,也对应着类似的工具类,但是底层的实现逻辑却是非常不同的。我们先从使用DFSClient.DFSDataInputStream类来读取HDFS上一个文件的一段代码来看,如下所示:
package org.shirdrn.hadoop.hdfs;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FSDataInputStream;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
public class HdfsFileReader {
     public static void main(String[] args) {
          String file = "hdfs://hadoop-cluster-m:8020/data/logs/basis_user_behavior/201405071237_10_10_1_73.log";
          Path path = new Path(file);
          Configuration conf = new Configuration();
          FileSystem fs;
          FSDataInputStream in;
          BufferedReader reader = null;
          try {
               fs = FileSystem.get(conf);
               in = fs.open(path); // 打开文件path,返回一个FSDataInputStream流对象
               reader = new BufferedReader(new InputStreamReader(in));
               String line = null;
               while((line = reader.readLine()) != null) { // 读取文件行内容
                    System.out.println("Record: " + line);
               }
          } catch (IOException e) {
               e.printStackTrace();
          } finally {
               try {
                    if(reader != null) reader.close();
               } catch (IOException e) {
                    e.printStackTrace();
               }
          }
     }
}
登录后复制

基于上面代码,我们可以看到,通过一个FileSystem对象可以打开一个Path文件,返回一个FSDataInputStream文件输入流对象,然后从该FSDataInputStream对象就能够读取出文件的内容。所以,我们从FSDataInputStream入手,详细分析从HDFS读取文件内容的过程,在实际地读取物理数据块之前,首先要获取到文件对应的Block列表元数据信息,整体流程如下图所示:
hdfs-get-block-locations
下面,详细说明整个流程:

创建FSDataInputStream流对象

从一个Path路径对象,能够获取到一个FileSystem对象,然后通过调用FileSystem的open方法打开一个文件流:

  public FSDataInputStream open(Path f) throws IOException {
    return open(f, getConf().getInt("io.file.buffer.size", 4096));
  }
登录后复制

由于FileSystem是抽象类,将具体的打开操作留给具体子类实现,例如FTPFileSystem、HarFileSystem、WebHdfsFileSystem等,不同的文件系统具有不同打开文件的行为,我们以DistributedFileSystem为例,open方法实现,代码如下所示:

  public FSDataInputStream open(Path f, int bufferSize) throws IOException {
    statistics.incrementReadOps(1);
    return new DFSClient.DFSDataInputStream(
          dfs.open(getPathName(f), bufferSize, verifyChecksum, statistics));
  }
登录后复制

statistics对象用来收集文件系统操作的统计数据,这里使读取文件操作的计数器加1。然后创建了一个DFSClient.DFSDataInputStream对象,该对象的参数是通过DFSClient dfs客户端对象打开一个这个文件从而返回一个DFSInputStream对象,下面,我们看DFSClient的open方法实现,代码如下所示:

  public DFSInputStream open(String src, int buffersize, boolean verifyChecksum,
                      FileSystem.Statistics stats) throws IOException {
    checkOpen();
    //    Get block info from namenode
    return new DFSInputStream(src, buffersize, verifyChecksum);
  }
登录后复制

checkOpen方法就是检查一个标志位clientRunning,表示当前的dfs客户端对象是否已经创建并初始化,在dfs客户端创建的时候该标志就为true,表示客户端正在运行状态。我们知道,当客户端DFSClient连接到Namenode的时候,实际上是创建了一个到Namenode的RPC连接,Namenode作为Server角色,DFSClient作为Client角色,它们之间建立起Socket连接。只有显式调用DFSClient的close方法时,才会修改clientRunning的值为false,实际上真正地关闭了已经建立的RPC连接。
我们看一下创建DFSInputStream的构造方法实现:

    DFSInputStream(String src, int buffersize, boolean verifyChecksum) throws IOException {
      this.verifyChecksum = verifyChecksum;
      this.buffersize = buffersize;
      this.src = src;
      prefetchSize = conf.getLong("dfs.read.prefetch.size", prefetchSize);
      openInfo();
    }
登录后复制

先设置了几个与读取文件相关的参数值,这里有一个预先读取文件的Block字节数的参数prefetchSize,它的值设置如下:

  public static final long DEFAULT_BLOCK_SIZE = DFSConfigKeys.DFS_BLOCK_SIZE_DEFAULT;
  public static final long    DFS_BLOCK_SIZE_DEFAULT = 64*1024*1024;
    defaultBlockSize = conf.getLong("dfs.block.size", DEFAULT_BLOCK_SIZE);
    private long prefetchSize = 10 * defaultBlockSize;
登录后复制

这个prefetchSize的值默认为10*64*1024*1024=671088640,也就是说,默认预读取一个文件的10个块,即671088640B=640M,如果想要修改这个值,设置dfs.block.size即可覆盖默认值。
然后调用了openInfo方法,从Namenode获取到该打开文件的信息,在openInfo方法中,具体实现如下所示:

    synchronized void openInfo() throws IOException {
      for (int retries = 3; retries > 0; retries--) {
        if (fetchLocatedBlocks()) { // fetch block success. 如果成功获取到待读取文件对应的Block列表,则直接返回
          return;
        } else {
          // Last block location unavailable. When a cluster restarts,
          // DNs may not report immediately. At this time partial block
          // locations will not be available with NN for getting the length.
          // Lets retry a few times to get the length.
          DFSClient.LOG.warn("Last block locations unavailable. "
              + "Datanodes might not have reported blocks completely."
              + " Will retry for " + retries + " times");
          waitFor(4000);
        }
      }
      throw new IOException("Could not obtain the last block locations.");
    }
登录后复制

上述代码中,有一个for循环用来获取Block列表。如果成功获取到待读取文件的Block列表,则直接返回,否则,最多执行3次等待重试操作(最多花费时间大于12秒)。未能成功读取文件的Block列表信息,是因为Namenode无法获取到文件对应的块列表的信息,当整个集群启动的时候,Datanode会主动向NNamenode上报对应的Block信息,只有Block Report完成之后,Namenode就能够知道组成文件的Block及其所在Datanode列表的信息。openInfo方法方法中调用了fetchLocatedBlocks方法,用来与Namenode进行RPC通信调用,实际获取对应的Block列表,实现代码如下所示:

    private boolean fetchLocatedBlocks() throws IOException,
        FileNotFoundException {
      LocatedBlocks newInfo = callGetBlockLocations(namenode, src, 0, prefetchSize);
      if (newInfo == null) {
        throw new FileNotFoundException("File does not exist: " + src);
      }
      if (locatedBlocks != null && !locatedBlocks.isUnderConstruction() && !newInfo.isUnderConstruction()) {
        Iterator<locatedblock> oldIter = locatedBlocks.getLocatedBlocks().iterator();
        Iterator<locatedblock> newIter = newInfo.getLocatedBlocks().iterator();
        while (oldIter.hasNext() && newIter.hasNext()) {
          if (!oldIter.next().getBlock().equals(newIter.next().getBlock())) {
            throw new IOException("Blocklist for " + src + " has changed!");
          }
        }
      }
      boolean isBlkInfoUpdated = updateBlockInfo(newInfo);
      this.locatedBlocks = newInfo;
      this.currentNode = null;
      return isBlkInfoUpdated;
    }
</locatedblock></locatedblock>
登录后复制

调用callGetBlockLocations方法,实际上是根据创建RPC连接以后得到的Namenode的代理对象,调用Namenode来获取到指定文件的Block的位置信息(位于哪些Datanode节点上):namenode.getBlockLocations(src, start, length)。调用callGetBlockLocations方法返回一个LocatedBlocks对象,该对象包含了文件长度信息、List blocks列表对象,其中LocatedBlock包含了一个Block的基本信息:

  private Block b;
  private long offset;  // offset of the first byte of the block in the file
  private DatanodeInfo[] locs;
  private boolean corrupt;
登录后复制

有了这些文件的信息(文件长度、文件包含的Block的位置等信息),DFSClient就能够执行后续读取文件数据的操作了,详细过程我们在后面分析说明。

通过Namenode获取文件信息

上面,我们提到获取一个文件的基本信息,是通过Namenode来得到的,这里详细分析Namenode是如何获取到这些文件信息的,实现方法getBlockLocations的代码,如下所示:

  public LocatedBlocks getBlockLocations(String src, long offset, long length) throws IOException {
    myMetrics.incrNumGetBlockLocations();
    return namesystem.getBlockLocations(getClientMachine(), src, offset, length);
  }
登录后复制

可以看到,Namenode又委托管理HDFS name元数据的FSNamesystem的getBlockLocations方法实现:

  LocatedBlocks getBlockLocations(String clientMachine, String src, long offset, long length) throws IOException {
    LocatedBlocks blocks = getBlockLocations(src, offset, length, true, true, true);
    if (blocks != null) {
      //sort the blocks
      // In some deployment cases, cluster is with separation of task tracker
      // and datanode which means client machines will not always be recognized
      // as known data nodes, so here we should try to get node (but not
      // datanode only) for locality based sort.
      Node client = host2DataNodeMap.getDatanodeByHost(clientMachine);
      if (client == null) {
        List<string> hosts = new ArrayList<string> (1);
        hosts.add(clientMachine);
        String rName = dnsToSwitchMapping.resolve(hosts).get(0);
        if (rName != null)
          client = new NodeBase(clientMachine, rName);
      }  
      DFSUtil.StaleComparator comparator = null;
      if (avoidStaleDataNodesForRead) {
        comparator = new DFSUtil.StaleComparator(staleInterval);
      }
      // Note: the last block is also included and sorted
      for (LocatedBlock b : blocks.getLocatedBlocks()) {
        clusterMap.pseudoSortByDistance(client, b.getLocations());
        if (avoidStaleDataNodesForRead) {
          Arrays.sort(b.getLocations(), comparator);
        }
      }
    }
    return blocks;
  }
</string></string>
登录后复制

跟踪代码,最终会在下面的方法中实现了,如何获取到待读取文件的Block的元数据列表,以及如何取出该文件的各个Block的数据,方法实现代码,这里我做了详细的注释,可以参考,如下所示:

  private synchronized LocatedBlocks getBlockLocationsInternal(String src,
                                                       long offset,
                                                       long length,
                                                       int nrBlocksToReturn,
                                                       boolean doAccessTime,
                                                       boolean needBlockToken)
                                                       throws IOException {
          INodeFile inode = dir.getFileINode(src);  // 获取到与待读取文件相关的inode数据
          if (inode == null) {
               return null;
          }
          if (doAccessTime && isAccessTimeSupported()) {
               dir.setTimes(src, inode, -1, now(), false);
          }
          Block[] blocks = inode.getBlocks(); // 获取到文件src所包含的Block的元数据列表信息
          if (blocks == null) {
               return null;
          }
          if (blocks.length == 0) { // 获取到文件src的Block数,这里=0,该文件的Block数据还没创建,可能正在创建
               return inode.createLocatedBlocks(new ArrayList<locatedblock>(blocks.length));
          }
          List<locatedblock> results;
          results = new ArrayList<locatedblock>(blocks.length);
          int curBlk = 0; // 当前Block在Block[] blocks数组中的索引位置
          long curPos = 0, blkSize = 0; // curPos表示某个block在文件中的字节偏移量,blkSize为Block的大小(字节数)
          int nrBlocks = (blocks[0].getNumBytes() == 0) ? 0 : blocks.length; // 获取到文件src的Block数,实际上一定>0,但是第一个block大小可能为0,这种情况认为nrBlocks=0
          for (curBlk = 0; curBlk 0,所以我觉得这段代码写的稍微有点晦涩)
               blkSize = blocks[curBlk].getNumBytes();
               assert blkSize > 0 : "Block of size 0";
               if (curPos + blkSize > offset) {
                    break;
               }
               curPos += blkSize;
          }
          if (nrBlocks > 0 && curBlk == nrBlocks) // offset >= end of file, 到这里curBlk=0,如果从文件src的第一个Block的字节数累加计算,知道所有的Block的字节数都累加上了,总字节数仍然Datanode映射的列表中,无法读取该Block的Datanode节点数
               if (numCorruptNodes != numCorruptReplicas) {
                    LOG.warn("Inconsistent number of corrupt replicas for "
                              + blocks[curBlk] + "blockMap has " + numCorruptNodes
                              + " but corrupt replicas map has " + numCorruptReplicas);
               }
               DatanodeDescriptor[] machineSet = null;  // 下面的if...else用来获取一个Block所在的Datanode节点
               boolean blockCorrupt = false;
               if (inode.isUnderConstruction() && curBlk == blocks.length - 1
                         && blocksMap.numNodes(blocks[curBlk]) == 0) { // 如果文件正在创建,当前blocks[curBlk]还没有创建成功(即没有可用的Datanode可以提供该Block的服务),仍然返回待创建Block所在的Datanode节点列表。数据块是在Datanode上存储的,只要Datanode完成数据块的存储后,通过heartbeat将数据块的信息上报给Namenode后,这些信息才会存储到blocksMap中
                    // get unfinished block locations
                    INodeFileUnderConstruction cons = (INodeFileUnderConstruction) inode;
                    machineSet = cons.getTargets();
                    blockCorrupt = false;
               } else { // 文件已经创建完成
                    blockCorrupt = (numCorruptNodes == numNodes); // 是否当前的Block在所有Datanode节点上的副本都坏掉,无法提供服务
                    int numMachineSet = blockCorrupt ? numNodes : (numNodes - numCorruptNodes); // 如果是,则返回所有Datanode节点,否则,只返回可用的Block副本所在的Datanode节点
                    machineSet = new DatanodeDescriptor[numMachineSet];
                    if (numMachineSet > 0) { // 获取到当前Block所有副本所在的Datanode节点列表
                         numNodes = 0;
                         for (Iterator<datanodedescriptor> it = blocksMap.nodeIterator(blocks[curBlk]); it.hasNext();) {
                              DatanodeDescriptor dn = it.next();
                              boolean replicaCorrupt = corruptReplicas.isReplicaCorrupt(blocks[curBlk], dn);
                              if (blockCorrupt || (!blockCorrupt && !replicaCorrupt))
                                   machineSet[numNodes++] = dn;
                         }
                    }
               }
               LocatedBlock b = new LocatedBlock(blocks[curBlk], machineSet, curPos, blockCorrupt); // 创建一个包含Block的元数据对象、所在Datanode节点列表、起始索引位置(字节数)、健康状况的LocatedBlock对象
               if (isAccessTokenEnabled && needBlockToken) { // 如果启用Block级的令牌(Token)访问,则为当前用户生成读模式的令牌信息,一同封装到返回的LocatedBlock对象中
                    b.setBlockToken(accessTokenHandler.generateToken(b.getBlock(), EnumSet.of(BlockTokenSecretManager.AccessMode.READ)));
               }
               results.add(b); // 收集待返回给读取文件的客户端需要的LocatedBlock列表
               curPos += blocks[curBlk].getNumBytes();
               curBlk++;
          } while (curPos 
<p>我们可以看一下,最后的调用inode.createLocatedBlocks(results)生成LocatedBlocks对象的实现,代码如下所示:</p>
<pre class="brush:php;toolbar:false">
  LocatedBlocks createLocatedBlocks(List<locatedblock> blocks) {
    return new LocatedBlocks(computeContentSummary().getLength(), blocks, isUnderConstruction()); // 通过ContentSummary对象获取到文件的长度
  }
</locatedblock>
登录后复制

客户端通过RPC调用,获取到了文件对应的Block以及所在Datanode列表的信息,然后就可以根据LocatedBlocks来进一步获取到对应的Block对应的物理数据块。

对Block列表进行排序

我们再回到FSNamesystem类,调用getBlockLocationsInternal方法的getBlockLocations方法中,在返回文件block列表LocatedBlocks之后,会对每一个Block所在的Datanode进行的一个排序,排序的基本规则有如下2点:

  • Client到Block所在的Datanode的距离最近,这个是通过网络拓扑关系来进行计算,例如Client的网络路径为/dc1/r1/c1,那么路径为/dc1/r1/dn1的Datanode就比路径为/dc1/r2/dn2的距离小,/dc1/r1/dn1对应的Block就会排在前面
  • 从上面一点可以推出,如果Client就是某个Datanode,恰好某个Block的Datanode列表中包括该Datanode,则该Datanode对应的Block排在前面
  • Block所在的Datanode列表中,如果其中某个Datanode在指定的时间内没有向Namenode发送heartbeat(默认由常量DFSConfigKeys.DFS_NAMENODE_STALE_DATANODE_INTERVAL_DEFAULT定义,默认值为30s),则该Datanode的状态即为STALE,具有该状态的Datanode对应的Block排在后面

基于上述规则排序后,Block列表返回到Client。

Client与Datanode交互更新文件Block列表

我们要回到前面分析的DFSClient.DFSInputStream.fetchLocatedBlocks()方法中,查看在调用该方法之后,是如何执行实际处理逻辑的:

    private boolean fetchLocatedBlocks() throws IOException,
        FileNotFoundException {
      LocatedBlocks newInfo = callGetBlockLocations(namenode, src, 0, prefetchSize); // RPC调用向Namenode获取待读取文件对应的Block及其位置信息LocatedBlocks对象
      if (newInfo == null) {
        throw new FileNotFoundException("File does not exist: " + src);
      }
      if (locatedBlocks != null && !locatedBlocks.isUnderConstruction() && !newInfo.isUnderConstruction()) { // 这里面locatedBlocks!=null是和后面调用updateBlockInfo方法返回的状态有关的
        Iterator<locatedblock> oldIter = locatedBlocks.getLocatedBlocks().iterator();
        Iterator<locatedblock> newIter = newInfo.getLocatedBlocks().iterator();
        while (oldIter.hasNext() && newIter.hasNext()) { // 检查2次获取到的LocatedBlock列表:第2次得到newInfo包含的Block列表,在第2次得到的locatedBlocks中是否发生变化,如果发生了变化,则不允许读取,抛出异常
          if (!oldIter.next().getBlock().equals(newIter.next().getBlock())) {
            throw new IOException("Blocklist for " + src + " has changed!");
          }
        }
      }
      boolean isBlkInfoUpdated = updateBlockInfo(newInfo);
      this.locatedBlocks = newInfo;
      this.currentNode = null;
      return isBlkInfoUpdated;
    }
</locatedblock></locatedblock>
登录后复制

如果第一次读取该文件时,已经获取到了对应的block列表,缓存在客户端;如果客户端第二次又读取了该文件,仍然获取到一个block列表对象。在两次读取之间,可能存在原文件完全被重写的情况,所以新得到的block列表与原列表完全不同了,存在这种情况,客户端直接抛出IO异常,如果原文件对应的block列表没有变化,则更新客户端缓存的对应block列表信息。
当集群重启的时候(如果允许安全模式下读文件),或者当一个文件正在创建的时候,Datanode向Namenode进行Block Report,这个过程中可能Namenode还没有完全重建好Block到Datanode的映射关系信息,所以即使在这种情况下,仍然会返回对应的正在创建的Block所在的Datanode列表信息,可以从前面getBlockLocationsInternal方法中看到,INode的对应UnderConstruction状态为true。这时,一个Block对应的所有副本中的某些可能还在创建过程中。
上面方法中,调用updateBlockInfo来更新文件的Block元数据列表信息,对于文件的某些Block可能没有创建完成,所以Namenode所保存的关于文件的Block的的元数据信息可能没有及时更新(Datanode可能还没有完成Block的报告),代码实现如下所示:

    private boolean updateBlockInfo(LocatedBlocks newInfo) throws IOException {
      if (!serverSupportsHdfs200 || !newInfo.isUnderConstruction() || !(newInfo.locatedBlockCount() > 0)) { // 如果获取到的newInfo可以读取文件对应的Block信息,则返回true
        return true;
      }
      LocatedBlock last = newInfo.get(newInfo.locatedBlockCount() - 1); // 从Namenode获取文件的最后一个Block的元数据对象LocatedBlock
      boolean lastBlockInFile = (last.getStartOffset() + last.getBlockSize() == newInfo.getFileLength()); 
      if (!lastBlockInFile) { // 如果“文件长度 != 最后一个块起始偏移量 + 最后一个块长度”,说明文件对应Block的元数据信息还没有更新,但是仍然返回给读取文件的该客户端
        return true;
      }
      // 这时,已经确定last是该文件的最后一个bolck,检查最后个block的存储位置信息
      if (last.getLocations().length == 0) {
        return false;
      }
      ClientDatanodeProtocol primary = null;
      Block newBlock = null;
      for (int i = 0; i 
<p>我们看一下,在updateBlockInfo方法中,返回false的情况:Client向Namenode发起的RPC请求,已经获取到了组成该文件的数据块的元数据信息列表,但是,文件的最后一个数据块的存储位置信息无法获取到,说明Datanode还没有及时通过block report将数据块的存储位置信息报告给Namenode。通过在openInfo()方法中可以看到,获取文件的block列表信息有3次重试机会,也就是调用updateBlockInfo方法返回false,可以有12秒的时间,等待Datanode向Namenode汇报文件的最后一个块的位置信息,以及Namenode更新内存中保存的文件对应的数据块列表元数据信息。<br>
我们再看一下,在updateBlockInfo方法中,返回true的情况:</p>
登录后复制
  • 文件已经创建完成,文件对应的block列表元数据信息可用
  • 文件正在创建中,但是当前能够读取到的已经完成的最后一个块(非组成文件的最后一个block)的元数据信息可用
  • 文件正在创建中,文件的最后一个block的元数据部分可读:从Namenode无法获取到该block对应的位置信息,这时Client会与Datanode直接进行RPC通信,获取到该文件最后一个block的位置信息

上面Client会与Datanode直接进行RPC通信,获取文件最后一个block的元数据,这时可能由于网络问题等等,无法得到文件最后一个block的元数据,所以也会返回true,也就是说,Client仍然可以读取该文件,只是无法读取到最后一个block的数据。
这样,在Client从Namenode/Datanode获取到的文件的Block列表元数据已经是可用的信息,可以根据这些信息读取到各个Block的物理数据块内容了,准确地说,应该是文件处于打开状态了,已经准备好后续进行的读操作了。

本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

Video Face Swap

Video Face Swap

使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热门文章

<🎜>:泡泡胶模拟器无穷大 - 如何获取和使用皇家钥匙
4 周前 By 尊渡假赌尊渡假赌尊渡假赌
北端:融合系统,解释
4 周前 By 尊渡假赌尊渡假赌尊渡假赌
Mandragora:巫婆树的耳语 - 如何解锁抓钩
3 周前 By 尊渡假赌尊渡假赌尊渡假赌

热工具

记事本++7.3.1

记事本++7.3.1

好用且免费的代码编辑器

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用

禅工作室 13.0.1

禅工作室 13.0.1

功能强大的PHP集成开发环境

Dreamweaver CS6

Dreamweaver CS6

视觉化网页开发工具

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

热门话题

Java教程
1670
14
CakePHP 教程
1428
52
Laravel 教程
1329
25
PHP教程
1276
29
C# 教程
1256
24
出现0x80004005错误代码怎么办 小编教你0x80004005错误代码解决方法 出现0x80004005错误代码怎么办 小编教你0x80004005错误代码解决方法 Mar 21, 2024 pm 09:17 PM

在电脑中删除或解压缩文件夹,时有时候会弹出提示对话框“错误0x80004005:未指定错误”,如果遇到这中情况应该怎么解决呢?提示错误代码0x80004005的原因其实有很多,但大部分因为病毒导致,我们可以重新注册dll来解决问题,下面,小编给大伙讲解0x80004005错误代码处理经验。有用户在使用电脑时出现错误代码0X80004005的提示,0x80004005错误主要是由于计算机没有正确注册某些动态链接库文件,或者计算机与Internet之间存在不允许的HTTPS连接防火墙所引起。那么如何

Moondrop 发布 Block 真无线耳机,具有低延迟游戏模式 Moondrop 发布 Block 真无线耳机,具有低延迟游戏模式 Aug 10, 2024 pm 03:31 PM

Moondrop 为音频爱好者发布了 Block 真无线耳机,可舒适地佩戴在外耳中。与塞入耳道的耳塞不同,Block 不会造成耳朵堵塞的感觉或积聚耳垢。随附 13 毫米驱动器

夸克网盘的文件怎么转移到百度网盘? 夸克网盘的文件怎么转移到百度网盘? Mar 14, 2024 pm 02:07 PM

  夸克网盘和百度网盘都是现在最常用的储存文件的网盘软件,如果想要将夸克网盘内的文件保存到百度网盘,要怎么操作呢?本期小编整理了夸克网盘电脑端的文件转移到百度网盘的教程步骤,一起来看看是怎么操作吧。  夸克网盘的文件怎么保存到百度网盘?要将夸克网盘的文件转移到百度网盘,首先需在夸克网盘下载所需文件,然后在百度网盘客户端中选择目标文件夹并打开。接着,将夸克网盘中下载的文件拖放到百度网盘客户端打开的文件夹中,或者使用上传功能将文件添加至百度网盘。确保上传完成后在百度网盘中查看文件是否成功转移。这样就

谷歌安全码在哪里获取 谷歌安全码在哪里获取 Mar 30, 2024 am 11:11 AM

谷歌验证器是一种用于保护用户账户安全的工具,其密钥是用于生成动态验证码的重要信息。如果忘记了谷歌验证器的密钥,只能通过安全码进行验证,那么下文本站小编就将为大家带来谷歌安全码在哪里获取的详细内容介绍,希望能帮助到大家,想要了解的用户们就请跟着下文继阅读吧!首先打开手机设置,进入设置页面。下拉页面,找到Google。进入Google页面,点击Google账号。进入账号页面,点击验证码下方的查看。输入密码或者使用指纹验证身份。获得Google安全码,利用安全码验证谷歌身份。

hiberfil.sys是什么文件?hiberfil.sys可以删除吗? hiberfil.sys是什么文件?hiberfil.sys可以删除吗? Mar 15, 2024 am 09:49 AM

  最近有很多网友问小编,hiberfil.sys是什么文件?hiberfil.sys占用了大量的C盘空间可以删除吗?小编可以告诉大家hiberfil.sys文件是可以删除的。下面就来看看详细的内容。hiberfil.sys是Windows系统中的一个隐藏文件,也是系统休眠文件。通常存储在C盘根目录下,其大小与系统安装内存大小相当。这个文件在计算机休眠时被使用,其中包含了当前系统的内存数据,以便在恢复时快速恢复到之前的状态。由于其大小与内存容量相等,因此它可能会占用较大的硬盘空间。  hiber

MySQL中.ibd文件的作用详解及相关注意事项 MySQL中.ibd文件的作用详解及相关注意事项 Mar 15, 2024 am 08:00 AM

MySQL中.ibd文件的作用详解及相关注意事项MySQL是一种流行的关系型数据库管理系统,数据库中的数据存储在不同的文件中。其中,.ibd文件是InnoDB存储引擎中的数据文件,用于存储表中的数据和索引。本文将对MySQL中.ibd文件的作用进行详细解析,并提供相关代码示例以帮助读者更好地理解。一、.ibd文件的作用:存储数据:.ibd文件是InnoDB存

Go 语言文件重命名操作全解析 Go 语言文件重命名操作全解析 Apr 08, 2024 pm 03:30 PM

Go语言中使用os.Rename函数重命名文件,语法为:funcRename(oldpath,newpathstring)error。该函数将oldpath指定的文件重命名为newpath指定的文件。示例包括简单重命名、移动文件到不同目录以及忽略错误处理。Rename函数执行原子操作,在两个文件位于同一目录时可能仅更新目录项,跨卷或正在使用的文件重命名可能失败。

创建和运行Linux'.a”文件 创建和运行Linux'.a”文件 Mar 20, 2024 pm 04:46 PM

在Linux操作系统中处理文件需要使用各种命令和技术,使开发人员能够高效地创建和执行文件、代码、程序、脚本和其他东西。在Linux环境中,扩展名为”.a”的文件作为静态库具有重要的重要性。这些库在软件开发中发挥着重要作用,允许开发人员有效地管理和共享多个程序的公共功能。对于Linux环境中的有效软件开发,了解如何创建和运行“.a”文件至关重要。本文将介绍如何全面安装和配置Linux“.a”文件,让我们一起探索Linux“.a”文件的定义、用途、结构,以及创建和执行它的方法。什么是L

See all articles