0.操作系统接口

XV6中文文档 GitHub 修正版 xv6-chinese.pdf来源于https://github.com/ranxian/xv6-chinese 打开后发现他们的pdf编码有问题,用了日文字库,使得字体很奇怪。 xv6-chinese.epub是调整编码为中文后的文件。(epub可用win10 edge浏览器打开) xv6中文文档.pdf是上述epub转换的pdf文件。 立即下载

操作系统的工作是将计算机的资源在多个程序之间共享,并且给程序提供一系列比硬件本身更有用的服务。管理并抽象底层硬件,举例来说,一个文字处理软件(比如word)不用关心自己使用的是何种硬盘。多路复用硬件,使得多个程序可以同时运行。最后,给程序提供一种受控的方式,使得程序之间可以共享数据,共同工作。

操作系统通过接口向用户程序提供服务。设计一个好的接口实际上是很难的。一方面我们希望接口设计得简单和精准,使其易于正确地实现;另一方面,我们可能忍不住想为应用提供一些更复杂的功能。解决这种矛盾的办法是让接口的设计依赖于少量的机制(mechanism),而通过这些机制的组合提供强大、通用的功能。

本书通过xv6操作系统来阐述操作系统的概念,它提供Unix操作系统中的基本接口(由Ken Thompson和Dennis Ritchie引入),同时模仿Unix的内部设计。Unix里机制结合良好的窄接口提供了令人吃惊的通用性。这样的接口设计非常成功,使得包括BSD、LInux、MacOSX\Solaris(甚至Microsoft Windows在某种程度上)都有类似的Unix接口。理解xv6是理解这些操作系统的一个良好起点。

如图,xv6使用了传统的内核概念,一个向其他运行中程序提供服务的特殊程序。每一个运行中程序(称之为进程)都拥有包含指令、数据、栈的内存空间。指令实现了程序的运算,数据用于运算过程的变量,栈管理了程序的过程调用。

进程通过系统调用使用内核服务。系统调用会进入内核,让内核执行服务然后返回。所以进程总是在用户空间和内核空间之间交替运行。

内核使用了CPU的硬件保护机制来保证用户进程只能访问自己的内存空间。内核拥有实现保护机制所需的硬件权限(hardware pricileges),而用户程序没有这些权限。当一个用户程序进行一次系统调用时,硬件会提升特权级并开始执行一些内核中预定义的功能。

内核提供的一系列调用就是用户程序可见的操作系统接口,xv6提供了Unix传统系统调用的一部分,它们是:

 

系统调用描述
fork()创建进程
exit()结束当前进程
wait()等待子进程结束
kill(pid)结束 pid 所指进程
getpid()获得当前进程 pid
sleep(n)睡眠 n 秒
exec(filename, *argv)加载并执行一个文件
sbrk(n)为进程内存空间增加 n 字节
open(filename, flags)打开文件,flags 指定读/写模式
read(fd, buf, n)从文件中读 n 个字节到 buf
write(fd, buf, n)从 buf 中写 n 个字节到文件
close(fd)关闭打开的 fd
dup(fd)复制 fd
pipe( p)创建管道, 并把读和写的 fd 返回到p
chdir(dirname)改变当前目录
mkdir(dirname)创建新的目录
mknod(name, major, minor)创建设备文件
fstat(fd)返回文件信息
link(f1, f2)给 f1 创建一个新名字(f2)
unlink(filename)删除文件

 这一章剩下的部分将说明xv6系统服务的概貌---进程、内存、文件描述符、管道和文件系统,为了描述他们,我们给出了代码和一些讨论。这些系统调用在shell上的应用阐述了它们在设计上是多么独具匠心。

shell是一个普通的程序,它接收用户输入的命令并且执行它们,它也是传统Unix系统中最基本的用户界面。shell作为一个普通程序,而不是内核的一部分,充分说明了系统调用接口的强大:shell并不是也给特别的用户程序。这意味着shell是很容易被替代的,实际上这导致了现代Unix系统有着各种各样的shell,每一个都有自己的用户界面和脚本特性。xv6 shell本质上是一个Unix Bourne shell的简单实现。它的实现在第7850行。

