# MIT 6.S081 实战记录:从实现 xv6 `find` 到解决 WSL 推送 GitHub
发布时间:2026/10/29 11:13:54 锦皓数字建站

MIT 6.S081 实战记录从实现 xv6find到解决 WSL 推送 GitHub也是终于准备把这个MIT搞完之后会持续更新然后搞自己喜欢的研究方向去了https://github.com/onlybestzhao/MIT_6.S081_LABS本文记录一次 MIT 6.S081 xv6 实验中的真实排错过程主要包含两部分在 xv6 中实现递归查找文件在 WSL 中解决 GitHubpush失败并写一个自动提交脚本。一、实验目标实现 xv6 版find希望在 xv6 shell 中执行find.b表示从当前目录开始递归查找名字为b的文件期望输出类似./b ./a/b这里两个参数的含义不同argv[1] .: 搜索起点 argv[2] b: 要查找的名字递归过程中搜索起点会变化find(., b) find(./a, b) find(./a/c, b)但目标名字始终不变。二、先理解 xv6 的目录结构xv6 中目录本质上也是一种可以读取的文件。目录中的每一项可以用struct dirent表示structdirent{ushort inum;charname[DIRSIZ];};两个字段的含义是inuminode 编号name目录项名称。读取目录时典型代码是structdirentde;while(read(fd,de,sizeof(de))sizeof(de)){if(de.inum0)continue;}inum 0表示这个目录槽位没有有效目录项需要跳过。当前目录项的名字在de.name文件类型则需要通过stat或fstat获取例如structstatst;stat(path,st);st.type用来判断路径对应的是目录、普通文件还是设备文件。三、第一次遇到的问题argv不是只有用户输入的参数执行find.b程序实际收到的参数大致是argc 3 argv[0] find argv[1] . argv[2] b argv[3] 0因此main必须按照两个参数接收intmain(intargc,char*argv[])不能把它写成intmain(char*argv[])否则程序会把本来表示参数个数的整数误当成指针访问argv[0]时就可能产生非法内存访问。同时不能把整个argv和某个单独的参数混为一谈argv// 参数数组argv[1]// 一个字符串.argv[2]// 一个字符串b递归函数更适合表达为find(current_path,target_name);而不是让递归函数每次重新解释整个argv。四、第二个问题if后面多写一个分号曾经出现过这样的代码if(de.inum0);continue;在 C 语言中分号本身就是一条空语句。上面的代码等价于if(de.inum0){// 什么也不做}continue;于是无论inum是否为零都会执行continue后面的路径拼接代码永远到不了。正确的写法是if(de.inum0)continue;或者使用大括号if(de.inum0){continue;}continue的作用是跳过当前这一轮循环直接进入下一轮break才是结束整个循环。五、第三个问题路径拼接不是给指针赋值ls.c中常见的路径拼接代码如下strcpy(buf,path);pbufstrlen(buf);*p/;memmove(p,de.name,DIRSIZ);p[DIRSIZ]0;假设path . de.name b执行过程是strcpy(buf, path) - . p buf strlen(buf) - p 指向字符串结尾 *p / - ./ memmove(p, de.name, ...) - ./b... p[DIRSIZ] 0 - ./b这里p和buf指向同一块字符数组的不同位置。通过p写入字符就是在修改buf数组内部的内容所以不需要写buf./b;数组名不能这样重新赋值。p只是一个游标负责指出当前要写入的位置。如果错误地写成pbuf;*p/;就会覆盖buf的第一个字符而不是在字符串末尾追加/。六、第四个问题de.name ! target不是字符串比较下面的代码比较的是两个地址if(de.name!target)即使两个字符串内容都是b它们也可能位于不同的内存地址。比较字符串内容必须使用strcmp(de.name,target)0因此st.typeT_FILEstrcmp(de.name,target)0才表示“当前目录项是普通文件并且名字匹配目标”。七、第五个问题递归调用不能共享会变化的状态递归函数中的这些变量都属于一次调用的状态intfd;charbuf[512];char*p;structdirentde;structstatst;如果把它们定义在函数外所有递归层都会共享同一份变量。子目录递归时会覆盖父目录的路径、文件描述符和目录项状态。另外即使写成staticcharbuf[512];只要它是函数内的static变量所有递归调用仍然共享这一份数组。static不是“每次函数调用创建一份新变量”的意思。递归函数中的局部变量应该是普通局部变量voidfind(char*path,char*target){intfd;charbuf[512];char*p;structdirentde;structstatst;}这样每次递归调用都有自己的变量。八、第六个问题DIRSIZ不是完整路径长度DIRSIZ限制的是单个目录项名字的长度charname[DIRSIZ];它不代表完整路径的长度。完整路径可能是./b ./a/b ./a/c/d/b因此下面的数组不适合作为递归路径缓冲区charbuf[DIRSIZ1];即使单个文件名不超过 14 个字符完整路径仍然可能超过 15 个字节。路径缓冲区应该参考 xv6 的路径限制分配足够空间并在拼接前检查剩余容量if(strlen(path)1DIRSIZ1sizeof(buf)){fprintf(2,find: path too long\n);return;}数组越界会覆盖栈或全局区域中的其他数据造成路径变成/b递归结果丢失加一条printf后行为改变创建文件顺序变化后结果变化。这类现象不是printf产生了逻辑而是程序已经存在未定义行为。九、递归查找的正确思路递归函数可以抽象成find(current_path, target_name)每层执行打开当前路径读取目录项跳过无效目录项跳过.和..拼出子路径用stat判断子路径类型如果是目录递归进入如果是普通文件且名字匹配输出完整路径。伪代码如下find(path, target): 打开 path 读取 path 中的每个目录项 de 如果 de 无效跳过 如果 de.name 是 . 或 ..跳过 child path / de.name 获取 child 的类型 如果 child 是目录 find(child, target) 否则如果 child 是普通文件且 de.name target 输出 child这个过程中current_path 会变化 target_name 始终不变十、第二部分WSL 中git push失败完成 xv6 代码后还遇到了 GitHub 推送问题。典型报错是Failed to connect to github.com port 443排查后发现github.com - 20.205.243.166这个地址当时无法连接但其他 GitHub 地址可以连接。所以问题分成两层GitHub 认证是否成功WSL 到 GitHub 的 HTTPS 网络是否可达。认证成功并不代表网络线路一定正常网络可达也不代表 GitHub 账号已经认证。十一、GitHub CLI 认证在 WSL 中执行gh auth login交互选择GitHub.com HTTPS Yes Login with a web browser登录后检查gh auth statusGit 应该使用 GitHub CLI 提供的凭据助手。十二、自动选择可达的 GitHub IP为了避免每次手动测试 IP编写了github_push.py。脚本的核心做法是通过socket.getaddrinfo获取github.com的 IPv4 地址对每个地址建立 TCP 连接在 443 端口上执行 TLS 握手使用github.com作为 TLS 的server_hostname找到可用地址后仅为本次git push添加http.curloptResolvegithub.com:443:可用IP这样不会修改远程 URL也不会永久修改系统 DNS。脚本顶部只需要修改ADD_PATHS[GITHUB_PUSH_WSL.md,github_push.py,]COMMIT_MESSAGEAdd documentation and push helperPUSH_REMOTEoriginPUSH_BRANCHlab1运行python3 github_push.py脚本依次执行自动寻找可达 GitHub 地址 git add git status git commit git push关键代码如下deffind_reachable_github_ip()-str:contextssl.create_default_context()foraddressingithub_candidates():try:withsocket.create_connection((address,443),timeout5)asconnection:withcontext.wrap_socket(connection,server_hostnamegithub.com,):returnaddressexcept(OSError,ssl.SSLError):continueraiseSystemExit(No reachable GitHub HTTPS address found.)这里不能直接对 IP 执行普通 HTTPS 请求因为 GitHub 依赖域名和 TLS SNI。使用server_hostnamegithub.com可以保证 TLS 握手仍然针对正确的域名。十三、脚本的边界这个脚本可以自动处理DNS 返回多个 IPv4 地址某些 GitHub IP 443 端口不可达备用 IP 需要轮换GitHub CLI 已经配置好认证。但它不能保证本机完全断网时仍然能 pushGitHub 账号没有权限时仍然能 push没有登录 GitHub 时自动完成认证所有网络阻断都能通过换 IP 解决。如果所有候选地址都失败应检查curl-4-I--connect-timeout10https://github.com gh auth statusgitremote-v十四、最终经验这次实验最重要的收获不是某一行代码而是排查问题的顺序先确认输入参数 再确认指针和字符串 再确认路径拼接 再确认递归状态 最后检查内存边界Git 推送问题也要分层排查认证问题 网络问题 DNS 问题 Git 配置问题 仓库权限问题
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。