当前位置: 首页 > news >正文

Golang os.Rename invalid cross-device link的原因

文章目录

    • 背景
      • 运行环境
    • 文件系统对比
      • linux下的文件系统
      • mac下的文件系统
      • linux下的mv指令
    • golang的os.Rename源码
      • os.Rename
      • syscall.Rename
      • syscall.Renameat
      • SYS_RENAMEAT是什么
    • 查看系统调用函数文档
      • 什么是man page
      • man page的用法
        • user commands
        • system calls
      • renameat不支持跨挂载点调用
      • strace确定程序调用了renameat
    • 怎么避免错误
    • 总结

背景

在执行go程序的时候,其中有一步是把/tmp目录下的一个文件移动到用户目录下,使用go的os.Rename函数来实现。经测试在mac上是可以正常跑的,但是在linux机器上却报错了。报错如下:

go run ./script/download-go go1.22.3
download from https://go.dev/dl/go1.22.3.linux-amd64.tar.gz% Total    % Received % Xferd  Average Speed   Time    Time     Time  CurrentDload  Upload   Total   Spent    Left  Speed
100    75  100    75    0     0     53      0  0:00:01  0:00:01 --:--:--    53
100 65.7M  100 65.7M    0     0   160k      0  0:07:00  0:07:00 --:--:--  419k
rename /tmp/go2503368948/go go-release/go1.22.3: invalid cross-device link
exit status 1

错误信息 invalid cross-device link 表明 /tmp 目录和 go-release 目录是在不同的文件系统中。也就是说不能把一个文件从一个文件系统移动(或重命名)到另一个文件系统。
os.Rename算是比较常用的文件操作函数,博主一直把os.Rename当作mv在使用,也一直没有遇到过这个问题,还是挺奇怪的,值得探索一下出错的原因。

运行环境

操作系统: linux_x86_64
CPU架构: amd_64
Golang: 1.22.2

文件系统对比

linux下的文件系统

dfFilesystem                                       Size  Used Avail Use% Mounted on
dev                                              7.6G     0  7.6G   0% /dev
run                                              7.7G  3.2M  7.7G   1% /run
/dev/nvme0n1p2                                   916G  299G  571G  35% /
tmpfs                                            7.7G   50M  7.6G   1% /dev/shm
tmpfs                                            7.7G   24K  7.7G   1% /tmp

mac下的文件系统

df
Filesystem     512-blocks      Used Available Capacity iused      ifree %iused  Mounted on
/dev/disk3s3s1  965595304  28934208 217744920    12%  387452 1088724600    0%   /
devfs                 400       400         0   100%     692          0  100%   /dev
/dev/disk3s6    965595304        40 217744920     1%       0 1088724600    0%   /System/Volumes/VM
/dev/disk3s4    965595304  22879080 217744920    10%    1372 1088724600    0%   /System/Volumes/Preboot

可以看到mac系统上,/根目录都对应同一个挂载点和同一个文件系统。而在我的linux开发机上/tmp是另一个挂载点和文件系统,根据报错信息来看,也是符合预期的。

linux下的mv指令

测试从/tmp目录移动文件到用户目录下,结果是可行的,并没有报错。

golang的os.Rename源码

直接去源码里面找代码看下,源码目录为GOROOT目录下面的src目录。因为没有配置vim环境,所以只能通过rg的方式来找了。

os.Rename

rg "Rename"// file_unix.go
return syscall.Rename(oldname, newname)

syscall.Rename

// cd syscall
// rg "Raname"// syscall_linux.go文件unc Rename(oldpath string, newpath string) (err error) {return Renameat(_AT_FDCWD, oldpath, _AT_FDCWD, newpath)

syscall.Renameat

// zsyscall_linux_amd64.go 文件func Renameat(olddirfd int, oldpath string, newdirfd int, newpath string) (err error) {var _p0 *byte_p0, err = BytePtrFromString(oldpath)if err != nil {return}var _p1 *byte_p1, err = BytePtrFromString(newpath)if err != nil {return}// 调用的是SYS_RENAMEAT这个系统调用_, _, e1 := Syscall6(SYS_RENAMEAT, uintptr(olddirfd), uintptr(unsafe.Pointer(_p0)), uintptr(newdirfd), uintptr(unsafe.Pointer(_p1)), 0, 0)if e1 != 0 {err = errnoErr(e1)}return}// zsysnum_linux_amd64.go// 对应的系统调用编号是264SYS_RENAMEAT               = 264