进程和内存

一个xv6进程由两部分组成,一部分是用户内存空间(指令,数据,栈),另一部分是仅对内核可见的进程状态。xv6提供了分时特性:它在可用CPU之间不断切换,决定哪一个等待中的进程被执行。当一个进程不再执行时,xv6保存它的寄存器,当它们再次被执行时恢复这些寄存器的只。内核将每一个进程和一个pid(process identifier)关联起来。

一个进程可以通过系统调用fork来创建一个新的进程。fork创建的新进程被称为子进程,子进程的内容同创建它的进程(父进程)一样。fork函数在父进程,子进程中都返回(一次调用两次返回)。对于父进程返回子进程的pid,对于子进程它返回0.

int pid;
pid = fork();
if(pid > 0){
    printf("parent: child=%d\n", pid);
    pid = wait();
    printf("child %d is done\n", pid);
} else if(pid == 0){
    printf("child: exiting\n");
    exit();
} else {
    printf("fork error\n");
}

系统调用exit会导致调用它的进程停止运行,并且释放诸如内存和打开文件在内的资源。系统调用wait会返回一个当前进程已退出的子进程,如果没有子进程退出,wait会等候直到有一个子进程退出。在上面的例子中,下面两行输出

parent: child=1234
child: exiting

 可能以任意顺序被打印,这种顺序由父进程或子进程谁先结束printf决定。当子进程退出时,父进程的wait也就是返回了,于是父进程打印:

parent: child 1234 is done

 需要留意的是父子进程拥有不同的空间和寄存器,改变一个进程中的变量不会影响另一个进程。

系统调用exec将某个文件(通常是可执行文件)里读取内存镜像,并将其替换到调用它的进程的内存空间。这份文件必须符合特定的格式,规定文件的那一部分是指令,那一部分是数据,那里是指令的开始等等。xv6使用ELF文件格式,第二章将详细介绍它。当exec执行成功后,它并不返回到原来的调用进程,而是从ELF头中声明的入口开始,执行从文件中加载的指令。exec接收两个参数:可执行文件名和一个字符串参数数组。举例来说:

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

 这段代码将调用程序替换为/bin/echo这个程序,这个程序的参数列表为echo hello。大部分的程序都忽略第一个参数,这个参数惯例上是程序的名字(此是echo)

xv6 shell用以上调用为用户执行程序。shell的主要结构很简单那,详见main的代码(8001)。主循环通过getcmd读取命令行的输入,然后它调用fork来生成一个shell进程的副本。父shell调用wait,而子进程执行用户命令。举例来说,用户在命令行输入“echo hello”,getcmd会以echo hello为参数调用runcmd(7906),由runcmd执行实际的命令。对于echo hello,runcmd将调用exec。如果exec调用成功,子进程就会转而取执行echo程序里的指令。在某个时刻echo会调用exit,这会使得其父进程wait返回。你可能会疑惑为什么fork和exec为什么没有被合并为一个调用,我们之后会发现,将创建进程和加载程序分开两个过程是一个非常机智的设计。

xv6没有用户这个概念当然更没有不同用户间的保护隔离措施。按照Unix的术语来说,所有的xv6进程都以root用户执行。

I/O和文件描述符

文件描述符是一个整数,它代表了一个进程可以读写的被内核管理的对象。进程可以通过多种方式获得一个文件描述符,如打开文件、目录、设备,或者创建一个管道(pipe),或者复制已经存在的文件描述符。简单起见,我们常常把文件描述符指向的对象称为“文件”,文件描述符的接口是对文件、管道、设备的抽象,这种抽象使得它们看上去就是字节流。

每个进程都有一张表,而xv6内核就以文件描述符作为这张表的索引,所以每个进程都有一个从0开始的文件描述符空间。按照惯例,进程从文件描述符0读入(标准输入),从文件描述符1输出(标准输出),从文件描述符2输出错误(标准错误输出)。我们会看到shell正是利用了这种惯例来实现IO重定向。shell保证在任何时候都有三个打开的文件描述符(8007),他们是控制台(console)的默认文件描述符。

