2013. 12. 17. 15:03

[Linux/Ubuntu 파일 시스템 관리] file system mount, un-mount(마운트, umount) 하기

/etc/fstab 파일을 통해 자동으로 mount되는 root file sytem (/)과 다른 file system들을 확인할 수 있다. 다른 file system은 mount 명령으로 수동으로 mount할 수 있다.


fstab 파일로 부터 file system mount하기


linux를 처음 설치했을 때, /etc/fstab 파일은 root file system에 대한 정보를 자동으로 포함시켜 준다. 이러한 file system은 부팅시에 mount되도록 하거나, 수동으로나마 mount 되도록 준비시켜 준다.


아래는 /etc/fstab 파일의 예제이다.

# device name   mount point     fs-type      options            dump-freq pass-num

LABEL=/         /               ext4         defaults           1 1

/dev/sda6       none            swap         defaults           0 0

none            /dev/pts        devpts       gid=5,mode=620     0 0

none            /proc           proc         defaults           0 0

none            /dev/shm        tmpfs        defaults           0 0

 

# Removable media

/dev/cdrom      /mnt/cdrom      udf,iso9660  noauto,owner,ro    0 0

 

# NTFS Windows 7 partition

/dev/sda1       /mnt/Windows    ntfs-3g      quiet,defaults,locale=en_US.utf8,umask=0,noexec     0 0

 

# Partition shared by Windows and Linux

/dev/sda7       /mnt/shared     vfat         umask=000          0 0

 

# mounting tmpfs

tmpfs           /mnt/tmpfschk   tmpfs        size=100m          0 0

 

# mounting cifs

//pingu/ashare  /store/pingu    cifs         credentials=/root/smbpass.txt                       0 0

 

# mounting NFS

pingu:/store    /store          nfs          rw                 0 0


# mounting UUID

UUID=9a9611fe-8363-4204-9d6b-e183c8ab8f5f /mnt/uuid ext4 defaults 0 0


# no auto

/dev/sda2       /mnt/WindowsOld ntfs-3g      noauto             0 0


# LVM

/dev/VolGroup00/LogVol00 /mnt/lvm000 ext3    defaults           0 0

모든 file system은 자동으로 mount될 수 있으나, /dev/sda2는 noauto 옵션을 사용하여 자동으로 mount되지 않는다. /mnt/lvm000은 LVM(logical volume management)로 설정된 파티션을 사용한다. LVM volume은 물리적 파티션을 이동하거나 뭉치는 작업을 보다 쉽게 해준다.


/etc/fstab 파일의 field 설명은 다음과 같다.


 field

 설명 

 1

file system을 표현하는 device 이름. 원래 mount할 파티션의 device 이름(/dev/sda1)이 되도록 하였다. 이제는 LABEL이나 UUID를 device 이름 대신에 사용할 수 있다.

 2

file system의 mount 지점. directory tree 구조에 해당되는 mount될 지점. 

 3

file system 종류.

 4

mount 명령 옵션. mount 옵션 중에 noauto와 ro 등이 있는데 이들이 사용될 수 있다. 콤마(,)로 구별할 수 있다.  

 5

dump file system? 만약 dump로 이뤄진 backup이 필요한 경우 설정한다. 1은 dump가 될 필요성이 있다는 뜻이다. 

 6

file system check? fsck를 통한 file system 검사 여부를 지정한다. 1은 우선 검사를 수행해라는 뜻으로 보통 root file system에서 사용된다. 2는 root file system이 검사가 이뤄진뒤 검사를 수행한다.


하드 디스크 혹은 이동식 디스크를 /etc/fstab에 추가할 수 있다. 또한 remote file system(NFS, Samba)도 가능하다.


mount 명령으로 file system mount하기


아래는 현재 mount된 remote 혹은 local file system이 나열된다.

~$ mount

/dev/sdb5 on / type ext4 (rw,errors=remount-ro,commit=0)

proc on /proc type proc (rw,noexec,nosuid,nodev)

none on /sys type sysfs (rw,noexec,nosuid,nodev)

fusectl on /sys/fs/fuse/connections type fusectl (rw)

none on /sys/kernel/debug type debugfs (rw)

none on /sys/kernel/security type securityfs (rw)

none on /dev type devtmpfs (rw,mode=0755)

