그래서, 쓰던 HDD는 빼고 (잘했지...), 새 SSD에 linux를 깔았다. 그리고, 몇번 끄적거리다보니 다른 notebook에 밀리기 시작함. 생각해보면, Linux를 쓰는데, 2008년식 물건을 쓸 이유가 없다. 주위에 좀 찾아보면 2011년 PC notebook에 깔아도 MacBook 2008보다 빠르니까...
어제 자다가 갑자기 MacBook이 생각나서 이리저리 생각하다가 다시 Mac OS로 돌아가기로 결심했다. 주위에 8 Bit MSX나 68k Mac을 취미로 쓰는 사람들을 생각해보니, MacBook은 그것보다 더 현역이다. internet도 되고, firefox도 source 구해다가 설치하면 될 것같고, 레트로 취미로 나쁘지 않다는 생각이 들었다.
일어나서 쑤셔넣었던 HDD (무려 250GB다!)로 갈아끼우니, 반가운 화면이 떴다.
그리고, firefox를 source build하려다보니 다음과 같은 error가 brew에서 나왔다.
curl: (35) error:1407742E:SSL routines:SSL23_GET_SERVER_HELLO:tlsv1 alert protocol version
아차... 이것때문에 linux 깔았었지. 누군가 이 문제를 풀었을 것이라고 생각해서 googling해서 찾아낸 link - https://try.gitea.io/donbright/lm
tar xf ~/Downloads/lm-master.tar.gz
export PATH=$HOME/lm/bin:$PATH
# put that export line in your ~/.profilefor now... you can removeifu need later)
cdlm
./buildy.sh
이제 firefox를 다시 build 시도할 예정.
참고로, MacBook에 binary들이 얼마나 중첩이 되어 있나하면.
sel-yjoh-m1:~ tolkien$ /usr/bin/git --version git version 1.7.12.4 (Apple Git-37) sel-yjoh-m1:~ tolkien$ /usr/local/bin/git --version git version 2.13.3 sel-yjoh-m1:~ tolkien$ ~/lm/bin/git --version git version 2.17.0
가지고 있는 macbook 2008 early model은 64bit cpu이지만, 32bit EFI BIOS여서 mac OS update에서 버림받았다. 그래도 그럭저럭 쓰다가 이젠... 사파리, 크롬도 못 쓰고, firefox 32bit 간신히 사용중인 상태에서 얼마전부터 ssh로 접속 안되는 곳이 생기기 시작. 아마 openssl issue로 추정. 그외 다수...라는 핑계로 mac OS 버리고 linux로 이주 결정.
hdiutil convert -format UDRW -o ubuntu.img ubuntu.iso 하면 ubuntu.img.dmg 파일이 나오는데, 이걸 ubuntu.img 로 이름 바꾸고 .. usb 메모리를 usb 포트에 꽂고 diskutil list 하면 usb 파티션이 어딘지 나와요. (저는 /dev/disk3 ... ) 그러면 diskutil unmountDisk /dev/disk3 하면 언마운트 됐다고 나오고 ...
sudo dd if=ubuntu.img of=/dev/disk3 bs=10m
라고 하고 기다리면 돼요..
그리고 리부팅하면서 option 키 누르고 있으면 부팅 미디어 선택 가능한데, 그때 USB 를 선택하면 되고 ..
파티션 나눌 때 /efi 파티션을 아마 몇백메가 만들어야 할 거에요 ...
뭐 따로 설치하거나 ... efi 파일을 복사하거나 .. 하지 않아도 요즘 건 거의 다 돼요 ...
어쨌든 내 macbook은 안된다. 일단, boot media로 인식하지 못한다. rufus, unetbootin등 다 해봤지만, 어쨌든 usb로 booting하는데 실패했다. 그러다가, custom efi binary를 써서 booting하는 방법을 따라해보니 된다. 이에 전산고고학을 전공하시는 후학을 위해서 간략하게 기록해본다.
Ubuntu-based ISOs and those that contain a \boot\grub\loopback.cfg file can be easily UEFI-booted to just by copying some files, no menu is needed!
1. Format a FAT32 USB flash drive (e.g. using RMPrepUSB)
2. Make a \efi\boot directory on the flash drive
3. Copy your Ubuntu-based ISO file (must be <4GB) to the \efi\boot directory and rename it as boot.iso
4. Extract the .efi files from the UEFI_boot_from_ISO.zip file and copy them to the \efi\boot directory
FAT32 volume
===========
\efi\boot\bootX64.efi
\efi\boot\bootIA32.efi
\efi\boot\boot.iso
Note: \efi\boot is lowercase and boot.iso is lowercase.
That's it!
Now you should be able to UEFI-boot (32-bit and 64-bit) from the USB flash drive (disable Secure Boot in the BIOS options first).
To use a different Ubuntu-based ISO which should contain a \boot\grub\loopback.cfg file, simply replace the boot.iso file.
A 64-bit version of Ubuntu will not boot on a 32-bit UEFI system. Use a 32-bit Ubuntu ISO on a 32-bit UEFI system and a 64-bit UEFI ISO on a 64-bit UEFI system.
This tutorial is based on an article here, but the.efi files in my download have been patched by me to load the loopback.cfg file and so ensure reliable booting.
In depth
The .efi file contains a version of grub2 with an embedded grub menu
The grub2 variable iso_path must be set before loopback.cfg is called (and exported).
An example of a simple embedded menu which boots to the loopback.cfg file inside the boot.iso is:
set efi_machtype='x86_64'
export efi_machtype
set efi_arch='EFI64'
export efi_arch
fix_video
set grub2efi_intel_gma_patch='enabled'
export grub2efi_intel_gma_patch
fakebios
set grub2efi_fakebios='generic'
export grub2efi_fakebios
set boot_iso='/efi/boot/boot.iso'
export boot_iso
set iso_path=${boot_iso}
export iso_path
set real_root=${root}
export real_root
set real_prefix=${prefix}
export real_prefix
loopback loop (${real_root})${boot_iso}
set root=(loop)
set gfxpayload=keep
configfile /boot/grub/loopback.cfg
boot
A typical Ubuntu menu entry inside loopback.cfg inside boot.iso would be:
Note that no persistent parameter is specified in the loopback.cfg, which prevents us from booting with persistence...:-(
그후, option key를 눌러서 boot media을 EFIboot로 선택하면 booting 된다. 안되면 다른 글을 찾아볼 것. 여기서 커피 한잔. 이제 시작한 거다.
설치는 다른 얘기다. 1. 배포폰 + 32/64bit 조합만으로 많은 삽질이 있었다. 2. 일반 pc처럼 mac OS -> grub로 얌전하게 booting되지 않는다. -> rEFInd라는 emulator를 설치해주어야 한다. 3. 나는 겪지 않았지만, 주변장치관련 잡다한 작업이 필요할 수 있다고 한다.
일반 PC와 달리 rEFInd를 설치할 partition이 필요하다. ubuntu 설치 usb로 booting후 바로 설치로 들어가는 것이 아니라 terminal을 열고 partition을 만들고 파일을 복사해주어야 한다.
- 설치할 disk가 sdb라고 한다면
- gdisk /dev/sdb 해서 GPT partition map을 쓴다고 선택한 다음
100M size의 partition을 만든다. type은 ef00 (EFI system)
# cd /boot/temp-efi # Or wherever you mounted your UEFI partition # mv EFI/refind/ EFI/boot/ # Rename the directory # mv EFI/boot/refind_ia32.efi EFI/boot/boot.efi # Rename the file
그리고, ubuntu 설치로 들어가서 설치하는데, 설치할 disk 선택시 전부 쓰기를 선택하면 만들었던 rEFInd partition이 날라간다. 따라서, 설치할 partition을 수동으로 만들어주어야 한다.
그외...
나는 ubuntu 12.04.5 64bit desktop을 먼저 설치한 다음, 14.04 -> 16.04로 단계적으로 upgrade했다.
- Fedora Live는 boot 초반에 grub console로 떨어진다. 뭔가 문제가 있겠지.
- ubuntu 16.04 desktop 32/64bit 둘 다 설치후반 grub 설치에서 error 나오고 멈춤.
커널은 유용한 데이터 구조를 구현하기위한 많은 라이브러리 루틴을 포함합니다. 그중 두 가지 유형의 tree가 있습니다 : radix tree와 red-black tree. 이 기사에서는 radix tree API를 살펴볼 것입니다. red-black tree는 나중에.
The kernel includes a number of library routines for the implementation of useful data structures. Among those are two types of trees: radix trees and red-black trees. This article will have a look at the radix tree API, with red-black trees to follow in the future.
Wikipedia는radix tree 항목을 가지고 있지만 Linux radix tree는 잘 설명되어 있지 않습니다. Linux radix tree는 (포인터) 값이 (긴) 정수 키와 연관 될 수있는 메커니즘입니다. 그것은 저장면에서 상당히 효율적이며, 조회에서 꽤 빠릅니다. 또한 Linux 커널의 radix tree에는 특정 항목과 태그를 연결하는 기능을 비롯하여 커널 관련 요구 사항에 따라 일부 기능이 있습니다.
Wikipedia hasa radix tree article, but Linux radix trees are not well described by that article. A Linux radix tree is a mechanism by which a (pointer) value can be associated with a (long) integer key. It is reasonably efficient in terms of storage, and is quite quick on lookups. Additionally, radix trees in the Linux kernel have some features driven by kernel-specific needs, including the ability to associate tags with specific entries.
위쪽 치즈 다이어그램은 Linux radix tree의 leaf node를 보여줍니다. 노드는 다수의 슬롯을 포함하며, 각각의 슬롯은 트리 작성자가 관심을 갖는 포인터를 포함 할 수 있습니다. 빈 슬롯은 NULL 포인터를 포함합니다. 이 tree들은 상당히 넓습니다 - 2.6.16-rc 커널에서는 각 radix tree node에 64 개의 슬롯이 있습니다. 슬롯은 (긴) 정수 키의 일부로 인덱싱됩니다. 가장 높은 키 값이 64보다 작으면 전체 트리를 단일 노드로 나타낼 수 있습니다. 그러나 일반적으로 키의 다소 큰 세트가 사용됩니다. 그렇지 않으면 간단한 배열을 사용할 수 있습니다. 그래서 더 큰 tree는 다음과 같이 보일 것입니다 :
The cheesy diagram on the right shows a leaf node from a Linux radix tree. The node contains a number of slots, each of which can contain a pointer to something of interest to the creator of the tree. Empty slots contain a NULL pointer. These trees are quite broad - in the 2.6.16-rc kernels, there are 64 slots in each radix tree node. Slots are indexed by a portion of the (long) integer key. If the highest key value is less than 64, the entire tree can be represented with a single node. Normally, however, a rather larger set of keys is in use - otherwise, a simple array could have been used. So a larger tree might look something like this:
이 tree는 3 level deep입니다. 커널이 특정 키를 검색 할 때 루트 노드에서 적절한 슬롯을 찾기 위해 가장 중요한 6 비트가 사용됩니다. 다음 6 비트는 중간 노드의 슬롯을 인덱싱하고, 최하위 6 비트는 실제 값을 가리키는 포인터를 포함하는 슬롯을 나타냅니다. 자식이 없는 노드는 트리에 존재하지 않으므로 radix tree가 성긴(sparse) tree에 대한 효율적인 저장소를 제공 할 수 있습니다.
This tree is three levels deep. When the kernel goes to look up a specific key, the most significant six bits will be used to find the appropriate slot in the root node. The next six bits then index the slot in the middle node, and the least significant six bits will indicate the slot containing a pointer to the actual value. Nodes which have no children are not present in the tree, so a radix tree can provide efficient storage for sparse trees.
Radix tree는 메인 라인 커널 트리에 몇 명의 사용자가 있습니다. PowerPC 아키텍처는 트리를 사용하여 실제 IRQ 번호와 가상 IRQ 번호를 매핑합니다. NFS 코드는 관련 아이 노드 구조에 트리를 첨부하여 미해결 요청을 추적합니다. 그러나 radix tree의 가장 널리 사용되는 것은 메모리 관리 코드에 있습니다. 백업 저장소를 추적하는 데 사용되는 address_space 구조에는 해당 매핑과 연결된 코어 페이지를 추적하는 radix tree가 있습니다. 무엇보다도 이 트리를 사용하면 메모리 관리 코드가 더럽거나 쓰기 저장중인 페이지를 빠르게 찾을 수 있습니다.
Radix trees have a few users in the mainline kernel tree. The PowerPC architecture uses a tree to map between real and virtual IRQ numbers. The NFS code attaches a tree to relevant inode structures to keep track of outstanding requests. The most widespread use of radix trees, however, is in the memory management code. The address_space structure used to keep track of backing store contains a radix tree which tracks in-core pages tied to that mapping. Among other things, this tree allows the memory management code to quickly find pages which are dirty or under writeback.
커널 데이터 구조에서 일반적인 것처럼 기수를 선언하고 초기화하는 두 가지 모드가 있습니다.
As is typical with kernel data structures, there are two modes for declaring and initializing radix trees:
첫번째 형식은 지정된 이름으로 radix tree를 선언하고 초기화합니다. 두번째 형식은 런타임에 초기화를 수행합니다. 두 경우 모두 메모리 할당이 수행되는 방법을 코드에 알려주기 위해 gfp_mask 를 제공해야합니다. radix tree 연산 (특히 삽입)이 원자적 컨텍스트에서 수행되는 경우, 주어진 마스크는 GFP_ATOMIC 이어야합니다.
The first form declares and initializes a radix tree with the given name; the second form performs the initialization at run time. In either case, a gfp_mask must be provided to tell the code how memory allocations are to be performed. If radix tree operations (insertions, in particular) are to be performed in atomic context, the given mask should be GFP_ATOMIC.
항목을 추가하고 제거하는 기능은 간단합니다.
The functions for adding and removing entries are straightforward:
int radix_tree_insert(struct radix_tree_root *tree, unsigned long key, void *item); void *radix_tree_delete(struct radix_tree_root *tree, unsigned long key);
radix_tree_insert ()를 호출하면, 지정된 트리에 지정된 항목이 삽입됩니다 (키와 관련된 ). 이 작업에는 메모리 할당이 필요할 수 있습니다. 할당이 실패하면 삽입이 실패하고 반환 값은 -ENOMEM이 됩니다. 이 코드는 기존 항목을 덮어 쓰지 않습니다. 트리에 키가 이미 존재하면 radix_tree_insert () 는 -EEXIST 를 반환 합니다 . 성공시 반환 값은 0입니다. radix_tree_delete () 는 tree에 key 와 연관된 항목을 제거하고, 해당 항목에 대한 포인터를 반환합니다.
A call to radix_tree_insert() will cause the given item to be inserted (associated with key) in the given tree. This operation may require memory allocations; should an allocation fail, the insertion will fail and the return value will be -ENOMEM. The code will refuse to overwrite an existing entry; if key already exists in the tree, radix_tree_insert() will return -EEXIST. On success, the return value is zero. radix_tree_delete() removes the item associated with key from tree, returning a pointer to that item if it was present.
Radix tree에 항목을 넣지 못하는 것이 심각한 문제가 될 수있는 상황이 있습니다. 이러한 상황을 피하기 위해 한 쌍의 특수 기능이 제공됩니다.
There are situations where failure to insert an item into a radix tree can be a significant problem. To help avoid such situations, a pair of specialized functions are provided:
int radix_tree_preload(gfp_t gfp_mask); void radix_tree_preload_end(void);
이 함수는 다음 radix tree 삽입이 실패하지 않도록 보장하기 위해, 주어진 gfp_mask를 사용하여 충분한 메모리를 할당하려고합니다. 할당된 구조는 CPU 당 변수에 저장됩니다. 즉, 호출 기능이 예약하거나 다른 프로세서로 이동할 수 있기 전에 삽입 기능을 수행해야 합니다. 이를 위해, radix_tree_preload () 는 성공적 일 때, preemption이 비활성화 된 상태로 돌아갑니다. 호출자는 결국 radix_tree_preload_end () 를 호출하여 선점권을 다시 활성화해야합니다. 실패하면 -ENOMEM 이 반환되고 선점이 비활성화 되지 않습니다.
This function will attempt to allocate sufficient memory (using the given gfp_mask) to guarantee that the next radix tree insertion cannot fail. The allocated structures are stored in a per-CPU variable, meaning that the calling function must perform the insertion before it can schedule or be moved to a different processor. To that end, radix_tree_preload() will, when successful, return with preemption disabled; the caller must eventually ensure that preemption is enabled again by calling radix_tree_preload_end(). On failure, -ENOMEM is returned and preemption is not disabled.
radix tree 검색은 몇 가지 방법으로 수행 할 수 있습니다.
Radix tree lookups can be done in a few ways:
void *radix_tree_lookup(struct radix_tree_root *tree, unsigned long key); void **radix_tree_lookup_slot(struct radix_tree_root *tree, unsigned long key); unsigned int radix_tree_gang_lookup(struct radix_tree_root *root, void **results, unsigned long first_index, unsigned int max_items);
가장 간단한 형식인 radix_tree_lookup () 은 트리에서 key 를 찾고 연관된 항목을 반환합니다 (실패 할 경우 NULL ). 대신 radix_tree_lookup_slot () 은 항목에 대한 포인터가 있는 슬롯에 대한 포인터를 반환합니다. 호출자는 포인터를 변경하여 새 항목을 키와 연관시킬 수 있습니다. 그러나 항목이 없으면 radix_tree_lookup_slot () 은 슬롯을 만들지 않으므로 radix_tree_insert () 대신 사용할 수 없습니다.
The simplest form, radix_tree_lookup(), looks for key in the tree and returns the associated item (or NULL on failure). radix_tree_lookup_slot() will, instead, return a pointer to the slot holding the pointer to the item. The caller can, then, change the pointer to associate a new item with the key. If the item does not exist, however, radix_tree_lookup_slot() will not create a slot for it, so this function cannot be used in place of radix_tree_insert().
마지막으로, radix_tree_gang_lookup ()을 호출하면 first_index 에서 시작하는 키 값이 오름차순으로 결과의 max_items 항목까지 반환됩니다. 돌려 주어지는 아이템의 수는 요구한 것보다 적을 수 있습니다만, 짧은 반환 (제로 이외)은 트리에 값이 없는 것을 의미하지 않습니다.
Finally, a call to radix_tree_gang_lookup() will return up to max_items items in results, with ascending key values starting at first_index. The number of items returned may be less than requested, but a short return (other than zero) does not imply that there are no more values in the tree.
radix tree 함수는 내부적으로 어떤 종류의 잠금(locking)도 수행하지 않는다는 것을 알아야합니다. 여러 스레드가 트리를 손상시키거나 다른 종류의 불쾌한 경쟁 조건에 빠지지 않도록 하는 것은 호출자의 몫입니다. Nick Piggin은 현재 free 트리 노드를 읽기 위해 copy-update를 사용하는 패치를 배포하고 있습니다. 이 패치는 (1) 결과 포인터가 원자적 컨텍스트에서만 사용되며 (2) 호출 코드가 자체 경쟁 조건 생성을 피하는 한 잠금없이 조회 작업을 수행 할 수 있습니다. 그러나 패치가 병합 될 예정인지 명확하지 않습니다.
One should note that none of the radix tree functions perform any sort of locking internally. It is up to the caller to ensure that multiple threads do not corrupt the tree or get into other sorts of unpleasant race conditions. Nick Piggin currently has a patch circulating which would use read-copy-update to free tree nodes; this patch would allow lookup operations to be performed without locking as long as (1) the resulting pointer is only used in atomic context, and (2) the calling code avoids creating race conditions of its own. It is not clear when that patch might be merged, however.
radix tree 코드는 "태그"라는 기능을 지원합니다. 특정 비트가 트리의 항목에 설정 될 수 있습니다. 태그는 예를 들어 더럽거나(dirty) 쓰기 저장 상태(under write back)인 메모리 페이지를 표시하는 데 사용됩니다. 태그 작업을 위한 API는 다음과 같습니다.
The radix tree code supports a feature called "tags," wherein specific bits can be set on items in the tree. Tags are used, for example, to mark memory pages which are dirty or under writeback. The API for working with tags is:
void *radix_tree_tag_set(struct radix_tree_root *tree, unsigned long key, int tag); void *radix_tree_tag_clear(struct radix_tree_root *tree, unsigned long key, int tag); int radix_tree_tag_get(struct radix_tree_root *tree, unsigned long key, int tag);
radix_tree_tag_set () 은 key에 의해 인덱싱 된 항목에 주어진 태그 를 설정합니다; 존재하지 않는 키에 태그를 설정하려고하면 오류가 발생합니다. 반환 값은 태그가 추가 된 항목에 대한 포인터입니다. 태그는 임의의 정수처럼 보이지만 현재 작성된 코드는 최대 두 개의 태그를 허용합니다. 0 또는 1 이외의 태그 값을 사용하면 일부 바람직하지 않은 위치에서 메모리가 자동으로 손상됩니다; 자신을 경고한다고 생각해보십시오.
radix_tree_tag_set() will set the given tag on the item indexed by key; it is an error to attempt to set a tag on a nonexistent key. The return value will be a pointer to the tagged item. While tag looks like an arbitrary integer, the code as currently written allows for a maximum of two tags. Use of any tag value other than zero or one will silently corrupt memory in some undesirable place; consider yourself warned.
태그는 radix_tree_tag_clear () 로 제거 할 수 있습니다; 다시 한번 얘기하자면, 반환 값은 태그(또는 untag)가 붙은 항목에 대한 포인터입니다. radix_tree_tag_get () 함수는 키에 의해 색인된 항목이 주어진 태그 세트를 가지고 있는지를 검사 할 것이다. key 가 없으면 반환 값은 0이고, key 가 있지만 태그가 설정되어 있지 않으면 -1, 그렇지 않으면 +1입니다. 그러나, 이 기능은 현재 소스 코드에서 주석으로 처리되어 있으므로 트리 코드에서는 이를 사용하지 않습니다.
Tags can be removed with radix_tree_tag_clear(); once again, the return value is a pointer to the (un)tagged item. The function radix_tree_tag_get() will check whether the item indexed by key has the given tag set; the return value is zero if key is not present, -1 if key is present but tag is not set, and +1 otherwise. This function is currently commented out in the source, however, since no in-tree code uses it.
태그를 쿼리하는 데는 두 가지 다른 기능이 있습니다.
There are two other functions for querying tags:
int radix_tree_tagged(struct radix_tree_root *tree, int tag); unsigned int radix_tree_gang_lookup_tag(struct radix_tree_root *tree, void **results, unsigned long first_index, unsigned int max_items, int tag);
radix_tree_tagged () 는 트리의 항목에 주어진 태그가 있을 경우 0이 아닌 값을 반환합니다. 주어진 태그가 있는 항목의 목록은 radix_tree_gang_lookup_tag ()를 통해 얻을 수 있습니다.
radix_tree_tagged() returns a non-zero value if any item in the tree bears the given tag. A list of items with a given tag can be obtained with radix_tree_gang_lookup_tag().
결론적으로, radix tree API의 또 다른 흥미로운 점은 radix tree를 파기 할 수있는 기능이 없다는 것입니다. 분명히 radix tree가 영원히 지속될 것이라고 가정합니다. 실제로는, radix tree에서 모든 항목을 삭제하면 루트 노드 이외의 모든 메모리가 해제되어 정상적으로 처리 될 수 있습니다.
In concluding, we can note one other interesting aspect of the radix tree API: there is no function for destroying a radix tree. It is, evidently, assumed that radix trees will last forever. In practice, deleting all items from a radix tree will free all memory associated with it other than the root node, which can then be disposed of normally.