栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 系统运维 > 运维 > Linux

第一章 操作系统接口(MIT6.S081)

Linux 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

第一章 操作系统接口(MIT6.S081)

第一章 操作系统接口 1.1进程和内存 fork与exec

首先他们都是系统调用的接口

  • fork在父进程中创建了一个子进程,并使用wait在父进程中等待子进程结束,回收。

fork返回值在子进程中为0,父进程中为子进程的pid

int pid=fork();
if (pid>0){
    pid=wait((int*)0);//wait()函数参数的是一个地址,调用后参数是子进程的退出状态
}else if (pid==0){
    exit(0);
}
  • exec有两个参数,可执行文件的文件名与字符串参数数组
char* argv[3];
argv[0]="echo";
argv[1]="hello";
argv[2]=0;
exec("bin/echo",argv);
execfork
内存内容是否相同不同相同
是否覆盖来原有的内存空间是否

1.2 I/O和文件描述符 文件描述符

文件描述符是一个强大的抽象,它在名称上指向文件,实际上可以指向任何I/O对象,即也可以指向设备和或者管道,在下面的描述中默认这一点。

write和read

read和write都是系统调用的接口,以字节为单位读取文件(通过文件描述符)。需要注意一点,buf本质上是指针,第二个参数需要传入指针。

write(fd,buf,n) //fd为文件描述符,
read(fd,buf,n)//buf为读或者写的字符串
  1. 引用文件的每个文件描述符都有一个与之关联的偏移量,且每次read和write都从此偏移量处开始读取或者写入。

  2. 每个进程都有一个文件描述符的私有空间,里面有三个从零开始的打开的文件描述符。进程从文件描述符0(标准输入input)读取,将输出写入文件描述符1(标准输出output),并将错误写入文件描述符2(标准错误)。

  3. 当文件没有更多的字节可读时,read返回0。读取或者写入正常的情况下,read和write返回读取或者写入的字节数。(说明这一条是为了理解下面这这一段代码)

char buf[512];
int n;
for(;;){
    n=read(0,buf,sizeof(buf));
    if (n==0){
        break;
    }
    if (n<0){
        fprintf(2,"read error");
        exit(1);
    }
    if(write(1,buf,n)!=n){
        fprintf(2,"write error");
        exit(1);
    } 
}
close,open和dup

这三个都是关于文件和文件描述符的系统调用。

  • close释放了一个文件描述符0,使其将来可以被open,dup,pipe重用。
close(0)
  • open将打开文件,并使用编号最小的文件描述符。它有两个参数,第一个是文件名的字符串,第一个是一组flags(表示open后对文件做什么)。
int fd=open("a.txt",O_RDONLY)
宏定义功能说明
O_RDONLYread only
O_WRONLYwrite only
O_RDWRread and write
O_CREATEcreate one if the file does not exist
O_TRUNC截断文件, 会清空文件已有内容, 即保留长度为0
  • dup复制一个现有的文件描述符,生成一个引用同样I/O对象的文件描述符。复制得到文件描述符和原文件描述符共享文件偏移量。(注意一点,共享偏移量是fork和dup独有的,见以下代码)
if (fork()==0){
    write(1,"hello ",6);
    exit(0);
}else{
    wait((int*)0);
    write(1,"worldn",6);
}
int fd=dup(1);
write(1,"hello ",6);
write(fd,"worldn",6);
I/O重定向(以及为什么fork和exec分离)

现在需要将之前的对比表格实现升级

execfork
内存内容是否相同不同相同
是否覆盖来原有的内存空间是否
保留原有的文件表(即文件描述符)是是(准确来说是复制)

exec和fork都保留了文件表,这是实现I/O重定向的前提。

现在看如下代码,I/O重定向的实现。fork和exec之间改变了文件描述符的引用对象,这一过程并没有影响主进程。但如果fork和exec没有分离,那么只能先改变主进程的I/O,forkexec之后再变回来,显得笨拙。

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

1.3 管道 pipe

管道是小型内核缓冲区,提供了一种进程通信方式,它和文件不一样,需要一对文件描述符,一个用于读取,一个用于写入。

下面的实例

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 worldn",12);
    close(p[1]);
}

几点需要注意

  • exec执行的时候,读取使用是文件描述符0(默认为标准读取,此处为管道读取)

  • dup使用的文件描述符是0,即未使用的最小编号文件描述符。

  • 在子进程中,关闭文件描述符p[1]是必要的。因为管道在读取的过程中,当没有数据可用的,会进入等待。如果要结束读取,只有所有写入端的文件描述符都关闭。

1.4 文件系统 chdir,mkdir和mknod

如果代码文件处于根目录中,而我们要打开根目录下/a/b/c的文件。则有两者方法

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

    系统调用chdir会切换当前进程到参数对应的目录。

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

    这样并不会改变进程的当前目录

mkdir是创建一个目录:

mkdir("/dir");
fd=open("/dir/file",O_CREATE | O_WRONLY);

mknod则是创建一个引用设备的特殊文件,其实就是文件的inode(索引节点)

mknod("/console",1,1);
link,inode和fstat

inode可以认为是和link一对多的关系,即同一个底层文件(inode)可以有多个名字(link)。

inode包含了文件状态的信息,我们可以通过系统调用fstat检索文件的信息。使用标准库示例如下:

#include
#include
#include
main()
{
   struct stat buf;
   int fd;
   fd = open("/etc/passwd", O_RDONLY);
   fstat(fd, &buf);
   printf("/etc/passwd file size +%dn ", buf.st_size);
}

这个stat类型的结构体在xv6这个操作系统中被定义为

#define T_DIR 1
#define T_FILE 2
#define T_DEVICE 3

struct stat{
    int dev;// 文件系统的磁盘设备
    uint ino;//Inode编号
    short type;//文件类型
    short nlink;//指向文件的链接数
    uint64 size;//文件字节数
};

现在使用系统调用link:

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

这样文件a和b就指向了同一个inode,此时调用fstat,两者将返回同样的ino(inode编号),nlink为2。

注意一点:
只有当文件的链接数为零且没有文件描述符引用的时候,文件的inode和包含其内容的磁盘才会释放

unlink("a");
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/945899.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号