none on /dev/pts type devpts (rw,noexec,nosuid,gid=5,mode=0620)

none on /dev/shm type tmpfs (rw,nosuid,nodev)

none on /var/run type tmpfs (rw,nosuid,mode=0755)

none on /var/lock type tmpfs (rw,noexec,nosuid,nodev)

binfmt_misc on /proc/sys/fs/binfmt_misc type binfmt_misc (rw,noexec,nosuid,nodev)

vmware-vmblock on /var/run/vmblock-fuse type fuse.vmware-vmblock (rw,nosuid,nodev,default_permissions,allow_other)

gvfs-fuse-daemon on /home/greenfish/.gvfs type fuse.gvfs-fuse-daemon (rw,nosuid,nodev,user=greenfish)

~$


-t으로 file system을 지정할 수 있으며, -l로 label 정보를 확인할 수 있다.

~$ mount -t ext4

/dev/sdb5 on / type ext4 (rw,errors=remount-ro,commit=0)

~$ 

~$ mount -t ext4 -l

/dev/sdb5 on / type ext4 (rw,errors=remount-ro,commit=0) [ubuntu_boot]

~$


다음은 간단한 mount 명령의 사용예이다. /dev/sda1 device를 /mnt/mnt000에 mount한다.

~$ sudo mount /dev/sda1 /mnt/mnt000

~$ 

~$ 

~$ sudo mount -v /dev/sda1 /mnt/mnt000

mount: you didn't specify a filesystem type for /dev/sda1

       I will try type ntfs

Mount is denied because the NTFS volume is already exclusively opened.

The volume may be already mounted, or another software may use it which

could be identified for example by the help of the 'fuser' command.

~$ sudo umount /mnt/mnt000

~$ sudo mount -v /dev/sda1 /mnt/mnt000

mount: you didn't specify a filesystem type for /dev/sda1

       I will try type ntfs

~$


-t를 이용하면 file system을 명시할 수 있다.

~$ sudo mount -v -t ntfs-3g /dev/sda1 /mnt/mnt000

~$ sudo umount /mnt/mnt000

~$ sudo mount -v -t ext3 /dev/sda1 /mnt/mnt000

mount: wrong fs type, bad option, bad superblock on /dev/sda1,

       missing codepage or helper program, or other error

       In some cases useful info is found in syslog - try

       dmesg | tail  or so


~$


앞선 /etc/fstab file과 관계는 다음과 같다.

~$ sudo mount -v /dev/sda1

mount: can't find /dev/sda1 in /etc/fstab or /etc/mtab

~$ 


그리고, /etc/fstab 파일에 아래 부분을 추가하고 저장한다.

...

/dev/sda1 /mnt/mnt001 ntfs-3g defaults 0 0 


그럼, 이전의 오류가 발생하지 않는다.

~$ sudo mount -v /dev/sda1

~$


fstab에 사용될 option은 -o를 사용하여 연결할 수 있다. 다음과 같이 rw(read/write), ro(read only) 지정이 가능하다.

~$ sudo mount -v -o rw /dev/sda1 /mnt/mnt000

mount: you didn't specify a filesystem type for /dev/sda1

       I will try type ntfs

~$ sudo umount /mnt/mnt000

~$ sudo mount -v -o ro /dev/sda1 /mnt/mnt000

mount: you didn't specify a filesystem type for /dev/sda1

       I will try type ntfs

~$ sudo touch /mnt/mnt000/test.txt

