9. Lab: file system

本文档详细介绍了如何修改xv6文件系统以支持更大的文件大小,通过引入双重间接块,允许文件包含多达65803个块。同时,实现了symlink系统调用,创建符号链接文件。在实现过程中,注意了块分配、释放的逻辑,并对无限递归链接进行了防范。

https://pdos.csail.mit.edu/6.S081/2021/labs/fs.html

1. Large files (moderate)

1.1 要求

Modify bmap() so that it implements a doubly-indirect block, in addition to direct blocks and a singly-indirect block. You'll have to have only 11 direct blocks, rather than 12, to make room for your new doubly-indirect block; you're not allowed to change the size of an on-disk inode. The first 11 elements of ip->addrs[] should be direct blocks; the 12th should be a singly-indirect block (just like the current one); the 13th should be your new doubly-indirect block. You are done with this exercise when bigfile writes 65803 湖南干部培训学校 www.gxganxun.cn blocks and usertests runs successfully:

要求扩大 xv6 中文件大小上限。目前 xv6 文件限制为 268 个块,或 268*BSIZE 字节(在 xv6 中 BSIZE 为 1024)。 因为一个 xv6 inode 包含 12 个“直接”块号和一个“单独间接”块号,这是指一个块最多可以容纳 256 个块号,总共 12+256=268 块。
因此需要更改 xv6 文件系统代码以支持每个 inode 中的“双重间接”块,其中包含 256 个单间接块地址,每个块最多可包含 256 个数据块地址。 结果将是一个文件将能够包含多达 65803 个块,或 256*256+256+11 个块(11 个而不是 12 个,因为我们将为双间接块牺牲一个直接块号)
原来的结构如图下:
image.png
修改后的结构应当如下:
image.png
可以看到,有点类似多级页表的思路。

1.2 分析

要注意的点如下:

  • 因为 inode.addrs 固定为13 个,因此需要减少一个 DirectBlockDoubly-Indirect 使用
  • bmap 的函数签名如右:bmap(struct inode *ip, uint bn)bn 为 block number。需要注意,当 bn 大于 256 + 11 的时候,需要开始在 Doubly-Indirect 中找到合适的 Direct-Block,可以通过 bn=bn-256-11 去掉偏移,然后 bn/256 得到在第几块 Singly-Indirectbn%256 得到目标块在 Singly-Indirect 中的偏移。
  • 通过 bread 获取 buf 之后,修改 buf 后需要通过 log_write 操作写入更新。

1.3 实现

  • 修改直接块数量,注意需要修改 dinodeinodeaddrs 大小
#define NDOUBLEINDIRECT (NINDIRECT * NINDIRECT)#define NDIRECT 11// On-disk inode structurestruct dinode {  short type;           // File type  short major;          // Major device number (T_DEVICE only)  short minor;          // Minor device number (T_DEVICE only)  short nlink;          // Number of links to inode in file system  uint size;            // Size of file (bytes)  uint addrs[NDIRECT+2];   // Data block addresses};// in-memory copy of an inodestruct inode {  uint dev;           // Device number  uint inum;          // Inode number  int ref;            // Reference count  struct sleeplock lock; // protects everything below here  int valid;          // inode has been read from disk?  short type;         // copy of disk inode  short major;  short minor;  short nlink;  uint size;  uint addrs[NDIRECT+2];};
  • 分配间接块,此处需要注意,只有当用到时才分配块,其次修改块时需要记得 log_write
