冬天OS(三十五):安装应用程序

--------------------------------------------------------

安装应用程序

--------------------------------------------------------

我们当然可以在 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);
  1.  8000h == 32768,而我们的文件系统主分区是 40257 个扇区,显然并没有超出文件系统分区的边界!
  2. (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】三个文件的数据就是我们的三个程序,让我们回顾一下这种方法的流程!

 

  1. 将源文件编译为可执行文件(编译链接)
  2. 将可执行文件打包成 tar 包
  3. 将 tar 包用 dd 命令写到磁盘的 8000h 处(相对于主分区)
  4. task_fs 启动之后在 mkfs 中创建 cmd.tar 文件(目的是为了可以通过文件来访问 tar 包)
  5. INIT 函数解 tar 包并创建三个【文件】

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

柔弱胜刚强.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值