touch: cannot touch `/mnt/mnt000/test.txt': Read-only file system

~$


주요한 option은 다음과 같다.

  • noatime
    ; file의 access time을 수정하지 않는다. I/O가 빈번한 mail spool 이나 log server 등에 적합하다.
  • noexec
    ; file system에 위치한 binary의 실행을 금지한다. 신뢰되지 않는 사용자의 /tmp 경로의 실행을 금지하여 보안성을 향상시킨다.
  • remount
    ; mount된 file system의 option을 변경한다. 한번의 명령으로 un-mount 수행후 다시 새로운 option으로 mount를 시도한다. 아래는 이전의 read/write mount option을 read-only로 변경한다.
    $ sudo mount -v -o remount,ro /dev/sda1
  • --bind
    ; directory tree 상의 다른 위치로 이미 mount된 file system을 mount한다.
    ~$ sudo mount -v /dev/sda1 /mnt/mnt000
    mount: you didn't specify a filesystem type for /dev/sda1
           I will try type ntfs
    ~$ sudo mount --bind -v /mnt/mnt000 /mnt/mnt000_sub
    /mnt/mnt000 on /mnt/mnt000_sub type none (rw,bind)
    ~$ ls /mnt/mnt000/AUTOEXEC.BAT
    /mnt/mnt000/AUTOEXEC.BAT
    ~$ ls /mnt/mnt000_sub/AUTOEXEC.BAT
    /mnt/mnt000_sub/AUTOEXEC.BAT
    ~$
  • --move
    ; mount 지점을 변경한다. 위 --bind 예제가 실행된 이후를 가정하여 아래와 같이 설명할 수 있다.
    ~$ sudo mount --move -v /mnt/mnt000 /mnt/mnt000_changed
    /mnt/mnt000 on /mnt/mnt000_changed type none (rw)
    ~$ ls /mnt/mnt000/AUTOEXEC.BAT
    ls: cannot access /mnt/mnt000/AUTOEXEC.BAT: No such file or directory
    ~$ ls /mnt/mnt000_changed/AUTOEXEC.BAT
    /mnt/mnt000_changed/AUTOEXEC.BAT
    ~$ ls /mnt/mnt000_sub/AUTOEXEC.BAT
    /mnt/mnt000_sub/AUTOEXEC.BAT
    ~$

참고로, windows의 공유 폴더등을 mount할 수 있는데, cifs package가 필요하다. ~$ sudo apt-get install cifs를 실행한후 다음과 같은 형식으로 공유폴더를 mount한다.

~$ sudo mount -v -t cifs //xxx.xxx.xxx.xxx/PATH /mnt/share -o username=USERID,domain=DOMAIN,file_mode=0777,dir_mode=07777

Password:

~$

xxxx.xxxx.xxxx.xxxx는 공유 폴더의 ip이며, PATH는 일상적인 windows의 공유 폴더 경로이다. 즉, \\xxx.xxx.xxx.xxx\PATH 를 생각하면 된다. USERID를 입력하면 되며, DOMAIN이 필요한 경우 지정한다. 필요하지 않다면, domain=은 기입하지 않아도 된다. file_mode와 dir_mode는 각각의 permission을 넣으면 되며, 필요하지 않다면 file_mode=와 dir_mode=는 기입하지 않아도 된다.

해당 명령을 실행하면 Password를 물어보는데, 명령으로 전달하기 위해서는 password=를 -o 이후에 추가하면 된다. 물론, password를 plain text로 전달해야 한다.


swap 공간을 file로([Research/Ubuntu] - [Linux/Ubuntu 파일 시스템 관리] swap 파티션 관리하기) 할 수 있듯이, 단일 file을 가지고 file system을 생성하고 loopback mount라고 알려진 방법으로 mount까지 할 수 있다(

[Research/Ubuntu] - [Linux/Ubuntu 파일 시스템 관리] File system 포맷(format)하기의 virtual file system 만들기 참고). linux 설치 cd 혹은 live cd의 download 이후 loopback으로 mount하여 그 내부의 파일들을 탐색할 수 있다.

~$ sudo mount -v ubuntu-11.04-desktop-i386.iso /mnt/ubuntu_img

mount: you didn't specify a filesystem type for /home/greenfish/ubuntu-11.04-desktop-i386.iso

       I will try type iso9660

mount: /home/greenfish/ubuntu-11.04-desktop-i386.iso is not a block device (maybe try `-o loop'?)

~$ sudo mount -v -o loop ubuntu-11.04-desktop-i386.iso /mnt/ubuntu_img

mount: going to use the loop device /dev/loop0

mount: you didn't specify a filesystem type for /dev/loop0

       I will try type iso9660

/dev/loop0 on /mnt/ubuntu_img type iso9660 (rw)

~$ ls /mnt/ubuntu_img/

autorun.inf  dists     md5sum.txt  preseed             usb-creator.exe

boot         install   pics        README.diskdefines  wubi.exe

casper       isolinux  pool        ubuntu

위 예는 ubuntu 11.04 설치 iso 파일을 mount하고, 그 내용을 탐색한 과정을 표시한 것이다. 우선, -o loop를 사용하지 않아 오류가 발생한 것이 확인되며, /dev/loop0를 사용한다고 알려준다.

