lab1—util
在开始之前有必要先了解一下当我们在命令行中输入的参数如何被传递给程序的。为此我参考了csapp的内容。
当你在命令行中输入了一行命令字符串然后按下enter键后,shell程序首先解析你输入的一行命令字符串,解析程序将你输入的那行字符串存储在一个字符数组argv中,argv数组如下图所示。数组的每一个元素都是你的输入的一部分。然后shell程序根据argv[0]判断这个命令是否是shell内置命令,如果是shell内置命令那么直接执行shell内置的指令,如果不是shell内置命令那么shell程序调用fork()创建一个子进程,然后在这个子进程中调用execve()函数,例如:execve(argv[0], argv, environ),environ类似于argv,如下图所示它里面存储的是环境变量字符串数组。execve函数加载并执行可执行文件argv[0],下面简单说一下加载的过程。
这个加载的过程是通过操作系统中称为加载器的程序来完成的,加载器删除子进程现有的虚拟内存段,并创建一组新的代码、数据、堆和栈段。新的栈和堆段被初始化为零。通过将虚拟地址空间中的页映射(注意,不是直接复制)到可执行文件的页大小的片,新的代码和数据段被初始化为可执行文件的内容。最后,加载器跳转到_start函数的地址,这个函数是在系统目标文件ctrl.o中定义的。_start函数调用函数__libc_start_main,这个函数定义在libc.so中,它初始化执行环境(设置栈,也就是main的参数,将栈设置成如下图所示),调用用户层的main函数,处理main函数的返回值,并在需要的时候把控制返回给内核。
也就是说,我们在命令行中传递的参数依次传递给了shell,shell的命令解析器,execv函数,加载器,_start函数,libc_start_main函数,最后传递给了我们要执行的命令的main函数。
sleep
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
int main(int argc, char *argv[])
{
int i;
const char *str = "sleep: usage: sleep [integer]\n";
if (argc != 2){
write(1, str, strlen(str));
exit(0);
}
i = atoi(argv[1]);
sleep(i);
exit(0);
return 0;
}
从命令行中传递给argv的时间参数是个字符串,根据提示调用atoi函数即可转换为整型。然后调用sleep函数即可。
pingpong
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
int main(int argc, char *argv[])
{
int pfd[2];
int cfd[2];
int pid;
char buff[1];
if (argc != 1){
fprintf(2, "pingpong: usage: pingpong\n", 26);
exit(1);
}
pipe(pfd);
pipe(cfd);
if ((pid = fork()) == 0){
close(pfd[1]);
close(cfd[0]);
if (read(pfd[0], buff, 1) == 1){
printf("%d: received ping\n", getpid());
write<