데이터 베이스 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 기반 앱

AI Clothes Remover

AI Clothes Remover

사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool

Undress AI Tool

무료로 이미지를 벗다

Clothoff.io

Clothoff.io

AI 옷 제거제

AI Hentai Generator

AI Hentai Generator

AI Hentai를 무료로 생성하십시오.

인기 기사

R.E.P.O. 에너지 결정과 그들이하는 일 (노란색 크리스탈)
3 몇 주 전 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. 최고의 그래픽 설정
3 몇 주 전 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. 아무도들을 수없는 경우 오디오를 수정하는 방법
3 몇 주 전 By 尊渡假赌尊渡假赌尊渡假赌
WWE 2K25 : Myrise에서 모든 것을 잠금 해제하는 방법
4 몇 주 전 By 尊渡假赌尊渡假赌尊渡假赌

뜨거운 도구

메모장++7.3.1

메모장++7.3.1

사용하기 쉬운 무료 코드 편집기

SublimeText3 중국어 버전

SublimeText3 중국어 버전

중국어 버전, 사용하기 매우 쉽습니다.

스튜디오 13.0.1 보내기

스튜디오 13.0.1 보내기

강력한 PHP 통합 개발 환경

드림위버 CS6

드림위버 CS6

시각적 웹 개발 도구

SublimeText3 Mac 버전

SublimeText3 Mac 버전

신 수준의 코드 편집 소프트웨어(SublimeText3)

0x80004005 오류 코드가 나타나는 경우 수행할 작업 편집기에서 0x80004005 오류 코드를 해결하는 방법을 알려줍니다. 0x80004005 오류 코드가 나타나는 경우 수행할 작업 편집기에서 0x80004005 오류 코드를 해결하는 방법을 알려줍니다. Mar 21, 2024 pm 09:17 PM

컴퓨터에서 폴더를 삭제하거나 압축을 풀 때 "오류 0x80004005: 지정되지 않은 오류"라는 프롬프트 대화 상자가 나타나는 경우가 있습니다. 이러한 상황이 발생하면 어떻게 해야 합니까? 실제로 오류 코드 0x80004005가 나타나는 데에는 여러 가지 이유가 있지만 대부분은 바이러스로 인해 발생합니다. 문제를 해결하기 위해 dll을 다시 등록할 수 있습니다. 아래에서는 편집기에서 0x80004005 오류 코드를 처리한 경험을 설명합니다. . 일부 사용자는 컴퓨터를 사용할 때 오류 코드 0X80004005가 표시됩니다. 0x80004005 오류는 주로 컴퓨터가 특정 동적 링크 라이브러리 파일을 올바르게 등록하지 않거나 컴퓨터와 인터넷 간의 HTTPS 연결을 허용하지 않는 방화벽으로 인해 발생합니다. 그렇다면 어떨까요?

Quark Cloud Disk에서 Baidu Cloud Disk로 파일을 전송하는 방법은 무엇입니까? Quark Cloud Disk에서 Baidu Cloud Disk로 파일을 전송하는 방법은 무엇입니까? Mar 14, 2024 pm 02:07 PM

Quark Netdisk와 Baidu Netdisk는 현재 파일 저장에 가장 일반적으로 사용되는 Netdisk 소프트웨어입니다. Quark Netdisk의 파일을 Baidu Netdisk에 저장하려면 어떻게 해야 합니까? 이번 호에서는 편집자가 Quark Network Disk 컴퓨터에서 Baidu Network Disk로 파일을 전송하는 방법에 대한 튜토리얼 단계를 정리했습니다. Quark 네트워크 디스크 파일을 Baidu 네트워크 디스크에 저장하는 방법은 무엇입니까? Quark Network Disk에서 Baidu Network Disk로 파일을 전송하려면 먼저 Quark Network Disk에서 필요한 파일을 다운로드한 다음 Baidu Network Disk 클라이언트에서 대상 폴더를 선택하고 열어야 합니다. 그런 다음 Quark Cloud Disk에서 다운로드한 파일을 Baidu Cloud Disk 클라이언트가 연 폴더에 끌어서 놓거나 업로드 기능을 사용하여 Baidu Cloud Disk에 파일을 추가합니다. 업로드가 완료된 후 파일이 Baidu Cloud Disk에 성공적으로 전송되었는지 확인하세요. 그게 다야

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 드라이브의 루트 디렉터리에 저장되며 크기는 시스템에 설치된 메모리 크기와 동일합니다. 이 파일은 컴퓨터가 최대 절전 모드일 때 사용되며, 복구 시 빠르게 이전 상태로 복원할 수 있도록 현재 시스템의 메모리 데이터를 담고 있습니다. 크기가 메모리 용량과 동일하므로 하드 드라이브 공간을 더 많이 차지할 수 있습니다. 동면

Moondrop, 지연 시간이 짧은 게임 모드를 갖춘 Block 진정한 무선 이어버드 출시 Moondrop, 지연 시간이 짧은 게임 모드를 갖춘 Block 진정한 무선 이어버드 출시 Aug 10, 2024 pm 03:31 PM