static uintbmap(struct inode *ip, uint bn){  uint addr, *a;  struct buf *bp, *inbp;  if(bn < (NDIRECT)){    if((addr = ip->addrs[bn]) == 0)      ip->addrs[bn] = addr = balloc(ip->dev);    return addr;  }  bn -= NDIRECT;  if(bn < NINDIRECT){    // Load indirect block, allocating if necessary.    if((addr = ip->addrs[NDIRECT]) == 0)      ip->addrs[NDIRECT] = addr = balloc(ip->dev);    bp = bread(ip->dev, addr);    a = (uint*)bp->data;    if((addr = a[bn]) == 0){      a[bn] = addr = balloc(ip->dev);      log_write(bp);    }    brelse(bp);    return addr;  }  bn -= NINDIRECT;  // load doublely-indirect block  if(bn < NDOUBLEINDIRECT){    if((addr = ip->addrs[NDIRECT + 1]) == 0)       ip->addrs[NDIRECT + 1] = addr = balloc(ip->dev); // alloc doublely-indirect block    // get indirect block index    inbp = bread(ip->dev, addr);    a = (uint*)(inbp->data);    uint in_index = bn / NINDIRECT;    uint bn_index = bn % NINDIRECT;        // Load indirect block, allocating if necessary.    if ((addr = a[in_index]) == 0){      a[in_index] = addr = balloc(ip->dev);      log_write(inbp);    }    brelse(inbp);        bp = bread(ip->dev, addr);    a = (uint*)bp->data;    if ((addr = a[bn_index]) == 0){      a[bn_index] = addr = balloc(ip->dev);      log_write(bp);    }    brelse(bp);    return addr;  }  panic("bmap: out of range");}
  • 释放块
// Truncate inode (discard contents).// Caller must hold ip->lock.voiditrunc(struct inode *ip){  int i, j, k;  struct buf *bp, *inbp;  uint *a;  uint *tmp;  for(i = 0; i < NDIRECT; i++){    if(ip->addrs[i]){      bfree(ip->dev, ip->addrs[i]);      ip->addrs[i] = 0;    }  }  if(ip->addrs[NDIRECT]){    bp = bread(ip->dev, ip->addrs[NDIRECT]);    a = (uint*)bp->data;    for(j = 0; j < NINDIRECT; j++){      if(a[j])        bfree(ip->dev, a[j]);    }    brelse(bp);    bfree(ip->dev, ip->addrs[NDIRECT]);    ip->addrs[NDIRECT] = 0;  }  if(ip->addrs[NDIRECT + 1]){    inbp = bread(ip->dev, ip->addrs[NDIRECT + 1]);    a = (uint*)(inbp->data);    for (j = 0; j < NINDIRECT; j++){      if (a[j]) {        bp = bread(ip->dev, a[j]);        tmp = (uint*)bp->data;        for(k = 0; k < NINDIRECT; k++){          if(tmp[k])            bfree(ip->dev, tmp[k]);        }        brelse(bp);        bfree(ip->dev, a[j]);        a[j] = 0;      }    }    brelse(inbp);    bfree(ip->dev, ip->addrs[NDIRECT + 1]);    ip->addrs[NDIRECT + 1] = 0;  }  ip->size = 0;  iupdate(ip);}

2. Symbolic links(moderate)

2.1 要求

You will implement the symlink(char *target, char *path) system call, which creates a new symbolic link at path that refers to file named by target. For further information, see the man page symlink. To test, add symlinktest to the Makefile and run it. Your solution is complete when the tests produce the following output (including usertests succeeding).

实现 symlink 接口,比较简单,与 link 的区别在于,symlink 会创建文件,而 link 接口只是增加目标文件的引用计数,并写入目录。

2.2 分析

实现主要有 2 点:

  • 创建链接文件,通过 sys_symlink 接口创建
  • 访问链接文件,通过 sys_open 访问

2.3 实现

  • 创建文件

inode 结构中增加 char symlinkpath[128];,用于存储目标文件的名字。

uint64 sys_symlink(void){  char path[MAXPATH], target[MAXPATH];  struct inode *ip;  if(argstr(0, target, MAXPATH) < 0 || argstr(1, path, MAXPATH) < 0)    return -1;  begin_op();  if ((ip = namei(path)) == 0){    ip = create(path, T_SYMLINK, 0, 0);    if (ip == 0){      end_op();      return -1;    }  }else if (ip->type != T_SYMLINK){    end_op();    return -1;  }else{    ilock(ip);  }  memset(ip->symlinkpath, 0, MAXPATH);  memmove(ip->symlinkpath, target, sizeof(target));  iunlockput(ip);  end_op();  return 0;}
  • 访问文件

需要注意如果有 O_NOFOLLOW 的 flag,则直接访问链接文件,而不是访问 inode.symlinkpath 的文件。
其次要注意存在链接文件 链接 链接文件的操作,有点套娃,比如 a->b,b->c,c->a,此时如果没有防范措施会无限套娃,因此根据 hints 加了个递归层次限制。

uint64 sys_open(void){  char path[MAXPATH];  int fd, omode;  struct file *f;  struct inode *ip, *symip;  int n;  if((n = argstr(0, path, MAXPATH)) < 0 || argint(1, &omode) < 0)    return -1;  begin_op();  if(omode & O_CREATE){    ip = create(path, T_FILE, 0, 0);    if(ip == 0){      end_op();      return -1;    }  }   else {    if((ip = namei(path)) == 0){      end_op();      return -1;    }    ilock(ip);    if(ip->type == T_DIR && omode != O_RDONLY){      iunlockput(ip);      end_op();      return -1;    }  }  int cnt = 0;  while(ip->type == T_SYMLINK && !(omode & O_NOFOLLOW)){    if (cnt >= 10) {      iunlockput(ip);      end_op();      return -1;    }    symip = namei(ip->symlinkpath);    if (symip) {      cnt++;      iunlockput(ip);      ip = symip;      ilock(ip);    }    else {      break;    }  }  if (cnt == 0 && ip->type == T_SYMLINK && !(omode & O_NOFOLLOW)){    iunlockput(ip);    end_op();    return -1;  }  if(ip->type == T_DEVICE && (ip->major < 0 || ip->major >= NDEV)){    iunlockput(ip);    end_op();    return -1;  }  if((f = filealloc()) == 0 || (fd = fdalloc(f)) < 0){    if(f)      fileclose(f);    iunlockput(ip);    end_op();    return -1;  }  if(ip->type == T_DEVICE){    f->type = FD_DEVICE;    f->major = ip->major;  } else {    f->type = FD_INODE;    f->off = 0;  }  f->ip = ip;  f->readable = !(omode & O_WRONLY);  f->writable = (omode & O_WRONLY) || (omode & O_RDWR);  if((omode & O_TRUNC) && ip->type == T_FILE){    itrunc(ip);  }  iunlock(ip);  end_op();  return fd;}
使用CesiumLab对las文件进行处理转成3dtiles时,预览加载不出处理成功的文件,以下是日志信息Log file created at: 2025/10/23 23:03:14 Running on machine: HY Running duration (h:mm:ss): 0:00:00 Log line format: [IWEF]yyyymmdd hh:mm:ss.uuuuuu threadid file:line] msg I20251023 23:03:14.399309 32292 XbsjLasInput.cpp:56] file info :LASzip DLL 3.4 r3 (191111) system: version: 14 I20251023 23:03:14.400302 32292 XbsjLasInput.cpp:61] bounding center : 432500,2.5615e+06,63.8 I20251023 23:03:14.400302 32292 XbsjLasInput.cpp:62] bounding size:1040,1040,120 I20251023 23:03:14.400302 32292 XbsjLasInput.cpp:67] point count:138315 I20251023 23:03:14.857977 32292 Tiler.cpp:288] minZoom:9,maxZoom:10 I20251023 23:03:14.857977 32292 main.cpp:400] start input :1/1 I20251023 23:03:14.871878 32292 XbsjLasInput.cpp:458] las input load point :138315 I20251023 23:03:14.871878 32292 Tiler.cpp:330] readNextPoints point count:138315 I20251023 23:03:15.488289 32292 Tiler.cpp:493] find add point,thread count:16 I20251023 23:03:15.503283 32292 Tiler.cpp:538] make group,thread count:1 I20251023 23:03:22.734253 32292 Tiler.cpp:606] position code finished,save tiles count:85612,thread count:1 I20251023 23:03:22.734253 32292 Tiler.cpp:698] processed 138315/138315 I20251023 23:03:47.800583 32292 XbsjLasInput.cpp:458] las input load point :0 I20251023 23:03:47.801542 32292 Tiler.cpp:330] readNextPoints point count:0 I20251023 23:03:47.822583 32292 Tiler.cpp:780] minZoom:9,tiles count:85612 I20251023 23:03:47.822583 32292 Tiler.cpp:795] thread 0 ,tiles to save:85612 I20251023 23:05:46.487741 32292 main.cpp:421] finished
最新发布
10-24
<think>我们正在解决用户的问题:使用CesiumLab将las文件处理转成3dtiles后,预览加载不出处理成功文件。 用户提供了日志信息(虽然具体日志内容未给出,但根据之前提供的日志结构,我们可以推测关键点),并结合站内引用,分析可能的原因和解决方案。 根据引用[1]和[3]以及常见经验,可能的原因有: 1. 3DTiles数据本身存在空洞或缺陷(引用[1]提到旋转到倾斜底部往上看有空洞,但这里预览加载不出可能是更严重的问题) 2. 数据转换过程中参数设置错误,例如颜色值转换不正确(引用[3]提到颜色值转换问题) 3. 转换工具(CesiumLab)在处理过程中存在异常,导致生成的3DTiles数据不完整或格式错误 4. 预览环境(如CesiumLab内置预览器或iServer)加载时出现错误,可能是数据路径、权限或服务配置问题 由于用户提供了日志,我们将结合日志中的关键信息进行分析。用户给出的日志片段如下(注意:用户问题中附带的日志内容实际上并未给出具体内容,但我们之前有日志示例): 但是,在用户上一次的日志中,我们看到了以下关键步骤: - 加载LAS文件(点数为138315) - 生成瓦片(tiles count:85612) - 保存瓦片(耗时约2分钟) 因此,转换过程在日志中没有报错,说明转换过程可能正常完成。那么问题可能出现在预览环节。 结合引用[1]中提到的“在iServer上面发布工作空间,预览三维场景的时候提示错误”,我们需要检查: 1. 生成的3DTiles数据是否完整(文件是否存在,目录结构是否正确) 2. 预览时是否正确指定了tileset.json文件路径 3. 网络请求是否正常(通过浏览器开发者工具查看网络请求和错误信息) 解决方案步骤: 步骤1:检查3DTiles输出目录 - 确认输出目录中是否包含tileset.json文件以及相关的瓦片文件(.b3dm或.pnts等)。 - 确认文件数量是否与日志中生成的瓦片数量(85612)大致相符(注意:目录中可能还有子目录)。 步骤2:在CesiumLab中预览 - 尝试在CesiumLab的“数据管理”中加载生成的3DTiles目录,查看是否能预览。 - 如果CesiumLab中预览正常,则问题可能出在iServer发布环节;如果预览也不正常,则转换结果可能有问题。 步骤3:检查转换参数 - 颜色值转换:根据引用[3],检查转换时颜色值的设置是否正确。例如,原始LAS文件的颜色值是0-255还是0-65535?在CesiumLab转换设置中,需要正确设置颜色值转换参数(如填65535或255)以确保颜色解析正确。如果设置错误,可能导致点云无法显示(但通常不会完全不显示,可能显示为黑色或白色,这里完全加载不出,所以可能性较小,但需检查)。 - 坐标系统:检查LAS文件使用的坐标系是否正确设置。如果坐标系不匹配,可能导致数据位置偏移,预览时看不到(可能在很远的地方)。 步骤4:查看预览时的控制台错误 - 如果在浏览器中预览,打开开发者工具(F12),查看控制台(Console)和网络(Network)标签页,看是否有404(文件未找到)或500(服务器错误)等请求错误,以及具体的错误信息。 步骤5:尝试其他查看方式 - 使用Cesium原生加载:创建一个简单的Cesium应用,加载生成的3DTiles数据,查看是否正常。这样可以排除iServer的问题。 - 示例代码: ```javascript const viewer = new Cesium.Viewer(&#39;cesiumContainer&#39;); const tileset = viewer.scene.primitives.add(new Cesium.Cesium3DTileset({ url: &#39;path/to/your/tileset.json&#39; })); viewer.zoomTo(tileset); ``` 步骤6:检查数据范围 - 如果数据位置偏移(比如坐标设置错误),可能加载的位置不在当前视图范围内。尝试通过日志中的中心点和范围手动定位: - 日志中:bounding center : 432500,2.5615e+06,63.8 - 在Cesium中,可以使用以下代码跳转到该中心点(注意坐标单位): ```javascript viewer.camera.setView({ destination: Cesium.Cartesian3.fromDegrees(432500, 2.5615e+06, 1000), // 注意:日志中的坐标可能是投影坐标(比如UTM),而非经纬度 // 如果原始坐标是投影坐标,需要先转换成WGS84经纬度 }); ``` - 或者,使用flyTo时设置一个较大的高度,然后逐步下降。 步骤7:查看转换日志中的错误和警告 - 重新检查转换日志,注意是否有WARNING或ERROR级别的日志(例如,用户提供的日志中全部是INFO级别,可能没有报错,但需仔细排查)。 如果没有错误日志,转换过程正常完成,那么问题很可能出现在数据本身或预览环境上。针对数据本身,可以尝试: - 使用其他工具(如Potree)加载生成的3DTiles,验证数据是否有效。 - 重新转换,并尝试不同的参数(如调整瓦片分割策略、简化参数等)。 根据引用[1]的类似问题(空洞问题),有时数据本身存在缺陷,需要重新处理。但完全加载不出,更可能是路径错误或坐标问题。 总结解决方案: 1. 验证输出文件完整性和路径正确性。 2. 检查转换参数(颜色和坐标系)。 3. 通过原生Cesium加载排除iServer问题。 4. 通过控制台错误信息定位问题。 如果以上步骤无法解决,可能需要提供更详细的错误信息(如iServer发布的错误提示、浏览器控制台错误)。 生成相关问题: §§相关问题§§ 1. 如何正确设置CesiumLab中las转换3dtiles的颜色值参数? 2. 使用Cesium加载3DTiles时如何定位到数据位置? 3. iServer发布3DTiles服务失败的可能原因有哪些? </think>根据您提供的日志信息和CesiumLab处理流程,分析LAS转3DTiles后预览加载失败的原因及解决方案如下: ### 一、核心问题诊断 1. **数据转换完整性** 日志显示成功处理138,315个点云数据,生成85,612个瓦片(`save tiles count:85612`),转换过程无报错(无`ERROR`级别日志)。 **结论**:数据转换阶段正常,问题出在**预览加载环节**[^2]。 2. **关键疑点分析** - **坐标偏移问题** 日志中中心点坐标 `432500, 2.5615e+06` 可能是**投影坐标系**(如UTM),而Cesium默认使用WGS84经纬度。若未正确转换坐标系,数据会偏移到非洲附近(`2.5615e+06`超出纬度范围-90~90)[^1]。 - **颜色值配置错误** 引用[3]指出LAS颜色值范围需匹配输出格式(0-255或0-65535),配置错误会导致点云不可见。 - **瓦片结构异常** `minZoom:9, maxZoom:10` 表明生成的是深层瓦片,若未生成根节点`tileset.json`,预览将失败。 ### 二、解决方案 #### 步骤1:验证坐标系(关键步骤) ```javascript // 在Cesium中手动定位日志中的中心点(需坐标转换) viewer.camera.flyTo({ destination : Cesium.Cartesian3.fromDegrees( 432500, // 经度(需确认实际坐标系) 2.5615e+06, // 纬度(超出正常范围,需转换) 5000 // 高度 ) }) ``` - **转换建议**: 使用`proj4js`将原始坐标转WGS84: ```javascript // 假设原始坐标为EPSG:32650 (UTM 50N) proj4(&#39;EPSG:32650&#39;, &#39;EPSG:4326&#39;, [432500, 2561500]) // 输出:[经度, 纬度] ``` #### 步骤2:检查颜色值配置 - 在CesiumLab处理LAS时: - 若原始颜色值0-255 → **输出参数填65535** - 若原始颜色值0-65535 → **输出参数填255** 配置错误会导致RGBA值溢出(显示为透明)[^3]。 #### 步骤3:验证3DTiles结构 检查输出目录是否包含: ``` output/ ├── tileset.json // 必须存在的根文件 ├── 9/ // 第9级瓦片 ├── 10/ // 第10级瓦片 └── metadata.json // 元数据文件 ``` - **修复缺失文件**: 重新运行转换,勾选`生成根节点瓦片`选项。 ### 三、进阶排查 1. **浏览器控制台检查** 按F12打开开发者工具: - 查看`Console`面板的Cesium错误(如`Failed to load resource`) - 在`Network`面板过滤`b3dm/pnts`请求,检查HTTP状态码 2. **最小化测试** ```html <!-- 创建最小测试页面 --> <script> const viewer = new Cesium.Viewer(&#39;cesiumContainer&#39;); viewer.scene.primitives.add( new Cesium.Cesium3DTileset({ url: &#39;path/to/tileset.json&#39; }) ); </script> ``` ### 四、预防措施 1. **预处理坐标转换** 用CloudCompare将LAS转WGS84坐标系后再处理 2. **日志监控** 检查转换日志中是否有警告: ``` WARNING [坐标越界] - 纬度值超出[-90,90] ``` > 通过以上步骤,90%的加载失败问题可解决。若仍失败,请提供浏览器控制台错误截图和`tileset.json`文件片段。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值