系统调用read和write从文件描述符所指的文件中读或写n个字节。read(fd,buf,n)从fd读最多n个字节(fd中可能没有n个字节),将它们拷贝到buf中,然后返回读出的字节数。每一个指向文件的文件描述符都和一个偏移关联。read从当前文件偏移处读取数据,然后把偏移增加读出字节数。紧随其后的read会从新的起点开始读数据。当没有数据可读时,read就会但会0,这就表示文件结束了。

write(fd,buf,n)写buf中的n个字节到fd并且返回实际写出的字节数。如果返回值小于n那么只可能是发生了错误。就像read一样,write也从当前文件的偏移处开始写,在写的过程中增加这个偏移。

下面的这段程序(实际上就是cat的本质实现)将数据从标准输入复制到标准输出,如果遇到了错误,它就会在标准错误输出输出一条信息。

char buf[512];
int n;

for(;;){
    n = read(0, buf, sizeof buf);
    if(n == 0)
        break;
    if(n < 0){
        fprintf(2, "read error\n");
        exit();
    }
    if(write(1, buf, n) != n){
        fprintf(2, "write error\n");
        exit();
    }
}

这段代码中值得一提的是cat并不知道它是从文件、控制台或者管道中读取数据的。同样地cat也不知道它是写到文件、控制台或者别的上面地方。文件描述符的使用和一些惯例(如0是标准输入,1是标准输出)使得我们可以轻松实现cat。

 系统调用close会释放一个文件描述符,使得它未来可以被open,pipe,dup等调用重用。一个新分配的文件描述符永远都是当前进程的最小的未被使用的文件描述符。

文件描述符和fork的交叉使用使用IO重定向能够轻易实现。fork会复制父进程的文件描述符和内存,所以子进程和父进程的文件描述符一模一样。exec会替换调用它的进程的内存但是会保留它的文件描述符表。这种行为使得shell可以这样实现重定向:fork一个进程,重新打开指定的文件描述符,然后执行新的代码。下面是一个简化版的shell执行cat<input.txt 的代码:

char *argv[2];
argv[0] = "cat";
### argv[1] = 0;
if(fork() == 0) {
    close(0);
    open("input.txt", O_RDONLY);
    exec("cat", argv);
}

 子进程关闭文件描述符0之后,我们可以保证open会使用0作为新打开的文件Input.txt的文件描述符(因为0是open执行时的最小可用文件描述符)。之后cat就会在标准输入指向input.txt的情况下运行。

xv6的shell正是这样实现IO重定位的(7930)。在shell的代码中,记得这时fork除了子进程,在子进程中runcmd会调用exec加载新的程序。现在你应该很清楚为何fork和exec是单独的两种系统调用了吧。这种区分使得shell可以在子进程执行指定程序之前对子进程进行修改。

虽然fork复制了文件描述符,但每一个文件当前的偏移仍然实在父子进程之间共享的,考虑下面这个例子:

if(fork() == 0) {
    write(1, "hello ", 6);
    exit();
} else {
    wait();
    write(1, "world\n", 6);
}

在这段代码的结尾,绑定在文件描述符1的文件有数据“hello world”,父进程的write会从子进程write结束的地方继续写(因为wait,父进程只在子进程结束之后才运行write)。这种行为有利于执行的shell命令的顺序输出,例如(echo hello;echo world)>output.txt。

dup复制一个已有的文件描述符,返回一个指向同一个输入/输出对象的新描述符。这两个描述符共享同一个文件偏移,正如被fork复制的文件描述符一样。这里有另外一种打印“hello world”的办法:

fd = dup(1);
write(1, "hello", 6);
write(fd, "world\n", 6);

 从同一个原初文件描述符通过一系列fork和dup调用产生的文件描述符都共享同一个文件偏移,而其他情况下产生的文件描述符就不是这样的了,即使它们打开的都是同一个文件。dup允许shell像这样实现命令:ls existing-file non-exsiting-file > tmp1 2 >&1。2>&1告诉shell给这条命令一个复制描述符1的描述符2.。这样existing-file的名字和non-exsiting-file的错误输出都将出现在tmp1中。xv6 shell并未实现标准错误输出的重定向,但现在你知道该怎么去实现它。