loseetup을 통해 loopback device를 설정할 수 있다.

~$ sudo losetup /dev/loop0

/dev/loop0: [0815]:1180932 (/home/greenfish/ubuntu-11.04-desktop-i386.iso)

~$ sudo losetup -d /dev/loop1

loop: can't delete device /dev/loop1: No such device or address

~$

-d option은 주로 NTS나 samba로 mount 되어있을 때, un-mount 실행에 문제가 발생하여 강제로 un-mount 할 때 실행한다.


umount를 가지고 file system un-mount하기


file system을 un-mount하기 위해 umount 명령을 호출한다. device 이름 혹은 mount 지점을 전달하여 un-mount 할 수 있다. 보통, mount 지점을 전달하도록 하면 혼란을 줄일 수 있다.


아래와 같이 mount와 un-mount를 번갈아 실행할 수 있다. umount에도 -v를 사용하여 자세한 정보를 확인할 수 있다.

~$ sudo mount /dev/sda1 /mnt/mnt000

~$ sudo umount /dev/sda1

~$ sudo mount /dev/sda1 /mnt/mnt000

~$ sudo umount -v /mnt/mnt000

/dev/sda1 umounted

~$


다음을 한번 실행해 본다. (vi editor가 익숙하다면 vi를 사용하라.)

~$ gedit file_lock.c


#define _GNU_SOURCE

#include <stdio.h>

#include <fcntl.h>

#include <sys/types.h>

#include <sys/stat.h>

#include <errno.h>


int main(int argc, char **argv) {

  int i = 0;

  int fd_internal = -1;


  if (argc > 1) {

    int fd = open(argv[1], O_WRONLY, O_DIRECT);

    if(fd == -1) {

      printf("Unable to open the file\n");

      exit(1);

    }

    static struct flock lock;


    lock.l_type = F_WRLCK;

    lock.l_start = 0;

    lock.l_whence = SEEK_SET;

    lock.l_len = 0;

    lock.l_pid = getpid();


    int ret = fcntl(fd, F_SETLKW, &lock);

    printf("Return value of fcntl:%d\n",ret);


    // endless loop

    for (i=0;;i++)

    {

        ssize_t readed = 0;

        unsigned char buf[33] = {0,};


        fflush(stdout);


        if (-1 != fd_internal)

        {

            close(fd_internal);

            fd_internal = -1;

        }


        fd_internal = open(argv[1], O_RDONLY, O_DIRECT);

        if(fd_internal == -1) {

          printf("Unable to open the file(next fd), errno=%d\n", errno);


          // fail, but try to use previous fd

          readed = read(fd, (void*)buf, 32);

          if (-1 == readed)

          {

              printf("success to read by the previous fd\r\n");

          }

          else

          {

             printf("failed to read by the previous fd\r\n");

          }

        }


        readed = read(fd_internal, (void*)buf, 32);


        if (-1 == readed)

        {

            printf("[%.4d] Read err, errno=%d\r\n", i, errno);


            fsync(fd);


            // fail, but try to use previous fd

            readed = read(fd, (void*)buf, 32);

            if (-1 == readed)

            {

                printf("success to read by the previous fd\r\n");

            }

            else

            {

                printf("failed to read by the previous fd\r\n");

            }

        }

        else

        {

            printf("[%.4d](%d) %s", i, readed, buf);

        }


        lseek(fd, 0, SEEK_SET);


        sleep(2);

    }

  }

}


~$ cc -o file_lock file_lock.c

file_lock.c: In function ‘main’:

file_lock.c:15:7: warning: incompatible implicit declaration of built-in function ‘exit’

~$

file_lock 이라는 binary를 생성하였는데, 이는 임의의 file을 lock하고, 지속적으로 open/read를 수행한다. open에 대해서는, O_DIRECT를 추가하여, buffer 없이 실행하도록 한다. 이러한 file_lock 기능을 추가한 이유는 lock으로 인한 un-mount 실패를 확인해 보기 위해서이다.


우선, terminal 하나(1번)를 열어, 다음과 같이 mount한다.

~$ sudo mount /dev/sda1 /mnt/mnt000

~$


그리고, 다른 terminal 하나(2번)를 열어, 다음과 같이 readme.txt를 생성하고, file_lock 한다.

