栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

从linux 源码角度去解决一个hard link 问题

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

从linux 源码角度去解决一个hard link 问题

比较晚了,大概2个月没写c++了吧,最近一直又在写golang了,但是如果你要问我最喜欢的语言排行,那这个顺序肯定是

c++ > php > golang > python

php尽管经常被黑,但是自己每个周吧,都会用php写写网站后台;毕竟工作久了,你会发现语言根本无所谓的,只是一个工具。

技术也许真的只是个青春饭,但是当下还是在做;再就是兴趣,我从初中就讨厌语文,英语,喜欢数学;数学就是这样1+1 = 2;结果总是固定的,不像语文黑的说成白的,白的也可以说成黑的这就是我讨厌语文的原因;进入正题~

设备号相关 翻阅: <<<>>>

stat函数相关: <>

最近遇见一个问题,是关于硬链接的,硬链接呢,跨文件系统是不可以使用的,比如说

zhanglei@ubuntu:~$ df -h
Filesystem      Size  Used Avail Use% Mounted on
udev            1.9G     0  1.9G   0% /dev
tmpfs           389M  1.9M  388M   1% /run
/dev/sda5        39G   30G  7.9G  79% /
tmpfs           1.9G     0  1.9G   0% /dev/shm
tmpfs           5.0M  4.0K  5.0M   1% /run/lock
tmpfs           1.9G     0  1.9G   0% /sys/fs/cgroup
/dev/loop0      128K  128K     0 100% /snap/bare/5
/dev/loop1      220M  220M     0 100% /snap/code/95
/dev/loop2      112M  112M     0 100% /snap/core/12941
/dev/loop3      111M  111M     0 100% /snap/core/12834
/dev/loop4      228M  228M     0 100% /snap/code/96
/dev/loop5       44M   44M     0 100% /snap/snapd/15177
/dev/loop6       55M   55M     0 100% /snap/snap-store/558
/dev/loop7       66M   66M     0 100% /snap/gtk-common-themes/1519
/dev/loop8       62M   62M     0 100% /snap/core20/1405
/dev/loop9       45M   45M     0 100% /snap/snapd/15534
/dev/loop10     249M  249M     0 100% /snap/gnome-3-38-2004/99
/dev/loop11      62M   62M     0 100% /snap/core20/1434
/dev/sda1       511M  4.0K  511M   1% /boot/efi
tmpfs           389M   24K  389M   1% /run/user/1000

如果我想看这个文件是隶属于哪个文件系统的呢?

df 里有这个功能

zhanglei@ubuntu:~$ df -h /home/zhanglei/
Filesystem      Size  Used Avail Use% Mounted on
/dev/sda5        39G   30G  7.9G  79% /

我的解决方案是读 df 这个二进制 的源码,参考kernel 的源码,我认为是最好的解决方案。

df的源码位于 linux coreutils 里面,他不属于kernel的源码,是kernel上层重要的封装,coreutils有很多重要的工具比如说ls ln 等等工具的实现

df的源码位于coreutils

GitHub - coreutils/coreutils: upstream mirror

coreutils 依赖gunlib 底层库

GitHub - coreutils/gnulib: upstream mirror

df的实现在df.c里面,我们看核心代码,df.c 在main 函数里调用了

  mount_list =
    read_file_system_list ((fs_select_list != NULL
                            || fs_exclude_list != NULL
                            || print_type
                            || field_data[FSTYPE_FIELD].used
                            || show_local_fs));

这个api去读了/proc/mounts

#  define MOUNTED "/proc/mounts"

read_file_system_list  关于 /proc/mounts 部分核心实现

      {
        struct mntent *mnt;
        char const *table = MOUNTED;

        fp = setmntent (table, "r");
        if (fp == NULL)
          return NULL;

        while ((mnt = getmntent (fp)))
          {
            bool bind = hasmntopt (mnt, "bind");

            me = xmalloc (sizeof *me);
            me->me_devname = xstrdup (mnt->mnt_fsname);
            me->me_mountdir = xstrdup (mnt->mnt_dir);
            me->me_mntroot = NULL;
            me->me_type = xstrdup (mnt->mnt_type);
            me->me_type_malloced = 1;
            me->me_dummy = ME_DUMMY (me->me_devname, me->me_type, bind);
            me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
            me->me_dev = dev_from_mount_options (mnt->mnt_opts);

            
            *mtail = me;
            mtail = &me->me_next;
          }

        if (endmntent (fp) == 0)
          goto free_then_fail;
      }
  }

/proc/mounts 读取出来后,会刷新到内存 struct mount_entry * 里。

df.c相关部分实现

df 查找文件系统相关的核心代码

    if (best_match
        && (stat (best_match->me_mountdir, &disk_stats) != 0
            || disk_stats.st_dev != statp->st_dev))
        best_match = NULL;

核心思路是 读取到/proc/mounts 后找到挂载的目录