文件描述符是一个强大的抽象,因为他们将它们所连接的细节隐藏起来了:一个进程向文件描述符1写出,它有可能是写到一份文件,一个设备(如控制台),或一个管道。

管道

管道是一个小的内核缓冲区,它以文件描述符的形式提供给进程,一个用于写操作,一个用于读操作。从管道的一端写的数据可以从管道的另一端读取。管道提供了一种进程间交互的方式。

接下来的示例代码运行了程序wc,它的标准输出绑定到了一个管道的读端口。

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 {
    write(p[1], "hello world\n", 12);
    close(p[0]);
    close(p[1]);
}

 这段程序调用pipe,创建一个新的管道并且将读写描述符记录在数组p中。在fork之后,父进程和子进程都有了指向管道的文件描述符。子进程将管道的都端口拷贝在描述符0上,关闭p中的描述符,然后执行wc。当wc从标准输入读取时,它实际上是从管道读取的。父进程向管道的写端口写入然后关闭它的两个文件描述符。

如果数据还没有准备好,那么对管道执行的read会一直等待,直到有数据了或者其他绑定在这个管道写端口的描述符都关闭了。在后一种情况中,read会返回0,就像是一份文件读到了最后。读操作会一直阻塞直到不可能再有新的数据到来了,这就是为什么我们执行wc之前要关闭子进程的写端口。如果wc指向了一个管道的写端口,那么wc就永远看不到eof了。

xv6 shell对管道的实现(比如fork sh.c | wc -1)和上面的描述是类似的(7950)。子进程创建一个管道连接管道的左右两端。然后它为管道左右两端都调用runcmd,然后通过两次wait等待左右两端结束。管道右端可能也是一个带有管道的指令,如a|b|c,它fork两个新进程(一个b,一个,c),因此,shell可能要创建一个进程树。树的叶子节点是命令,中间节点是进程,它们会等待左子和右子执行结束。理论上,你可以让中间节点都运行在管道的左端,但做的如此精确会使得实现变得复杂。

pipe可能看上去和临时文件没有什么两样:命令

echo hello world | wc

 可以用无管道的方式实现:

echo hello world > /tmp/xyz; wc < /tmp/xyz

 但管道和临时文件起码有三个关键的不同点。首先,管道会进行自我清扫,如果是shell重定向的话,我们必须要在任务完成后删除/tmp/xyz。第二,管道可以传输任意长度的数据。第三,管道允许同步:两个进程可以使用一对管道来进行二者之间的信息传递,每一个读操作都阻塞调用进程,直到另一个进程用write完成数据的发送。

文件系统

xv6文件系统提供文件和目录,文件就是一个简单的字节数组,而目录包含指向文件和其他目录的引用。xv6把目录实现为一种特殊的文件。目录是一棵树,它的根节点是一个特殊的目录root。/a/b/c指向一个在b目录下的文件c,而b本身又是在目录a中的,a又是处于root目录下的。不从/开始的目录表示的是相对调用进程当前目录的目录,调用进程的当前目录可以通过chdir这个系统调用进行改变。下面这些的代码都打开同一个文件(假设所有涉及到的目录都是存在的)。

chdir("/a");
chdir("b");
open("c", O_RDONLY);

open("/a/b/c", O_RDONLY);

 第一个代码段将当前目录切换到 /a/b; 第二个代码片段则对当前目录不做任何改变。

有很多的系统调用可以创建一个新的文件或者目录:mkdir创建一个新的目录,open加上O_CREATE标志打开一个新的文件,mknod创建一个新的设备文件。mknod创建一个新的设备文件。下面这个例子说明了这三种调用:

mkdir("/dir");
fd = open("/dir/file", O_CREATE|O_WRONGLY);
close(fd);
mknod("/console", 1, 1);

 mknod在文件系统中创建一个文件,但是这个文件没有任何内容。相反,这个文件的元信息标志他是一个设备文件,并且记录主设备号和辅设备号(mknod的两个参数),这两个设备号唯一确定一个内核设备。当一个进程之后打开这个文件时候,内核将读写的系统调用转达到内核设备的实现上,而不是传递给文件系统。