SYS_RENAMEAT是什么

对于linux操作系统,每个系统调用在系统调用表中都有一个唯一的编号。这个编号就是系统调用的标识,当用户的程序想要进行系统调用时,会使用这个编号对系统调用进行引用。用户的程序只能通过这个编号与系统调用交互,及进行相关的读、写、打开文件或者申请内存等操作。
而这里的SYS_RENAMEAT对应的就是系统调用的编号,可以搜索: **Linux System Call Table **来查看不同CPU架构对应的系统调用编号。

参考:https://www.chromium.org/chromium-os/developer-library/reference/linux-constants/syscalls/
x86_64
image.png

查看系统调用函数文档

man page文档

什么是man page

简单来说就是linux系统的API文档介绍,主要介绍系统提供的命令含义及用法。但是系统文档相对来说还是比较长的,因此著名的开源项目TLDR(https://github.com/tldr-pages/tldr) 也是由此而来,旨在简化man page带来的长文本负担。
TL;DR 代表“太长;没有读”。它起源于互联网俚语,用于表示长文本(或其中的一部分)因太长而被跳过。

man page的用法

用法介绍网上一大堆,这里只聚焦我们的问题,怎么查看系统调用函数的介绍。首先man page对这些系统函数是做了分区的,如下:
image.png

user commands

就是常用的命令行函数都在这里,例如ls。左上角的LS(1)就代表在分区1

// man 1 ls
LS(1)                                                                    User Commands                                                                    LS(1)NAMEls - list directory contentsSYNOPSISls [OPTION]... [FILE]...DESCRIPTIONList information about the FILEs (the current directory by default).  Sort entries alphabetically if none of -cftuvSUX nor --sort is specified.Mandatory arguments to long options are mandatory for short options too.-a, --alldo not ignore entries starting with .-A, --almost-alldo not list implied . and ..

system calls

系统调用函数,这里拿renameat举例子,左上角的rename(2)代表的就是系统调用函数。

man 2 renamertrename(2)                                                             System Calls Manual                                                             rename(2)NAMErename, renameat, renameat2 - change the name or location of a fileLIBRARYStandard C library (libc, -lc)SYNOPSIS#include <stdio.h>int rename(const char *oldpath, const char *newpath);#include <fcntl.h>           /* Definition of AT_* constants */#include <stdio.h>int renameat(int olddirfd, const char *oldpath,int newdirfd, const char *newpath);int renameat2(int olddirfd, const char *oldpath,int newdirfd, const char *newpath, unsigned int flags);Feature Test Macro Requirements for glibc (see feature_test_macros(7)):renameat():Since glibc 2.10:_POSIX_C_SOURCE >= 200809LBefore glibc 2.10:_ATFILE_SOURCErenameat2():_GNU_SOURCE

renameat不支持跨挂载点调用

EXDEV  oldpath and newpath are not on the same mounted filesystem.  (Linux permits a filesystem to be mounted at multiple points, but rename() does  notwork across different mount points, even if the same filesystem is mounted on both.)

这里已经明确说了,不允许跨挂载点的调用,哪怕是同一个文件系统也不行。

strace确定程序调用了renameat

// 只显示 renameat 的调用情况
strace -f -e trace=renameat go run ./script/download-go go1.22.3// 结果
[pid 92270] renameat(AT_FDCWD, "/tmp/go734096947/go", AT_FDCWD, 
"go-release/go1.22.3") = -1 EXDEV (Invalid cross-device link)

结合上面的系统调用函数分析,已经明确了根因。

怎么避免错误

参考:
golang社区关于os.Rename的讨论
开源社区的方案
csdn上的避免方案
目前os包没有直接提供类似于mv的函数,常规解决方案就是先copy,再rename,这样就能避免跨挂载点工作导致的错误。

总结

本来只是个小问题,知道报错的含义之后,很容易就会想到避免错误的方案。但是寻根问底也是工程师的天性,知其然更要知其所以然。
哀吾生之须臾,羡知识之无穷。

end

相关文章:

Golang os.Rename invalid cross-device link的原因

文章目录 背景运行环境 文件系统对比linux下的文件系统mac下的文件系统linux下的mv指令 golang的os.Rename源码os.Renamesyscall.Renamesyscall.RenameatSYS_RENAMEAT是什么 查看系统调用函数文档什么是man pageman page的用法user commandssystem calls renameat不支持跨挂载点…...

Flutter 中的 Badge 小部件:全面指南

Flutter 中的 Badge 小部件&#xff1a;全面指南 在移动应用设计中&#xff0c;徽章&#xff08;Badge&#xff09;是一种常见的UI元素&#xff0c;用于吸引用户注意并展示重要信息&#xff0c;如未读消息数量、新通知等。Flutter 通过各种第三方包提供了徽章小部件&#xff0…...

Java 多线程抢红包

问题需求 一个人在群里发了1个100元的红包&#xff0c;被分成了8个&#xff0c;群里有10个人一起来抢红包&#xff0c;有抢到的金额随机分配。 红包功能需要满足哪些具体规则呢? 1、被分的人数抢到的金额之和要等于红包金额&#xff0c;不能多也不能少。 2、每个人至少抢到1元…...

【PB案例学习笔记】-08 控件拖动实现

写在前面 这是PB案例学习笔记系列文章的第8篇&#xff0c;该系列文章适合具有一定PB基础的读者。 通过一个个由浅入深的编程实战案例学习&#xff0c;提高编程技巧&#xff0c;以保证小伙伴们能应付公司的各种开发需求。 文章中设计到的源码&#xff0c;小凡都上传到了gitee…...

读书笔记整理

1.对天才而言&#xff0c;任何努力都如做游戏般容易和有趣&#xff0c;兴趣是发展的原动力。从这个角度来看&#xff0c;通过普通人的劳动是无法创造天才的。 2.让孩子理解语法是很困难的。苦背不如练才是行之有效的办法。孩子们永远是故事迷&#xff0c;在教孩子们外语时&…...

uniapp蓝牙打印图片

前言 这是个蓝牙打印图片的功能&#xff0c;业务是打印界面固定的demo范围&#xff0c;这里通过html2canvas插件生成的图片base64&#xff0c;然后图片base64绘制到canvas中去后&#xff0c;获取canvas中的像素信息&#xff0c;然后对像素信息进行一个灰度值处理&#xff0c;灰…...

Ajax用法总结(包括原生Ajax、Jquery、Axois)

HTTP知识 HTTP&#xff08;hypertext transport protocol&#xff09;协议『超文本传输协议』&#xff0c;协议详细规定了浏览器和万维网服务器之间互相通信的规则。 请求报文 请求行: GET、POST /s?ieutf-8...&#xff08;url的一长串参数&#xff09; HTTP/1.1 请求头…...

LeetCode 题解:112. 路径总和,递归,JavaScript,详细注释

原题链接&#xff1a; 112. 路径总和 解题思路&#xff1a; 如果求根节点到叶子节点的路径上的节点值之和&#xff0c;假设共有3个节点&#xff0c;那么写成计算式是val1 val2 val3 sum那么将计算式转换就可以得到val3 sum - val1 - val2也就是说&#xff0c;问题可以从…...

Spring (15)Spring Boot的自动配置是如何工作的

Spring Boot的自动配置是通过条件注解&#xff08;如ConditionalOnClass&#xff0c;ConditionalOnBean等&#xff09;和EnableAutoConfiguration注解来实现的。这一机制主要是为了简化配置过程&#xff0c;让开发者在满足特定条件时自动配置Spring应用。接下来&#xff0c;我们…...

【机器学习】—机器学习和NLP预训练模型探索之旅

目录 一.预训练模型的基本概念 1.BERT模型 2 .GPT模型 二、预训练模型的应用 1.文本分类 使用BERT进行文本分类 2. 问答系统 使用BERT进行问答 三、预训练模型的优化 1.模型压缩 1.1 剪枝 权重剪枝 2.模型量化 2.1 定点量化 使用PyTorch进行定点量化 3. 知识蒸馏…...

54. UE5 RPG 增加伤害类型

在正常的RPG游戏中&#xff0c;都存在一个类别就是属性伤害&#xff0c;比如&#xff0c;在一个游戏里面有一个火属性的技能&#xff0c;它造成的伤害就是火属性类型的&#xff0c;并且它还有可能有附加伤害&#xff0c;比如给予目标一个灼烧效果&#xff0c;每秒造成多少的火属…...

llama3 微调教程之 llama factory 的 安装部署与模型微调过程,模型量化和gguf转换。

本文记录了从环境部署到微调模型、效果测试的全过程&#xff0c;以及遇到几个常见问题的解决办法&#xff0c;亲测可用&#xff08;The installed version of bitsandbytes was compiled without GPU support. NotImplementedError: Architecture ‘LlamaForCausalLM’ not sup…...

C++三剑客之std::any(二) : 源码剖析

目录 1.引言 2.std::any的存储分析 3._Any_big_RTTI与_Any_small_RTTI 4.std::any的构造函数 4.1.从std::any构造 4.2.可变参数模板构造函数 4.3.赋值构造与emplace函数 5.reset函数 6._Cast函数 7.make_any模版函数 8.std::any_cast函数 9.总结 1.引言 C三剑客之s…...

【C语言】8.C语言操作符详解(2)

文章目录 6.单⽬操作符7.逗号表达式8.下标访问[]、函数调⽤()8.1 [ ] 下标引⽤操作符8.2 函数调⽤操作符 9.结构成员访问操作符9.1 结构体9.1.1 结构的声明9.1.2 结构体变量的定义和初始化 9.2 结构成员访问操作符9.2.1 结构体成员的直接访问9.2.2 结构体成员的间接访问 6.单⽬…...

vivado 物理约束KEEP_HIERARCHY

KEEP_HIERARCHY Applied To Cells Constraint Values • TRUE • FALSE • YES • NO UCF Example INST u1 KEEP_HIERARCHY TRUE; XDC Example set_property DONT_TOUCH true [get_cells u1] IOB Applied To Cells Constraint Values IOB_XnYn UCF Examp…...

Linux 三十六章

​​​​​​​ &#x1f436;博主主页&#xff1a;ᰔᩚ. 一怀明月ꦿ ❤️‍&#x1f525;专栏系列&#xff1a;线性代数&#xff0c;C初学者入门训练&#xff0c;题解C&#xff0c;C的使用文章&#xff0c;「初学」C&#xff0c;linux &#x1f525;座右铭&#xff1a;“不要…...

ntsd用法+安装包

ntsd是一个强大的进程终止软件&#xff0c;除了少数系统进程之外一律杀掉 用法 1.ntsd -c q -p 进程的pid 2.ntsd -c q -pn 进程名 记得解压到System32里面 当然&#xff0c;资源管理器的进程是可以杀的所以也可以让电脑黑屏 同样可以让电脑黑屏的还有taskkill /f /im 进程…...

Nacos 微服务管理

Nacos 本教程将为您提供Nacos的基本介绍&#xff0c;并带您完成Nacos的安装、服务注册与发现、配置管理等功能。在这个过程中&#xff0c;您将学到如何使用Nacos进行微服务管理。下方是官方文档&#xff1a; Nacos官方文档 1. Nacos 简介 Nacos&#xff08;Naming and Confi…...

Kubernetes集群上的Etcd备份和恢复

在本教程中&#xff0c;您将学习如何在Kubernetes集群上使用etcd快照进行etcd备份和恢复。 在Kubernetes架构中&#xff0c;etcd是集群的重要组成部分。所有集群对象及其状态都存储在etcd中。为了更好地理解Kubernetes&#xff0c;有几点关于etcd的信息是您需要了解的。 它是…...

创建型模式 (Python版)

单例模式 懒汉式 class SingleTon:# 类属性_obj None # 用来存储对象# 创造对象def __new__(cls, *args, **kwargs):# 如果对象不存在&#xff0c;就创造一个对象if cls._obj is None:cls._obj super().__new__(cls, *args, *kwargs)# 返回对象return cls._objif __name__…...

深入剖析AI大模型:大模型时代的 Prompt 工程全解析

今天聊的内容&#xff0c;我认为是AI开发里面非常重要的内容。它在AI开发里无处不在&#xff0c;当你对 AI 助手说 "用李白的风格写一首关于人工智能的诗"&#xff0c;或者让翻译模型 "将这段合同翻译成商务日语" 时&#xff0c;输入的这句话就是 Prompt。…...

蓝牙 BLE 扫描面试题大全(2):进阶面试题与实战演练

前文覆盖了 BLE 扫描的基础概念与经典问题蓝牙 BLE 扫描面试题大全(1)&#xff1a;从基础到实战的深度解析-CSDN博客&#xff0c;但实际面试中&#xff0c;企业更关注候选人对复杂场景的应对能力&#xff08;如多设备并发扫描、低功耗与高发现率的平衡&#xff09;和前沿技术的…...

Frozen-Flask :将 Flask 应用“冻结”为静态文件

Frozen-Flask 是一个用于将 Flask 应用“冻结”为静态文件的 Python 扩展。它的核心用途是&#xff1a;将一个 Flask Web 应用生成成纯静态 HTML 文件&#xff0c;从而可以部署到静态网站托管服务上&#xff0c;如 GitHub Pages、Netlify 或任何支持静态文件的网站服务器。 &am…...

NFT模式:数字资产确权与链游经济系统构建

NFT模式&#xff1a;数字资产确权与链游经济系统构建 ——从技术架构到可持续生态的范式革命 一、确权技术革新&#xff1a;构建可信数字资产基石 1. 区块链底层架构的进化 跨链互操作协议&#xff1a;基于LayerZero协议实现以太坊、Solana等公链资产互通&#xff0c;通过零知…...

JVM暂停(Stop-The-World,STW)的原因分类及对应排查方案

JVM暂停(Stop-The-World,STW)的完整原因分类及对应排查方案,结合JVM运行机制和常见故障场景整理而成: 一、GC相关暂停​​ 1. ​​安全点(Safepoint)阻塞​​ ​​现象​​:JVM暂停但无GC日志,日志显示No GCs detected。​​原因​​:JVM等待所有线程进入安全点(如…...

JVM 内存结构 详解

内存结构 运行时数据区&#xff1a; Java虚拟机在运行Java程序过程中管理的内存区域。 程序计数器&#xff1a; ​ 线程私有&#xff0c;程序控制流的指示器&#xff0c;分支、循环、跳转、异常处理、线程恢复等基础功能都依赖这个计数器完成。 ​ 每个线程都有一个程序计数…...

08. C#入门系列【类的基本概念】:开启编程世界的奇妙冒险

C#入门系列【类的基本概念】&#xff1a;开启编程世界的奇妙冒险 嘿&#xff0c;各位编程小白探险家&#xff01;欢迎来到 C# 的奇幻大陆&#xff01;今天咱们要深入探索这片大陆上至关重要的 “建筑”—— 类&#xff01;别害怕&#xff0c;跟着我&#xff0c;保准让你轻松搞…...

git: early EOF

macOS报错&#xff1a; Initialized empty Git repository in /usr/local/Homebrew/Library/Taps/homebrew/homebrew-core/.git/ remote: Enumerating objects: 2691797, done. remote: Counting objects: 100% (1760/1760), done. remote: Compressing objects: 100% (636/636…...

规则与人性的天平——由高考迟到事件引发的思考

当那位身着校服的考生在考场关闭1分钟后狂奔而至&#xff0c;他涨红的脸上写满绝望。铁门内秒针划过的弧度&#xff0c;成为改变人生的残酷抛物线。家长声嘶力竭的哀求与考务人员机械的"这是规定"&#xff0c;构成当代中国教育最尖锐的隐喻。 一、刚性规则的必要性 …...

GraphRAG优化新思路-开源的ROGRAG框架

目前的如微软开源的GraphRAG的工作流程都较为复杂&#xff0c;难以孤立地评估各个组件的贡献&#xff0c;传统的检索方法在处理复杂推理任务时可能不够有效&#xff0c;特别是在需要理解实体间关系或多跳知识的情况下。先说结论&#xff0c;看完后感觉这个框架性能上不会比Grap…...