MIT6.S081 xv6: Operating System interface

Processes and memory

system call
系统调用	描述
int fork()	创建一个进程,返回子进程的PID
int exit(int status)	终止当前进程,status传递给wait()。不会返回
int kill(int pid)	终止给定PID的进程,成功返回0,失败返回-1
int getpid()	返回当前进程的PID
int sleep(int n)	睡眠n个时钟周期
int exec(char *file, char *argv[])	通过给定参数加载并执行一个文件;只在错误是返回
char *sbrk(int n)	使进程内存增加n字节,返回新内存的起始地址
int write(int fd, char *buf, int n)	将buf中n字节写入到文件描述符中;返回n
int read(int fd, char *buf, int n)	从文件描述符中读取n字节到buf;返回读取字节数,文件结束为0
int close(int fd)	释放一个文件描述符
int dup(int fd)	返回一个新文件描述符,其引用与fd相同的文件
int pipe(int p[])	创建管道,将读/写文件描述符放置在p[0]和p[1]
int chdir(char *dir)	改变当前目录
int mkdir(char *dir)	创建新目录
int mknod(char *file, int, int)	创建新设备文件
int fstat(int fd, struct stat *st)	将打开的文件的信息放置在*st中
int stat(char *file, struct stat *st)	将命名文件信息放置在*st中
int link(char *file1, char * file2)	为文件file1创建一个新的名称(file2)
int unlink(char *file)	移除一个文件
if(pid > 0){
	printf("parent: child=%d\n", pid);
	pid = wait((int *) 0);
	printf("child %d is done\n", pid);
} else if(pid == 0){
	printf("child: exiting\n");
	exit(0);
} else {
	printf("fork error\n");
}
fork

fork系统调用创建一个新的进程。Fork创建的新进程,称为子进程,其内存内容与调用的进程完全相同,原进程被称为父进程。在父进程和子进程中,fork都会返回。在父进程中,fork返回子进程的PID;在子进程中,fork返回0。

exit

exit系统调用退出调用进程,并释放资源,如内存和打开的文件。exit需要一个整数状态参数,通常0表示成功,1表示失败。

wait

wait系统调用返回当前进程的一个已退出(或被杀死)的子进程的PID,并将该子进程的退出状态码复制到一个地址,该地址由wait参数提供;如果调用者的子进程都没有退出,则wait等待一个子进程退出。如果调用者没有子进程,wait立即返回-1。如果父进程不关心子进程的退出状态,可以传递一个0地址给wait。

虽然子进程最初与父进程拥有相同的内存内容,但父进程和子进程是在不同的内存和不同的寄存器中执行的:改变其中一个进程中的变量不会影响另一个进程

char *argv[3];
argv[0] = "echo";
argv[1] = "hello";
argv[2] = 0;
exec("/bin/echo", argv);
printf("exec error\n");
exec

exec系统调用使用新内存映像来替换进程的内存, 新内存映像从文件系统中的文件中进行读取。这个文件必须有特定的格式,它指定了文件中哪部分存放指令,哪部分是数据,在哪条指令开始,等等。xv6使用ELF格式,第3章将详细讨论。当exec成功时,它并不返回到调用程序;相反,从文件中加载的指令在ELF头声明的入口点开始执行。exec需要两个参数:包含可执行文件的文件名和一个字符串参数数组。
上述代码会执行/bin/echo程序,并将argv数组作为参数。大多数程序都会忽略参数数组的第一个元素,也就是程序名称

I/O and File descriptors

文件描述符是一个小整数,代表一个可由进程读取或写入的内核管理对象。一个进程可以通过打开一个文件、目录、设备,或者通过创建一个管道,或者通过复制一个现有的描述符来获得一个文件描述符。

read

调用read(fd, buf, n)从文件描述符fd中读取不超过n个字节的数据,将它们复制到buf中,并返回读取的字节数。每个引用文件的文件描述符都有一个与之相关的偏移量。读取从当前文件偏移量中读取数据,然后按读取的字节数推进偏移量,随后的读取将返回上次读取之后的数据。当没有更多的字节可读时,读返回零,表示文件的结束。

write

write(fd, buf, n)表示将buf中的n个字节写入文件描述符fd中,并返回写入的字节数。若写入字节数小于n则该次写入发生错误。和read一样,write在当前文件偏移量处写入数据,然后按写入的字节数将偏移量向前推进:每次写入都从上一次写入的地方开始。

close

close系统调用会释放一个文件描述符,使它可以被以后的open、pipe或dup系统调用所重用

如果两个文件描述符是通过一系列的fork和dup调用从同一个原始文件描述符衍生出来的,那么这两个文件描述符共享一个偏移量。否则,文件描述符不共享偏移量,即使它们是由同一个文件的打开调用产生的

Pipes

管道是一个小的内核缓冲区,作为一对文件描述符暴露给进程,一个用于读,一个用于写。将数据写入管道的一端就可以从管道的另一端读取数据。管道为进程提供了一种通信方式。

int p[2];
char *argv[2];
argv[0] = "wc";
argv[1] = 0;
pipe(p);
if(fork() == 0) {
	close(0);
	dup(p[0]);
	close(p[0]);
	close(p[1]);
	exec("/bin/wc", argv);
} else {
	close(p[0]);
	write(p[1], "hello world\n", 12);
	close(p[1]);
}

程序调用pipe,创建一个新的管道,并将读写文件描述符记录在数组p中,经过fork后,父进程和子进程的文件描述符都指向管道。子进程调用close和dup使文件描述符0引用管道的读端,并关闭p中的文件描述符,并调用exec运行wc。当wc从其标准输入端读取时,它将从管道中读取。父进程关闭管道的读端,向管道写入,然后关闭写端。
如果没有数据可用,管道上的read会等待数据被写入,或者等待所有指向写端的文件描述符被关闭;在后一种情况下,读将返回0,就像数据文件的结束一样。事实上,如果没有数据写入,读会无限阻塞,直到新数据不可能到达为止(写端被关闭),这也是子进程在执行上面的wc之前关闭管道的写端很重要的一个原因:如果wc的一个文件描述符仍然引用了管道的写端,那么wc将永远看不到文件的关闭(被自己阻塞)。

File system

#define T_DIR 1 // Directory
#define T_FILE 2 // File
#define T_DEVICE 3 // Device
struct stat {
	int dev; // File system’s disk device
	uint ino; // Inode number
	short type; // Type of file
	short nlink; // Number of links to file
	uint64 size; // Size of file in bytes
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

gzgywh

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

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

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

打赏作者

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

抵扣说明:

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

余额充值