fstat可以获取一个文件描述符指向的文件的信息。它填充一个名为stat的结构体,在stat.h中定义为:
 

#define T_DIR  1
#define T_FILE 2
#define T_DEV  3
// Directory
// File
// Device
     struct stat {
       short type;  // Type of file
       int dev;     // File system’s disk device
       uint ino;    // Inode number
       short nlink; // Number of links to file
       uint size;   // Size of file in bytes
};

 文件名和则会个文件本身是有很大的区别。同一个文件(称为inode)可能有将多个名字,称为连接(links)。系统调用link创建另一个文件系统的名称,它指向同一个inode。下面的代码创建了一个即叫做a又叫做b的新文件。

open("a", O_CREATE|O_WRONGLY);
link("a", "b");

 读写a就相当于读写b。每一个inode都是由一个唯一的inode号直接确定。在上面这段代码中,我们可以通过fstat直到a和b都指向同样的内容:a和b都会返回同样的inode(ino),并且nlink数会设置为2。

系统调用unlink从文件系统移除一个文件名。一个文件的inode和磁盘空间只有当它的连接数变为0的时候才会被清空,也就是没有一个文件再指向它。因此再上面的代码最后加上

unlink("a"),

 我们同样可以通过b访问到它。另外,

fd = open("/tmp/xyz", O_CREATE|O_RDWR);
unlink("/tmp/xyz");

是创建一个临时inode的最佳方式,这个inode会在进程关闭fd或者退出的时候被清空。

xv6关于文件系统的操作都被实现为用户程序,例如mkdir,ln,rm等等。这种设计允许任何人都可以通过命令行扩展shell。现在看起来这种设计是显然的。但是Unix时代的其他系统的设计都将这样的命令内置在了shell中,而shell又是内知道内核中的。

有一个例外,那就是cd,他是shell中实现的(8016)。cd必须改变shell自身的当前工作目录。如果cd作为一个普通命令执行,那么shell就会fork一个子进程,而子进程会运行cd,cd只会改变子进程的当前工作目录。父进程的目录保持原样。

现实情况

UNIX将标准的文件描述符,管道和便于操作他们的shell命令整合在一起,这是编写通用,可重用程序的重大进步。这个想法激发了UNIX强大和流行的软件工具文化,而且shell也是首个所谓的脚本语言。UNIX的系统调用接口在今天仍然存在于许多操作系统中,诸如BSD,Linux,以及MacOSX。

现代内核提供了比xv6要多得多的系统调用和内核服务。最重要的一点,现代基于Unix的操作系统并不遵循早期Unix将设备暴漏为特殊文件的设计,比如刚才说的控制台文件。Unix的作者继续打造Plan9项目,它将“资源是文件”的概念应用到现代设备上,将网络、图形和其他资源都视作文件或者文件树。

 文件系统抽象是一个强大的想法,它被以万维网的形式广泛的应用在互联网资源上。即使如此,也存在着其他的操作系统接口的模型。Multics,一个Unix的前辈,将文件抽象为一种类似内存的概念,产生了十分不同的系统接口。Multics的设计的复杂性对Unix的设计者门产生了直接的影响,他们因此想把文件系统的设计做的更简单。

这本书考察xv6是如何实现类似Unix接口的,但涉及的想法和概念可以运用到Unix之外很多地方上。任何一个操作系统都需要让多个进程复用硬件,实现进程之间的相互隔离,并提供进程间通讯的机制。在学习xv6之后,你应该了解一些其他的更加复杂的操作系统,看看它们之中蕴含的xv6的概念。

xv6中文手册 xv6 book 中文版本 立即下载

相关推荐

xv6-chinese_xv6_xv6教学中文_操作系统_

xv6 是 MIT 开发的一个教学用的完整的类 Unix 操作系统,并且在 MIT 的操作系统课程 6.828 中使用。通过阅读并理解 xv6的代码,可以清楚地了解操作系统中众多核心的概念,对操作系统感兴趣的同学十分推荐一读!这份文档是中文翻译的 MITxv6 文档,是阅读代码过程中非常好的参考资料。

一、MIT xv6的启动和页表设计

