--------------------------------------------------------
安装应用程序
--------------------------------------------------------
我们当然可以在 mkfs 的时候创建一个空文件 echo ,然后在外面将 echo 的数据写入 echo,但是有个问题是如果还有个 pwd 文件呢?是不是要修改源码让它再事先创建一个 pwd 文件呢? ... 这样修改源码将是无止尽的,因此如果可以不管有多少程序,都可以打包存放在某个地方,然后操作系统可以挨个解析出里面的所有文件,那将是非常方便的!
·mkfs 节选
/* cmd.tar */
/* make sure it'll not be overwritten by the disk log */
assert(INSTALL_START_SECT + INSTALL_NR_SECTS <
sb.nr_sects - NR_SECTS_FOR_LOG);
// 8000h(32768) 号扇区在 smap 中映射为第几位
// 32768 - 269 == 32499(从数据扇区开始第 32499 个扇区就是 cmd.tar 文件数据)
int bit_offset = INSTALL_START_SECT /* 8000h */ - sb.n_1st_sect + 1;
// 32499 / (512 * 8) == 7 ... 3827(余数)
/// 这次运算说明了:cmd.tar 的数据开始扇区应该映射在 smap 的第 8 个扇区和之后
int bit_off_in_sect = bit_offset % (SECTOR_SIZE * 8);
int bit_left = INSTALL_NR_SECTS; /* 2048 */
int cur_sect = bit_offset / (SECTOR_SIZE * 8); /* 7 */
RD_SECT(ROOT_DEV, 2 + sb.nr_imap_sects + cur_sect);
// 3827 —— 上面的运算中剩下的位组不成一个扇区了,所以就留下来了
// 这里 3827 是位的单位,在一个扇区的位(4096)的范围内
while (bit_left)
{
// 以位为单位,设置其后的位为 1
int byte_off = bit_off_in_sect / 8; /* 3827 / 8 == 478 ... 3 */
fsbuf[byte_off] |= 1 << (bit_off_in_sect % 8);
bit_left--;
bit_off_in_sect++;
// 将一个扇区包含的位数都填充完了就换下一个扇区
// 这里确实会填充完一次扇区
if (bit_off_in_sect == (SECTOR_SIZE * 8)/* 4096 */)
{
WR_SECT(ROOT_DEV, 2 + sb.nr_imap_sects + cur_sect);
cur_sect++;
RD_SECT(ROOT_DEV, 2 + sb.nr_imap_sects + cur_sect);
bit_off_in_sect = 0;
}
}
WR_SECT(ROOT_DEV, 2 + sb.nr_imap_sects + cur_sect);
- 8000h == 32768,而我们的文件系统主分区是 40257 个扇区,显然并没有超出文件系统分区的边界!
- (32768 - 269)/ 2048 = 15 ... 1779 这是问题所在,就说从我们的第 16 个文件(非设备文件)开始,之后的文件创建将不再是以数据区为基准呈 1M 对齐!并且 cmd.tar 的数据部分会在那时候被覆盖(不过那时候已经没用了)!
·untar 函数
void untar(const char *filename)
{
printf("[extract `%s'\n", filename);
int fd = open(filename, O_RDWR);
assert(fd != -1);
char buf[SECTOR_SIZE * 16];
int chunk = sizeof(buf);
while (1)
{
read(fd, buf, SECTOR_SIZE);
if (buf[0] == 0)
break;
struct posix_tar_header *phdr = (struct posix_tar_header *)buf;
// 把 size 字符串转化为 size 数值
char *p = phdr->size;
int f_len = 0;
while (*p)
f_len = (f_len * 8) + (*p++ - '0'); /* octal */
int bytes_left = f_len;
// 创建文件并写入数据
int fdout = open(phdr->name, O_CREAT | O_RDWR);
if (fdout == -1)
{
printf(" failed to extract file: %s\n", phdr->name);
printf(" aborted]\n");
return;
}
printf(" %s (%d bytes)\n", phdr->name, f_len);
while (bytes_left)
{
int iobytes = min(chunk, bytes_left);
read(fd, buf,
((iobytes - 1) / SECTOR_SIZE + 1) * SECTOR_SIZE);
write(fdout, buf, iobytes);
bytes_left -= iobytes;
}
close(fdout);
}
close(fd);
printf(" done]\n");
}
tar 包中文件的排布:
这里有个特点,无论是 tar 包头还是 tar 包中的数据,都是按照 512 字节来对齐的( tar 包数据不够 512 字节的就进行填充),所以为我们的读取提供了方便,只用按照扇区来读并且按照扇区来保存!
验证:
首先看看我们 dd 命令写在磁盘的 tar 包:
可以看到赫然大字 "kernel.bin",之后的 512 字节就是 tar 包头结构了!
我们看到 kernel.bin 文件的大小是八进制的 232513 (十进制的 79179),根据这个,我们通过 019dfe00h + 512 + (79179 / 512 + 1) * 512 == 19f3600h ,也就是下一个文件(echo)的起始位置,我们来看看是不是呢?
看来我们得验证正确无疑,其后的 pwd 也可以使用相同的方法找到!
我们再看看新建文件是否正确:
因为这里新建的三个文件的名字是从压缩包中读进来的,这里名字是我们想要的,说明读取并解 tar 包没有问题!
运行:
OK,至此,【kernel.bin】、【echo】、【pwd】三个文件的数据就是我们的三个程序,让我们回顾一下这种方法的流程!
- 将源文件编译为可执行文件(编译链接)
- 将可执行文件打包成 tar 包
- 将 tar 包用 dd 命令写到磁盘的 8000h 处(相对于主分区)
- task_fs 启动之后在 mkfs 中创建 cmd.tar 文件(目的是为了可以通过文件来访问 tar 包)
- INIT 函数解 tar 包并创建三个【文件】