펌웨어 스크립트의 두 분기를 나란히 놓아 봤다.
보드 A
mount -t jffs2 /dev/mtdblock4 /mnt/upgrade/
cd /mnt/upgrade/upgrade
rm -rf ./c5/*
cd $path
cp -rf ./c5/* /mnt/upgrade/upgrade
flash_erase /dev/mtd2 0x0 0x1
nandwrite -p -s 0x0 /dev/mtd2 /mnt/upgrade/upgrade/upgrade-marker.bin
sync
umount /dev/mtdblock4
보드 B
flash_erase /dev/mtd0 0x0 0x80
nandwrite -p -s 0x0 /dev/mtd0 ./BOOT.bin
... (네 번 반복)
flash_erase /dev/mtd0 0x1040000 0x1
nandwrite -p -s 0x1040000 /dev/mtd0 ./upgrade-marker.bin
sync
같은 장비의 같은 작업인데 방식이 다르다.
Table of contents
Open Table of contents
두 분기를 나란히 놓았다
A는 플래시를 mount 한다. 마운트하고 옛 파일을 지우고 새 파일을 복사하고 언마운트한다.
B는 장치에 바로 쓴다. flash_erase 로 지우고 nandwrite 로 쓰는 것을 네 번 반복한다.
한쪽은 파일 복사고 다른 쪽은 블록 단위 쓰기다. 결과적으로 같은 장비가 새 펌웨어로 뜨는데 거기까지 가는 길이 다르다.
A는 복사하고 B는 직접 쓴다
왜 다른지를 각 분기가 다루는 파일 목록에서 찾았다. A 쪽은 이랬다.
angstrom_rootfs.jffs2
soc_system.rbf
upgrade-marker.bin
부트로더도 커널도 없다. FPGA 설정인 soc_system.rbf 와 루트 이미지뿐이라 파일로 두고 다음 부팅에 읽히면 되는 것들이다.
B는 부트로더와 디바이스트리와 커널을 바꾼다. 이것들은 파일이 아니라 특정 주소에 있어야 하는데 부트로더가 그 주소를 읽기 때문이다.
주소에서 읽히는 것은 마운트해서 파일로 넣을 수 없다. 넣어 봐야 읽는 쪽이 그 자리를 안 본다.
원인 — 대상에 따른 방식 차이
정리하면 이렇게 된다.
[파일로 읽히는 것] 마운트 → 복사
[주소에서 읽히는 것] 직접 쓰기
어떻게 읽히느냐가 어떻게 쓰느냐를 정한다. 쓰는 쪽의 취향이 아니라 읽는 쪽의 요구가 방식을 고른 것이다.
그래서 두 분기가 다른 것은 일관성이 없어서가 아니었다. 담는 것이 다르니 담는 방식도 다른 것이다.
마커는 양쪽 다 직접 썼다
그런데 A 안에서도 마커만은 직접 쓴다.
cp -rf ./c5/* /mnt/upgrade/upgrade # 파일 복사
flash_erase /dev/mtd2 0x0 0x1 # 여기부터 직접
nandwrite -p -s 0x0 /dev/mtd2 .../upgrade-marker.bin
cp 로 파일을 복사하다가 마커 차례에서 flash_erase 와 nandwrite 로 바뀐다. 한 분기 안에 두 방식이 섞여 있는 셈이다.
마운트를 이미 해 둔 상태이므로 마커도 파일로 복사할 수 있었을 텐데 그렇게 안 했다. 앞의 규칙을 그대로 적용하면 이유가 나온다.
전체 흐름 — 읽는 시점과 저장 위치
마커를 읽는 쪽이 파일시스템을 안 거친다.
[파일시스템이 뜨기 전] 마커를 읽어야 함
↓
파일로 두면 못 읽음
↓
특정 주소에 둠
읽는 시점이 파일시스템보다 앞이다. 부팅 초기에 이 값을 보고 업그레이드가 끝났는지를 판단해야 하는데 그때는 아직 마운트가 안 돼 있다.
앞의 정리에 한 줄이 더 붙는다. 부팅 초기에 읽히면 주소에 두고 나중에 읽히면 파일로 둔다.
대상의 종류가 아니라 읽히는 시점이 자리를 정한 것이다. 마커는 A에서도 B에서도 같은 이유로 같은 방식이 된다.
언마운트 전에 동기화한다
A 끝에 두 줄이 붙어 있다.
sync
umount /dev/mtdblock4
umount 가 알아서 반영할 텐데 sync 가 앞에 따로 있다. 중복처럼 보인다.
다만 umount 는 실패할 수 있다. 그 자리를 누가 쓰고 있으면 안 풀린다.
[sync 먼저] umount 가 실패해도 데이터는 반영됨
[umount 만] 실패하면 반영도 안 될 수 있음
실패해도 내용은 남기려는 것으로 보인다. 성공하는 경로에서는 한 번 더 하는 셈이고 실패하는 경로에서만 값을 한다.
지우고 복사하는 이유
A에 지우기와 복사가 나란히 있다.
cd /mnt/upgrade/upgrade
rm -rf ./c5/*
cd $path
cp -rf ./c5/* /mnt/upgrade/upgrade
rm -rf 로 먼저 비우고 cp -rf 로 새것을 넣는다. 덮어써도 될 것 같은데 지우는 단계를 굳이 둔다.
덮어쓰기는 새 패키지에 없는 옛 파일을 남긴다. 판이 올라가면서 파일 목록이 바뀌면 지난 판의 잔여물이 섞이는데 그것이 겉으로는 안 보인다.
공간도 문제다. 지우고 쓰면 한 시점에 필요한 자리가 줄어드는데 저장 자리가 작은 장비에서는 그 차이가 크다.
주의 — 상대 경로와 경로 오가기
이 대목에서 cd 가 두 번 나온다.
path=$(pwd) # 맨 위에서 저장
...
cd /mnt/upgrade/upgrade
...
cd $path # 돌아옴
맨 위에서 pwd 를 path 에 저장해 두고 마운트 지점으로 갔다가 다시 돌아온다. 상대 경로를 쓰기 때문에 생긴 왕복이다.
cp -rf ./c5/* 의 ./c5 는 패키지를 푼 자리 기준이라 다른 데로 갔다 오면 안 맞는다. 그래서 돌아오는 cd 가 반드시 필요하다.
절대 경로였으면 이 왕복이 필요 없었다.
cp -rf $path/c5/* /mnt/upgrade/upgrade
path 를 이미 저장해 뒀으니 그것을 쓰면 어디서 실행해도 같다. cd 로 오가는 것보다 이쪽이 읽기도 쉽고 중간에 cd 가 실패했을 때의 위험도 없다.
정리
- 같은 목적에 마운트 후
cp와nandwrite직접 쓰기 두 방식이 쓰인다 - 파일로 읽히는 것은 복사하고 주소에서 읽히는 것은 직접 쓴다
- 쓰는 쪽이 아니라 읽는 쪽의 요구가 방식을 고른다
- 마커는 파일시스템이 뜨기 전에 읽혀서 파일로 못 둔다
- 읽히는 시점이 저장 자리를 정한다
umount앞의sync는 언마운트가 실패해도 내용을 남긴다rm -rf로 비우고cp하면 지난 판 잔여물이 안 남고 자리도 덜 든다- 상대 경로를 쓰느라
cd로 오가는데$path를 쓰면 필요 없다