sysfs /sys sysfs rw,nosuid,nodev,noexec,relatime 0 0
proc /proc proc rw,nosuid,nodev,noexec,relatime 0 0
udev /dev devtmpfs rw,nosuid,noexec,relatime,size=1957852k,nr_inodes=489463,mode=755,inode64 0 0
devpts /dev/pts devpts rw,nosuid,noexec,relatime,gid=5,mode=620,ptmxmode=000 0 0
tmpfs /run tmpfs rw,nosuid,nodev,noexec,relatime,size=398268k,mode=755,inode64 0 0

然后调用stat 函数,stat函数核心数据结构

struct stat
  {
    __dev_t st_dev;		
#ifndef __x86_64__
    unsigned short int __pad1;
#endif
#if defined __x86_64__ || !defined __USE_FILE_OFFSET64
    __ino_t st_ino;		
#else
    __ino_t __st_ino;			
#endif
#ifndef __x86_64__
    __mode_t st_mode;			
    __nlink_t st_nlink;			
#else
    __nlink_t st_nlink;		
    __mode_t st_mode;		
#endif
    __uid_t st_uid;		
    __gid_t st_gid;		
#ifdef __x86_64__
    int __pad0;
#endif
    __dev_t st_rdev;		
#ifndef __x86_64__
    unsigned short int __pad2;
#endif
#if defined __x86_64__ || !defined __USE_FILE_OFFSET64
    __off_t st_size;			
#else
    __off64_t st_size;			
#endif
    __blksize_t st_blksize;	
#if defined __x86_64__  || !defined __USE_FILE_OFFSET64
    __blkcnt_t st_blocks;		
#else
    __blkcnt64_t st_blocks;		
#endif
#ifdef __USE_XOPEN2K8
    
    struct timespec st_atim;		
    struct timespec st_mtim;		
    struct timespec st_ctim;		
# define st_atime st_atim.tv_sec	
# define st_mtime st_mtim.tv_sec
# define st_ctime st_ctim.tv_sec
#else
    __time_t st_atime;			
    __syscall_ulong_t st_atimensec;	
    __time_t st_mtime;			
    __syscall_ulong_t st_mtimensec;	
    __time_t st_ctime;			
    __syscall_ulong_t st_ctimensec;	
#endif
#ifdef __x86_64__
    __syscall_slong_t __glibc_reserved[3];
#else
# ifndef __USE_FILE_OFFSET64
    unsigned long int __glibc_reserved4;
    unsigned long int __glibc_reserved5;
# else
    __ino64_t st_ino;			
# endif
#endif
  };

df 核心逻辑是通过/proc/mounts 读到所有磁盘挂载的目录,然后调用stat 去拿到目录的st_dev,然后去把硬链接的src 的st_dev 和 mount 目录的st_dev 做比较,如果相同的话就代表挂载到相应的filesystem

再次简单回顾下设备id,取自<>

二、主设备号和次设备号
 

ls -l /dev 我们看到主设备号是 用户之后的第一列 次设备 是第二列

主设备号由用户使用 次设备号由内核使用

linux内核中有宏定义

中

主设备号是MAJOR宏
次设备是MINOR宏

c 语言 简单实现

#include 
#include 
#include 

int main() {
    struct stat tmp;
    struct stat sda5;
    stat("/", &sda5);
    stat("/home/zhanglei", &tmp);
    printf("sda5:%ld;tmp:%ldn", sda5.st_dev, tmp.st_dev);
    return 0;
}

我们发现设备id是相同的

sda5:2053;tmp:2053

golang 核心查找逻辑实现

package source

import (
	"fmt"
	"os"
	"path/filepath"
	"github.com/pkg/errors"
	"math/rand"
	"syscall"
)

type HardLinkStat struct {
	hardlinkDir string
	info *FileInfo
}

func (handle *HardLinkStat) Init(hardlinkDir string) error {
	file, err := GetFileInfo(hardlinkDir)

	if err != nil {
		return errors.New(fmt.Sprintf("(%s) GetFileInfo error!", hardlinkDir))
	}
	handle.hardlinkDir = hardlinkDir
	handle.info = file
	return nil
}

func (handle *HardLinkStat) CheckAndHardLinkByFileInfo(pth string, fileInfo os.FileInfo) (pthLink string, err error) {
	stat, ok := fileInfo.Sys().(*syscall.Stat_t)
	if !ok {
		err = errors.Wrapf(err, "os.Link failed")
		return
	}

	if stat.Dev != handle.info.DevId {
		err = errors.Wrapf(err, "os.Link failed,error:%s", "stat.Dev != handle.info.DevId")
		return
	}

	return handle.HardLink(pth)
}

func (handle *HardLinkStat) HardLink(pth string) (pthLink string, err error) {
	// create a temp file which hand-links to the stream file
	ext := filepath.Ext(pth)
	dir := filepath.Dir(pth)
	for {
		pthLink = filepath.Join(dir, fmt.Sprintf("%08x.hardlink%s", rand.Uint32(), ext))
		if err = os.Link(pth, pthLink); err == nil {
			return
		} else if !errors.Is(err, os.ErrExist) {
			err = errors.Wrapf(err, "os.Link failed")
			return
		}
	}
}


转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/875413.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号