Moondrop은 오디오 매니아를 위해 외이에 편안하게 착용할 수 있는 Block 진정한 무선 이어버드를 출시했습니다. 외이도에 걸린 이어버드와 달리 Block은 귀가 막히는 느낌을 유발하지 않으며 귀지를 수집하지 않습니다. 13mm 드라이버가 동봉되어 있습니다.

Google 보안 코드를 받을 수 있는 곳 Google 보안 코드를 받을 수 있는 곳 Mar 30, 2024 am 11:11 AM

Google OTP는 사용자 계정의 보안을 보호하기 위해 사용되는 도구로, 그 핵심은 동적 인증 코드를 생성하는 데 사용되는 중요한 정보입니다. Google OTP의 키를 잊어버렸고 보안 코드를 통해서만 확인할 수 있는 경우, 이 웹사이트의 편집자가 Google 보안 코드를 얻을 수 있는 위치에 대한 자세한 소개를 제공할 것입니다. 더 많은 정보를 알고 계시다면 아래 내용을 계속 읽어주세요! 먼저 전화 설정을 열고 설정 페이지로 들어갑니다. 페이지를 아래로 스크롤하여 Google을 찾으세요. Google 페이지로 이동하여 Google 계정을 클릭하세요. 계정 페이지에 들어가서 인증 코드 아래에 있는 보기를 클릭하세요. 비밀번호를 입력하거나 지문을 사용하여 신원을 확인하세요. Google 보안 코드를 받고 보안 코드를 사용하여 Google 신원을 확인하세요.

MySQL에서 .ibd 파일의 역할과 관련 주의사항에 대한 자세한 설명 MySQL에서 .ibd 파일의 역할과 관련 주의사항에 대한 자세한 설명 Mar 15, 2024 am 08:00 AM

MySQL에서 .ibd 파일의 역할 및 관련 주의사항에 대한 자세한 설명 MySQL은 널리 사용되는 관계형 데이터베이스 관리 시스템이며 데이터베이스의 데이터는 서로 다른 파일에 저장됩니다. 그 중 .ibd 파일은 InnoDB 스토리지 엔진의 데이터 파일로, 테이블에 데이터와 인덱스를 저장하는 데 사용됩니다. 이 기사에서는 MySQL에서 .ibd 파일의 역할에 대한 자세한 분석을 제공하고 관련 코드 예제를 제공하여 독자의 이해를 돕습니다. 1. .ibd 파일의 역할: 데이터 저장: .ibd 파일은 InnoDB 저장소입니다.

Realme 12 Pro에 듀얼 SIM을 설치하는 방법은 무엇입니까? Realme 12 Pro에 듀얼 SIM을 설치하는 방법은 무엇입니까? Mar 18, 2024 pm 02:10 PM

국내 휴대폰의 일반적인 작동은 매우 유사하지만 일부 세부 사항에는 여전히 약간의 차이가 있습니다. 예를 들어 휴대폰 모델 및 제조업체에 따라 듀얼 SIM 설치 방법이 다를 수 있습니다. 신형 휴대폰인 Erzhenwo 12Pro도 듀얼심 듀얼 대기를 지원하는데, 이 휴대폰에 듀얼심을 어떻게 설치해야 할까요? Realme 12Pro에 듀얼 SIM을 설치하는 방법은 무엇입니까? 설치하기 전에 휴대폰을 끄는 것을 잊지 마십시오. 1단계: SIM 카드 트레이 찾기: 휴대폰의 SIM 카드 트레이를 찾습니다. 일반적으로 Realme 12 Pro에서는 SIM 카드 트레이가 휴대폰 측면이나 상단에 있습니다. 2단계: 첫 번째 SIM 카드를 삽입합니다. 전용 SIM 카드 핀이나 작은 물체를 사용하여 SIM 카드 트레이의 슬롯에 삽입합니다. 그런 다음 첫 번째 SIM 카드를 조심스럽게 삽입합니다.

Linux '.a' 파일 생성 및 실행 Linux '.a' 파일 생성 및 실행 Mar 20, 2024 pm 04:46 PM

Linux 운영 체제에서 파일을 작업하려면 개발자가 파일, 코드, 프로그램, 스크립트 및 기타 항목을 효율적으로 생성하고 실행할 수 있도록 하는 다양한 명령과 기술을 사용해야 합니다. Linux 환경에서는 확장자가 ".a"인 파일이 정적 라이브러리로서 매우 중요합니다. 이러한 라이브러리는 소프트웨어 개발에서 중요한 역할을 수행하므로 개발자는 여러 프로그램에서 공통 기능을 효율적으로 관리하고 공유할 수 있습니다. Linux 환경에서 효과적인 소프트웨어 개발을 위해서는 ".a" 파일을 생성하고 실행하는 방법을 이해하는 것이 중요합니다. 이번 글에서는 리눅스 ".a" 파일을 포괄적으로 설치하고 구성하는 방법을 소개한다. 리눅스 ".a" 파일의 정의, 목적, 구조, 생성 및 실행 방법을 살펴보자. L은 무엇입니까?

See all articles