~$ echo "i'm readme.txt. hello,world~~~~" > /mnt/mnt000/readme.txt

~$ ./file_lock /mnt/mnt000/readme.txt

Return value of fcntl:0

[0000](32) i'm readme.txt. hello,world~~~~

[0001](32) i'm readme.txt. hello,world~~~~

[0002](32) i'm readme.txt. hello,world~~~~

[0003](32) i'm readme.txt. hello,world~~~~

[0004](32) i'm readme.txt. hello,world~~~~

...

위와 같이 2초에 한번씩 /mnt/mnt000/readme.txt를 열어서 파일을 출력한다.

file_lock은 초반에 해당 file을 열고, 2초에 한번씩 또한번 열고 읽고 출력한뒤 다시 닫는다.

다시 말해, file의 영구적인 fd가 있고, 2초마다 열리고 닫히는 fd가 있다. 즉, 2개의 fd가 관리된다.

이런 상황에서, terminal 1번으로 옮겨가서, un-mount를 시도해 보자.

~$ sudo umount /mnt/mnt000

umount: /mnt/mnt000: device is busy.

        (In some cases useful info about processes that use

         the device is found by lsof(8) or fuser(1))

~$

즉, busy로 인해 실패하였다. 만일, fd가 2초마다 한번씩 열리고 닫히는 것만 유지되었다면, 미묘한 타이밍에는 busy하지 않으므로, 우연히 성공할 수 있을 것이다. 그러나, file_lock에서는, 계속 open을 시켜놓는 fd도 있기 때문에, umount는 항상 실패할 수 밖에 없다.

그래서 일반적인 방법으로, lock하고 있는 process를 kill하여 un-mount하는 과정을 살펴보도록 하자.


~$ sudo lsof | grep mnt000

lsof: WARNING: can't stat() fuse.gvfs-fuse-daemon file system /home/greenfish/.gvfs

      Output information may be incomplete.

file_lock 4373  greenfish    3w      REG        8,1          32      11660 /mnt/mnt000/readme.txt

file_lock 4373  greenfish    4r      REG        8,1          32      11660 /mnt/mnt000/readme.txt

~$

와 같이 4373 process에서 2개가 사용되고 있음이 확인된다. (fuser를 이용하기도 한다. 본 블로그의 'fuser'를 검색해 보기 바란다.)

~$ kill 4373

~$

하면, terminal 2번은 다음과 같이 프로세스가 종료된다.

...

[0218](32) i'm readme.txt. hello,world~~~~

[0219](32) i'm readme.txt. hello,world~~~~

Terminated

~$

그럼 다음과 같이 un-mount가 가능해진다.

~$ sudo umount /mnt/mnt000

~$


이제, lazy unmount를 시도해 보자.

앞의 과정(file_lock 실행까지)이 실행되었다 가정한다.

그럼 여전히 umount는 busy 오류가 발생할 것이다.

이때, 다음의 lazy un-mount 명령을 실행한다.

~$ sudo umount -vl /mnt/mnt000

~$

즉, -l을 사용하면 되는데, 오류 없이 성공하게 된다.

그럼, terminal 2번을 확인하면 다음과 같다.

[0029](32) i'm readme.txt. hello,world~~~~

[0030](32) i'm readme.txt. hello,world~~~~

[0031](32) i'm readme.txt. hello,world~~~~

Unable to open the file(next fd), errno=2

success to read by the previous fd

[0032] Read err, errno=9

success to read by the previous fd

Unable to open the file(next fd), errno=2

success to read by the previous fd

[0033] Read err, errno=9

success to read by the previous fd

...

즉, lazy un-mount를 실행하면, 해당 mount 지점을 사용하고 있는 프로세스는 종료되지 않고 계속 실행됨이 확인된다. 단지, lazy un-mount가 호출되면, 그 이후로는 open 실패(2=ENOENT)가 발생한다. 그리고, lazy un-mount가 실행되기 전에 open된 fd는 read 마저도 성공하게 된다. 몇몇 애매함이 존재하는 듯 하므로, 되도록 busy로 인한 un-mount 실패시에는 kill을 이용하여 명시적인 un-mount를 호출하는 것이 안전할 듯 하다.


마지막으로 cd-rom 같은 이동식 저장소인 경우, eject를 통한 un-mount가 가능하다.

~$ sudo eject /dev/cdrom

~$