MIT 6.S081课程里的关键概念、启动过程源码跟踪、page table讲解。

bottleofcancer的博客 788

xv6-chinese_xv6_MITxv6课程实践_xv6com_

xv6介绍文档中文版,中文的,容易理解。xv6是MIT开发的教学用的完整的类UNIX操作系统。这份文档是中文翻译的xv6文档。

MIT6.S081 Lab1:Xv6 and Unix utilities

这节实验是MIT6.S081的首个实验,主要是构建后续实验运行的环境,以及实现几个简单的小实验,大部分都挺简单的,唯一一个有难度的应该是1.4,主要是理解它的流程有点费劲。

weixin_43724444的博客 1050

【β-Boltzmann探索策略】

𝜀是一个介于0和1之间的参数,用于控制探索和利用的比例。当𝜀较大时,智能体更倾向于探索;当𝜀较小时,智能体更倾向于利用已知信息。

m0_59385870的博客 1083

MATLAB演示元胞自动机算法

一、元胞自动机理论 元胞自动机与格子理论是一个非常好的模型,许多复杂的问题都可以通过它来建立模型,下面就简要介绍一下。 元胞自动机 实质上是定义在一个具有离散、有限状态的元胞组成的元胞空间上,并按照一定的局部规则,在离散的时间维度上演化的动力学系统。 元胞 元胞又可称为单元、细胞,是元胞自动机的最基本的组成部分。 元胞具有以下特点: 元胞自动机最基本的单元。 元胞有记忆贮存状态的功...

frange zone 2万+

【MIT6.S081】xv6学习整理

对xv6初学者的一些建议。希望能通过本文快速入门。面向的对象主要是学习操作系统课程的本科学生。

人生若只如初见 2905

Mediapipe入门——搭建姿态检测模型并实时输出人体关节点3d坐标(2024.1.4更新)

Mediapipe入门,利用mediapipe搭建人体整体检测模型,并输出右手21个关节点的坐标;mp_holistic.Holistic函数理解,mp_holistic.Holistic.process函数理解;mp.solutions.drawing_utils.draw_landmarks函数理解;Mediapipe实时姿态估计,3d骨架铰接图,matplotlib和Mediapipe绘图

kalakalabala的博客 6万+

xv6,MIT开发的教学版操作系统

Xv6是由麻省理工学院(MIT)为操作系统工程的课程(代号6.828),开发的一个教学目的的操作系统。Xv6是在x86处理器上(x即指x86)用ANSI标准C重新实现的Unix第六版(Unix V6,通常直接被称为V6)。官网:http://pdos.csail.mit.edu/6.828/2014/xv6.html 转载于:https://www.cnblogs.com/aqing1987/p/...

weixin_30488313的博客 360

ESD 防护深度解析:模型、等级、系统设计与防护策略

本文系统阐述了静电放电(ESD)防护的核心技术体系。首先对比分析了HBM(人体模型)和CDM(充电设备模型)两种基础ESD模型的物理特性、测试标准及分级要求,指出相同电压下CDM的破坏性是HBM的15-20倍。其次详细解析了IEC61000-4-2系统级ESD标准,包括接触/空气放电测试方法及判定等级。重点提出了系统级防护的三层架构:空间隔离(≥10mm间距)、电气隔离(地平面分割)和TVS管选型(响应时间<1ns),并对比了MOV、SCR等防护器件特性。针对不同应用场景(消费电子、工业控制等)给出了差异化

bother3000的博客 1882

Vivado里LDPC IP核怎么配?手把手教你从6144bit数据到完整参数设置

本文详细介绍了在Vivado中配置LDPC IP核的完整流程,从6144bit数据的参数计算到实际IP核设置。通过分析基本图选择、扩展因子计算和移位参数确定等关键步骤,帮助工程师高效完成LDPC编译码的硬件实现,并提供了时序控制、仿真验证和性能优化的实用技巧。

weixin_30352645的博客 517

操作系统MIT6.S081:启动xv6和第一个进程

命令make qemu ,首先会把kernel/子目录下的源文件编译成可执行文件kernel/kernel(内核编译:Makefile会读取所有kernel下的.c文件。然后调用gcc编译器,生成对应的.s文件,这个是RISC-V汇编语言文件。之后在调用汇编解释器,生成.o文件,这个是汇编语言的二进制格式。最后,系统加载器(Loader)会收集所有的.o文件,将它们链接在一起,并生成内核文件。

weixin_50002674的博客 1878

Spring AI Alibaba实战训练营-13语音生成应用开发指南

本文介绍了如何基于Spring AI Alibaba创建DashScope语音应用,支持文本转语音(TTS)和语音转文本(STT)功能。主要内容包括:1)环境准备要求Java 17+、Maven 3.6+及阿里云DashScope API Key;2)通过Spring Initializr创建项目并配置相关依赖;3)设置API密钥的安全配置方法;4)实现语音合成的同步和流式功能,包括创建控制器类、调用API接口及保存语音文件。文章提供了详细的代码示例和配置说明,帮助开发者快速搭建语音处理应用。

program哲学 401

第0章 操作系统接口

操作系统的工作是(1)将计算机的资源在多个程序间共享,并且给程序提供一系列比硬件本身更有用的服务。(2)管理并抽象底层硬件,举例来说,一个文字处理软件(比如 word)不用去关心自己使用的是何种硬盘。(3)多路复用硬件,使得多个程序可以(至少看起来是)同时运行的。(4)最后,给程序间提供一种受控的交互方式,使得程序之间可以共享数据、共同工作。 操作系统通过接口向用户程序提供服务。设计一个好的接口...

嘿嘿 344

深度解析:LPDDR5 Receiver 的挑战与应对

LPDDR5 的数据速率已经来到Tbit​DataRate1​≈156ps在6400Mbps时也就是说,。👉 这就是为什么成为 LPDDR5 的“标配”。

weixin_43199439的博客 211

RK3399平台开发系列讲解(内核入门篇)1.1、通过sysfs清楚了解设备的系统状况

文章目录1、不敢高声语,恐惊天上人:sysfs目的2、窥一斑而知全豹:sysfs目录介绍3、千丝万缕:sysfs中要素的关系4、数据观星:sysfs中相关结构体 1、不敢高声语,恐惊天上人:sysfs目的 我们想通过sys下面的目录和文件清楚的了解跑Liunx系统的嵌入式设备的系统状况。 2、窥一斑而知全豹:sysfs目录介绍 sysfs把连接在系统上的设备和总线组织成为一个分级的文件,它们可以由用户空间存取,向用户空间导出内核的数据结构以及它们的属性。sysfs的一个目的就是展示设备驱动模型中各组件.

内核笔记 1万+

0x04 操作系统服务和接口

操作系统服务 基本服务 操作系统以服务形式向程序和用户提供执行程序的基本服务,包括用户界面、程序执行、I/O操作、文件系统操作、通信、错误检测等。 增值服务 如资源分配、统计、保护和安全等。 操作系统的服务形式主要有系统调用、用户接口和系统程序。系统调用是操作系统提供给程序的服务形式,而用户接口和系统程序是操作系统提供给用户的服务形式。 系统调用一般用高级语言编写(如C和C++),可以供程序通过应用程序接口(即API)来访问。系统调用在程序中无处不在,各种文件操作、屏幕输出、设备访问都需要调用系统调用。可以

ChanCherry的博客 836

怀旧版电脑GBC模拟器

再现原版GBC掌机的界面,电脑运行连游戏界面都一样大小,很怀旧,很真实,(英文版)强烈推荐,很小的文件,很难才找到的。

xv6课本翻译之——第0章 操作系统接口

Chapter 0 第0章 Operating system interfaces 操作系统接口 The job of an operating system is to share a computer among multiple programs and to provide a more useful set of services than the hardware ...

weixin_30388677的博客 660

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

pscad IEEE标准系统搭建说明

pscad IEEE标准系统搭建说明,包含典型的IEEE 9节点、14节点、30节点、39节点和118节点系统。内含标准测试系统的原始数据和潮流结果。

linux怎么开放USB口,如何在 PetaLinux 及独立操作系统中使用 MPSoC 器件启动 USB2.0 标准接口...

本文介绍在 Vivado 2016.4 - 2017.2 工具中,在 PetaLinux 和独立操作系统下如何使用 MPSoC 器件运行 USB2.0 标准接口。使用 Zynq UltraScale+ MPSoC,该 USB 接口就有两个 USB 2.0/3.0 控制器(USB0 和 USB1)。USB0 和 USB1 都可以在 USB 2.0 中工作,无需使用 USB 3.0 外部 PHY 硬件...

weixin_39613208的博客 440

Outlook Express 6 (OE6) for Win7/Win8/Win10

英文版 Outlook Express6.0, 兼容Win7/Win10/WinXP,解压就可用。

【xv6学习之第0章】操作系统接口

1、fork()函数 一个进程调用 fork() 函数后,系统先给新的进程分配资源,例如存储数据和代码的空间。然后把原来的进程的所有值都复制到新的新进程中,只有少数值与原来的进程的值不同。相当于克隆了一个自己。fork() 函数通过系统调用创建一个与原来进程几乎完全相同的进程,也就是两个进程可以做完全相同的事(即复制了 fork()函数后的代码),但如果初始参数或者传入的变量不同,两个进程也

mick_seu的博客 2818

WSaiOS模块接口标准(MIS v1.0):一种协议驱动的模块化认知操作系统架构

该标准以“协议通信”替代传统的“函数调用”作为模块间交互的基本范式,通过接口唯一性、黑箱原则、状态隔离和协议驱动四大核心原则,构建了一个完全解耦的模块化认知系统架构。MIS v1.0规定了统一的模块接口结构、严格的输入输出契约、标准化的WSCP(WS Cognitive Protocol)通信协议以及清晰的模块分类体系,为构建大规模、可演化、可验证的认知操作系统提供了架构基础。这一分类体系将系统分为“基础设施层”(核心模块)、“认知处理层”(认知模块)和“外部能力层”(能力模块),形成了清晰的层次化架构。

qq_24375721的博客 235

【操作系统基础】系统接口与系统调用

如众多接口一样,接口的存在主要是为使用者提供一种方便易懂的途径使用一件东西(不必搞懂这件东西背后的实现原理),接口屏蔽了其背后的复杂性,提供了方便和简洁的使用方法。操作系统中的接口体现的形式就是函数,函数的存在给使用者提供了方便的调用方法,屏蔽了函数内部实现的细节。系统调用(system call)其中system指的就是操作系统内核(前文【操作系统启动过程】中的system模块),call就如在汇编中call指令一样(跳转到call后面标签指向的程序地址进行执行)。

咸鱼的技术博客 1873

操作系统4---接口以及实现

一,接口 1.接口的定义 用户使用计算机的三种方式: 1.命令行:即通过命令程序,linux系统中常用此种方式 2.图形按钮:通过鼠标点击等操作实现对计算机的操控。windows系统在这方面做的就非常优秀。这种方式通过消息框架程序和消息处理程序实现 3.应用程序 不管采用何种方式,我们都需要让操作系统和应用程序之间建立联系。如何建立连接 ? 操作系统接口 接口其实是一种抽象,比如插排,它将内部的电路全部封装起来,只提供两个插口,用电设备插上就能用;不用管插座内部是如何实现的。 操作系统接口也具有连接两个东西

weixin_43604927的博客 5177

【翻译】XV6-DRAFT as of September 3,2014 第0章 操作系统接口

操作系统接口 操作系统的任务是让多个程序共享计算机(资源),并且提供一系列基于计算机硬件的但更有用的服务。操作系统管理并且把底层的硬件抽象出来,举例来说,一个文字处理软件(例如word)不需要关心计算机使用的是哪种类型的磁盘。操作系统使得硬件可以多路复用,允许许多程序共同使用计算机并且在同一时间上运行。最后,操作系统为程序间的互动提供受控的方法,因此多个程序可以共享数据、协同工作。 计算机操作系统...

weixin_30701521的博客 119
上一篇: 20.环形子数组的最大和(medium)
下一篇: 乘积最大子数组:为什么最大值旁边,还要存一个最小值?
咔_
博客等级 码龄4年 948粉丝 258原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值