第十章 系统级I/O

输入/输出(I/O)是在主存外部设备之间拷贝数据的过程。

第一节 Unix I/O

这一节涉及到操作系统的基本抽象之一——文件。也就是说,所有的I/O设备都被模型化为文件,而所有的输入输出都被当做对相应文件的读/写。相关的执行动作如下:

1.打开文件:

应用程序向内核发出请求→要求内核打开相应的文件→内核返回文件描述符

  • 文件描述符:一个小的非负整数,用来在后续对此文件的所有操作中标识这个文件。有三个已经被指定了的如下:

      标准输入——0(STDIN_FILENO)标准输出——1(STDOUT_FILENO)标准错误——2(STDERR_FILENO)

    括号中是常量表示形式,使用时需要加头文件<unistd.h>

也就是说,在Unix生命周期一开始,0、1、2就被占用,以后的open只能从3开始——习题10.1.

在UNIX下还有stdin,stdout,stderr表示同样的含义。

二者的主要区别为:

1.数据类型不同,前者为int类型,后者为FILE*;

2.STDIN_FILENO主要用在read(),write()等中,后者主要用在fread(),fwrite()以f开头。

2.改变当前的文件位置

通常,读,写操作都从当前文件偏移量处开始(也就是文件位置),并使偏移量增加所读写的字节数,可以理解为光标所在的位置。

当打开一个文件的最初时候文件的偏移量为0.

通过seek操作,可以显示的设置文件的当前位置为k。

3.读写文件

(1)读

读操作就是从文件拷贝n>0个字节到存储器,并且改变文件当前位置。(如果当前位置是k,则改变为k+n)

※EOF的来源:

这里有一个一直以来的理解上的误区:文件结尾处没有明确的EOF信号,是当文件当前位置的数值超过了文件大小时,会处罚一个称为end-of-file的条件,能够被应用程序检测到,这就是所谓的EOF信号。

(2)写

写操作是从存储器拷贝n>0个字节到一个文件,然后更新当前文件位置。

4.关闭文件

应用通知内核关闭文件→内核释放文件打开时的数据结构→恢复描述符→释放存储器资源。

第二节 打开和关闭文件

1.open函数

(1)函数定义:

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>int open(char *filename, int flags, mode_t mode);

(2)参数解析:

  • 返回值:类型为int型,返回的是描述符数字,总是在进程中当前没有打开的最小描述符。如果出错,返回值为-1.
  • filename:文件名
  • flags:指明进程打算如何访问这个文件,可以取的值见下:

      O_RDONLY:只读O_WRONLY:只写O_RDWR:可读可写O_CREAT:文件不存在,就创建新文件O_TRUNC:如果文件存在,就截断它O_APPEND:写操作前设置文件位置到结尾处

    这些值可以用连接起来。

  • mode:指定了新文件的访问权限位,符号名称如下:

2.close函数

(1)函数定义:

#include <unistd.h>int close(int fd);

(2)参数解析:

  • 返回值:成功返回0,出错返回-1

关闭一个已经关闭的描述符会出错

  • fd:即文件的描述符。

第三节 读和写文件

1.读 read

(1)函数原型:

#include <unistd.h>ssize_t read(int fd, void *buf, size_t n);

(2)参数解析:

  • 返回值:成功则返回读的字节数,EOF返回0,出错返回-1。返回值为有符号数。
  • fd:文件描述符
  • buf:存储器位置
  • n:最多从当前文件位置拷贝n个字节到存储器位置buf

2.写 write

(1)函数原型:

#include <unistd.h>ssize_t write(int fd, void *buf, size_t n);

(2)参数解析:

  • 返回值:成功则返回写的字节数,出错返回-1。返回值为有符号数。
  • fd:文件描述符
  • buf:存储器位置
  • n:最多从存储器位置buf拷贝n个字节到当前文件位置

需要注意的是,read和write在正常情况下返回值是实际传送的字节数量。

3.通过lseek函数可以显式的修改当前文件的位置

4.不足值

不足值指在某些情况下,read和write传送的字节比应用程序要求的要少,原因如下:

  • 读的时候遇到EOF
  • 从终端读文本行
  • 读和写socket

第四节 用RIO包健壮的读写

RIO,Robust I/O,针对的出现不足值的问题。

1.RIO的无缓冲的输入输出函数。

这些函数的作用是直接在存储器和文件之间传送数据,常适用于网络和二进制数据之间。

rio_readn函数和rio_writen定义:

#include "csapp.h"ssize_t rio_readn(int fd, void *usrbuf, size_t n);
ssize_t rio_writen(int fd, void *usrbuf, size_t n);

参数:

  • fd:文件描述符
  • usrbuf:存储器位置
  • n:传送的字节数
  • 返回值:

      rio_readn成功则返回传送的字节数,EOF为0(一个不足值),出错为-1rio_writen成功则返回传送的字节数,出错为-1,没有不足值。

    2.RIO的带缓冲的输入函数

    可以高效的从文件中读取文本行和二进制数据。

一个概念:一个文本行就是一个由换行符结尾的ASCII码字符序列。

范例:如何统计文本文件中文本行的数量——通过计算换行符。需要用到的函数:

#include "csapp.h"void rio_readinitb(rio_t *rp, int fd);//将描述符fd和地址rp处的一个类型为rio_t的读缓存区联系起来。ssize_t rio_readlineb(rio_t *rp,void *usrbuf, size_t maxlen);//从文件rp中读出一个文本行,包括换行符,拷贝到存储器位置usrbuf,并用空字符结束这个文本行。最多赌到maxlen-1个字节,最后一个给结尾的空字符。
ssize_t rio_readnb(rio_t *rp, void *usrbuf, size_t n);//从文件rp中读取最多n个字符到存储器位置usrbuf中。成功则返回传送的字节数,EOF为0,出错为-1。

【课本代码】

图10-4:
#include "csapp.h"int main(int argc, char **argv)
{int n;rio_t rio;char buf[MAXLINE];Rio_readinitb(&rio, STDIN_FILENO);//连接标准输入和rio地址while((n = Rio_readlineb(&rio, buf, MAXLINE)) != 0) //当成功返回时,将rio中的内容拷贝到存储器位置buf中,最多读maxline-1Rio_writen(STDOUT_FILENO, buf, n);//把存储器位置中的数据拷贝到标注输出中。exit(0);
}

先连接标准输入和地址rio,再根据返回值判断是否成功将rio中的一行内容拷贝到了buf中,如果是再把这一行拷贝到标准输出中,即可实现一次一行的从标准输入拷贝一个文本文件到标准输出。

图10-5:
#define RIO_BUFSIZE 8192
typedef struct {int rio_fd;                /* descriptor for this internal buf */int rio_cnt;               /* unread bytes in internal buf */char *rio_bufptr;          /* next unread byte in internal buf */char rio_buf[RIO_BUFSIZE]; /* internal buffer */
} rio_t;void rio_readinitb(rio_t *rp, int fd)
{rp->rio_fd = fd;  rp->rio_cnt = 0;  rp->rio_bufptr = rp->rio_buf;
}

由代码可以看出,rio_t数据结构的组成部分有文件描述符,缓存区中还没有读过的数值,下一个需要读的字节,文本行。在rio_readinitb函数中,创建了一个读缓存区,把文件描述符赋值,还没有读过的数值是0,下一个要读的字节就是文本行的起始,这代表这个读缓存区是空的。

图10-6:

RIO读程序的核心是rio_read函数

static ssize_t rio_read(rio_t *rp, char *usrbuf, size_t n)
{int cnt;while (rp->rio_cnt <= 0) {  /* 如果缓存区为空,调用read填满它 */rp->rio_cnt = read(rp->rio_fd, rp->rio_buf, sizeof(rp->rio_buf));if (rp->rio_cnt < 0) {if (errno != EINTR) /* 出错返回-1*/return -1;}else if (rp->rio_cnt == 0)  /* EOF返回0 */return 0;else rp->rio_bufptr = rp->rio_buf; /* reset buffer ptr */}/* 一旦缓存区非空,就从读缓存区拷贝n和rp->rio_cnt中较小值个字节到用户缓存区,并且返回拷贝的字节数 */cnt = n;          if (rp->rio_cnt < n)   cnt = rp->rio_cnt;memcpy(usrbuf, rp->rio_bufptr, cnt);rp->rio_bufptr += cnt;rp->rio_cnt -= cnt;return cnt;
}

rio_readnb函数

ssize_t rio_readnb(rio_t *rp, void *usrbuf, size_t n)
{size_t nleft = n;ssize_t nread;char *bufp = usrbuf;while (nleft > 0) {if ((nread = rio_read(rp, bufp, nleft)) < 0) {if (errno == EINTR) nread = 0;      /* 调用read填充 */elsereturn -1;      /* 错误,返回-1 */ } else if (nread == 0)break;              /* EOF */nleft -= nread;bufp += nread;}return (n - nleft);         /* 返回成功传送的字节数*/
}

rio_readlineb函数

ssize_t rio_readlineb(rio_t *rp, void *usrbuf, size_t maxlen)
{int n, rc;char c, *bufp = usrbuf;for (n = 1; n < maxlen; n++) { //最多是maxlen-1个if ((rc = rio_read(rp, &c, 1)) == 1) {*bufp++ = c;if (c == '\n')//找到换行符,就退出break;} else if (rc == 0) {if (n == 1)return 0; /* EOF,并且没有读到数据 */elsebreak;    /* EOF,有数据,出现不足值 */} elsereturn -1;    /* 错误,返回-1 */}*bufp = 0;return n;//返回成功传送的字节数
}

第五节 读取文件元数据

元数据即文件信息,需要用到的函数是stat和fstat。定义如下:

#include <unistd.h>
#include <sys/stat.h>int stat(const char *filename, struct stat *buf);
int fstat(int fd,struct stat *buf);返回值:成功为0,错误为-1

参数:

stat需要输入文件名,而fstat需要输入的是文件描述符。

关于stat数据结构如下图:

需要注意的有两个,st_mode和st_size。

  • st_size:包含文件的字节数大小
  • st_mode:包编码文件访问许可位和文件类型。许可位在第一节提到了,Unix文件类型如下,并有对应的宏指令,含义均为“是xx吗”,这些宏在sys/stat.h中定义:

      普通文件    二进制或文本文件(对内核没差) S_ISREG()目录文件    关于其他文件的信息   S_ISDIR()套接字 通过网络与其他进程通信的文件  S_ISSOCK()

查询和处理一个文件的st_mode位:

#include "csapp.h"int main (int argc, char **argv)
{struct stat stat;char *type, *readok;Stat(argv[1], &stat);//文件选择argv[1],写入一个stat数据结构if (S_ISREG(stat.st_mode))     /* 如果是一个文本文件 */type = "regular";else if (S_ISDIR(stat.st_mode))//如果是一个目录文件type = "directory";else type = "other";if ((stat.st_mode & S_IRUSR)) /* 检查阅读权限 */readok = "yes";elsereadok = "no";printf("type: %s, read: %s\n", type, readok);exit(0);
}

第六节 共享文件

内核用三个相关的数据结构来表示打开的文件:

  • 描述符表
  • 文件表:打开文件的集合是由一张文件表来表示的。
  • v-node表

示例:

典型的无共享的:

描述符1和4指向文件表中不同的表现,进而引用了两个不同的文件。

文件共享:

这里可以看到,描述符1和4指向了文件表中的不同表项,但是引用了同一个文件,关于这种情况书上给了一个实例:同一个filename调用open函数两次,这时描述符是不一样的,文件位置也不一样,但是都是同一个文件。这体现的关键思想是:

每个描述符都有它自己的文件位置 ,所以对不同描述符的读操作可以从文件的不同位置获取数据。

子进程继承父进程的打开文件:

初始状态如图6,只有父进程进行了打开文件,然后子进程会有一个父进程描述符表的副本,因而能够共享相同的打开文件表集合,同时也就共享相同的文件位置。

而由于文件表的性质,关闭一个描述符的时候只会减少相应的文件表表项中的引用计数,内核不会删除这个文件表表项直至引用计数清零,所以要想内核删除相应文件表表项,父子进程都必须关闭它们的描述符。

这几种状况的应用,在10.2和10.3题。

练习10.2中,因为fd1和fd2有独立的文件描述符,它们各自有各自的描述符表、文件表、v-code表,所以它们的读取是各自独立的,最后得值是f;

练习10.3中,Fork是子程序,和父程序共享同一个描述符表、文件表、v-code表,指向相同的文件,所以在子程序执行过后,父程序在其基础上进行,读取下一个字符,是o。

第七节 I/O重定向

I/O重定向操作符: >

ls > foo.txt

这句代码的含义就是使外壳加载和执行ls程序,并且将标准输出重定向到磁盘文件foo.txt。

I/O重定向函数: dup2

函数定义为:

#include <unistd.h>int dup2(int oldfd, int newfd);返回值:成功返回描述符,错误返回-1

这个函数执行的操作是,拷贝描述符表表项oldfd,覆盖描述表表项newfd,如果后者被打开,则在拷贝前关闭它。

例题10.5中,初始情况下fd1和fd2的描述符分别是3和4,所以是两个不同描述符表,指向两个不同的文件,但是由于在读了fd2一个字节之后,将fd1重定向到了fd2,所以此时再读fd1相当于在读fd2,也就是结果是o。

第八节 标准I/O

1.标准I/O库:

ANSI C定义了一组高级输入输出函数,称为标准I/O库,包含:

  • fopen、fclose,打开和关闭文件
  • fread、fwrite,读和写字节
  • fgets、fputs,读和写字符串
  • scanf、printf,复杂的格式化的I/O函数

    2.流——类型为FILE的流是对文件描述符和流缓冲区的抽象

    标准I/O库将一个打开的文件模型化为一个流。

每个ANSI C程序开始的时候都有三个打开的流:stdin、stdout、stderr,对应于标准输入、标准输出和标准错误 (参见第一节笔记),定义如下:

#include <stdio.h>extern FILE *stdin;
extern FILE *stdout;
extern FILE *stderr;

第九节 套接字

网络套接字上最好不要使用标准I/O函数,而是使用RIO函数,原因:

如果没有清楚缓存区,输入函数后面不能接输出函数,输出函数后面也不能接输入函数,而对套接字使用lseek是非法的,打开两个流有很麻烦,所以!在网络套接字上不要使用标准I/O函数来进行输入和输出!

错误处理

附录A中主要讲了这本书中的错误处理方式,有一个方法——错误处理包装函数,这个思想很有意思,相当于给基本函数再套上一层皮,然后run这个皮,发现了错误就终止,完全正确的话就跟没有这层皮一样。

1.错误处理风格

(1)Unix风格

遇到错误后返回-1,并且将全局变量errno设置为指明错误原因的错误代码;

如果成功完成,就返回有用的结果。

(2)Posix风格

返回0表示成功,返回非0表示失败;

有用的结果在传进来的函数参数中。

(3)DNS风格

有两个函数,gethostbyname和gethostbyaddr,失败时返回NULL指针,并设置全局变量h_errno。

(4)错误报告函数

void unix_error(char *msg) /* unix-style error */
{fprintf(stderr, "%s: %s\n", msg, strerror(errno));exit(0);
}
/* $end unixerror */void posix_error(int code, char *msg) /* posix-style error */
{fprintf(stderr, "%s: %s\n", msg, strerror(code));exit(0);
}void dns_error(char *msg) /* dns-style error */
{fprintf(stderr, "%s: DNS error %d\n", msg, h_errno);exit(0);
}void app_error(char *msg) /* application error */
{fprintf(stderr, "%s\n", msg);exit(0);
}

2.错误处理包装函数

Unix风格

成功时返回void,返回错误时包装函数打印一条信息,然后退出。

void Kill(pid_t pid, int signum)
{int rc;if ((rc = kill(pid, signum)) < 0)unix_error("Kill error");
}

Posix风格

成功时返回void,错误返回码中不会包含有用的结果。

void Pthread_detach(pthread_t tid) {int rc;if ((rc = pthread_detach(tid)) != 0)posix_error(rc, "Pthread_detach error");
}

DNS风格

struct hostent *Gethostbyname(const char *name)
{struct hostent *p;if ((p = gethostbyname(name)) == NULL)dns_error("Gethostbyname error");return p;
}

参考资料

1.mjay1234的专栏→http://blog.csdn.net/mjay1234/article/details/7357261

2.《深入理解计算机系统》

学习总结

预计学习时间:4h
实际学习时间:6h

这一章内容从页码上看起来不算多,但是牵扯到一些不熟悉却又至关重要的内容,我花了比想象中更多的时间来通读书上的代码,试图解释每一句的含义,这样做给我对代码的理解有一定的帮助,但对于学以致用来说还是有些吃力。而实践代码一开始忘记了,后来才想起来,导致最后花了很多时间去看,理解的还是有些不够到位。

实践代码

看代码的时候我最先不理解的就是main函数的定义:

int main(int argc, char *argv[]){}

经查阅得知,argc是用来表示在命令行下输入命令时的参数个数,包括指令本身;argv[]是用来取得你输入的参数。针对具体指令分析如下(每一步解释由注释形式给出)。

首先先先看一下涉及到的头文件的用处:

stdio.h 标准输入输出
stdlib.h    C标准函数库
unistd.h    Unix类系统定义符号常量
fcntl.h 定义了很多宏和open,fcntl函数原型
sys/types.h 基本系统数据类型
dirent.h    unix类目录操作的头文件,包含了许多UNIX系统服务的函数原型,例如opendir函数、readdir函数。
termios.h   在Posix规范中定义的标准接口

cp

#include        <stdio.h>//标准输入输出
#include        <stdlib.h>//C标准函数库
#include        <unistd.h>//Unix类系统定义符号常量
#include        <fcntl.h>//定义了很多宏和open,fcntl函数原型#define BUFFERSIZE      4096//定义存储器容量
#define COPYMODE        0644//定义复制的长度void oops(char *, char *);int main(int argc, char *argv[])
{int in_fd, out_fd, n_chars;//三个描述符值char buf[BUFFERSIZE];//存储器位置/*cp的参数有两个,分别是要复制的文件,和目的目录,这样一共应该是有三个操作数所以要先检查argc的值是否为三,如果不是,返回标准错误*/if (argc != 3) {fprintf(stderr, "usage: %s source destination\n", *argv);exit(1);}/*检查cp的第一个参数,要复制的文件,用open打开,in_fd为open返回的描述符如果返回-1,代表打开失败,提示错误*/if ((in_fd = open(argv[1], O_RDONLY)) == -1)oops("Cannot open ", argv[1]);/*检查cp的第二个参数,复制的目的地址,用create在目的地址创建新文件,out_fd为open返回的描述符如果返回-1,代表创建失败,提示错误*/if ((out_fd = creat(argv[2], COPYMODE)) == -1)oops("Cannot creat", argv[2]);/*cp指令的动作就是读取一个文件的内容到存储器,在新的地址创建空白文件,再从存储器将内容写入新文件。这里判断复制是否成功:如果能读取顺利,而读取的位数和写的位数不同,是写错误;如果读取失败,是读错误。*/while ((n_chars = read(in_fd, buf, BUFFERSIZE)) > 0)if (write(out_fd, buf, n_chars) != n_chars)oops("Write error to ", argv[2]);if (n_chars == -1)oops("Read error from ", argv[1]);/*这里执行的是关闭文件的动作,in_fd和out_fd两个文件描述符所指向的文件只要有一个关闭错误,就提示关闭错误。*/if (close(in_fd) == -1 || close(out_fd) == -1)oops("Error closing files", "");
}/*这个是用来输出错误信息的函数*/
void oops(char *s1, char *s2)
{fprintf(stderr, "Error: %s ", s1);perror(s2);//用来将上一个函数发生错误的原因输出到标准设备(stderr)exit(1);
}

ls(1)

#include    <stdio.h>
#include    <sys/types.h>
#include    <dirent.h>void do_ls(char []);int main(int argc, char *argv[])
{/*如果操作数只有1个,表明ls后面没有带参数,默认为当前目录,.表示当前目录。*/if ( argc == 1 )do_ls( "." );/*如果ls后面有参数,就把参数读入argv中。*/elsewhile ( --argc ){printf("%s:\n", *++argv );do_ls( *argv );}return 0;
}/*因为ls和dir功能相近,用dir来实现ls*/
void do_ls( char dirname[] )
{DIR     *dir_ptr;struct dirent   *direntp;/*如果没有指向的那个地址,报错*/if ( ( dir_ptr = opendir( dirname ) ) == NULL )fprintf(stderr,"ls1: cannot open %s\n", dirname);else{/*递归的方式来读取*/while ( ( direntp = readdir( dir_ptr ) ) != NULL )printf("%s\n", direntp->d_name );closedir(dir_ptr);}
}

ls2

ls2前半部分和ls1一样,所不同的只是多出来了一部分,用来显示文件的详细信息,比如用户名,群组名,大小,创建时间,读写权限等。

who

这个代码的思想是,从UTMP_FILE文件中读取想要的信息到存储器中,然后再用标准输出函数打印到屏幕上,最后关闭文件。

#include    <stdio.h>
#include    <stdlib.h>
#include    <utmp.h>
#include    <fcntl.h>
#include    <unistd.h>#define SHOWHOST    int show_info( struct utmp *utbufp )
{printf("%-8.8s", utbufp->ut_name);  printf(" ");                printf("%-8.8s", utbufp->ut_line);  printf(" ");                printf("%10ld", utbufp->ut_time);   printf(" ");
#ifdef  SHOWHOSTprintf("(%s)", utbufp->ut_host);
#endifprintf("\n");               return 0;
}
int main()
{struct utmp  current_record;    int     utmpfd;     int     reclen = sizeof(current_record);/*打开UTMP_FILE读取信息,如果打开失败则输出失败信息。*/if ( (utmpfd = open(UTMP_FILE, O_RDONLY)) == -1 ){perror( UTMP_FILE );    exit(1);}/*读取信息到存储器中,reclen就是是读的字节数,然后再调用函数打印出来。*/while ( read(utmpfd, &current_record, reclen) == reclen )show_info(&current_record);close(utmpfd);return 0;
}

echostate

这个代码是用来检查命令行中的提示符是否显示的,如果显示,输入的命令都可见,不显示则表示输入的命令不可见,具体例子结合setecho代码一起

#include        <stdio.h>
#include        <stdlib.h>
#include        <termios.h>int main()
{struct termios info;int rv;rv = tcgetattr( 0, &info );     /* read values from driver      */if ( rv == -1 ){perror( "tcgetattr");exit(1);}if ( info.c_lflag & ECHO )printf(" echo is on , since its bit is 1\n");elseprintf(" echo is OFF, since its bit is 0\n");return 0;
}

setecho

这个与上面对应,改变echo的状态

#include        <stdio.h>
#include        <stdlib.h>
#include        <termios.h>#define  oops(s,x) { perror(s); exit(x); }int main(int argc, char *argv[])
{struct termios info;if ( argc == 1 ) exit(0);if ( tcgetattr(0,&info) == -1 )oops("tcgettattr", 1);if ( argv[1][0] == 'y' )info.c_lflag |= ECHO ;/*打开提示符*/elseinfo.c_lflag &= ~ECHO ;/*隐藏提示符*/if ( tcsetattr(0,TCSANOW,&info) == -1 )oops("tcsetattr",2);return 0;
}

可以看出来,当echo is on的时候,输入的指令是可见的,当设置为off的时候,输入指令不可见

fileinfo

这个功能用来实现显示文件信息,建立了一个stat数据结构。

先判断命令是否有操作数,有的话才能继续进行下去,如果没有报错就打印出来相关文件信息,报错就用perror将报错信息打印出来。

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>void show_stat_info(char *, struct stat *);int main(int argc, char *argv[])
{struct stat info;        if (argc>1){if( stat(argv[1], &info) != -1 ){show_stat_info( argv[1], &info );return 0;}elseperror(argv[1]);  }return 1;
}
void show_stat_info(char *fname, struct stat *buf)
{printf("   mode: %o\n", buf->st_mode);         printf("  links: %d\n", buf->st_nlink);        printf("   user: %d\n", buf->st_uid);          printf("  group: %d\n", buf->st_gid);          printf("   size: %d\n", (int)buf->st_size);         printf("modtime: %d\n", (int)buf->st_mtime);        printf("   name: %s\n", fname );
}

filesize

用st_size成员来计算文件的字节数大小,先判断是否有错误,没有的话就调用。

#include <stdio.h>
#include <sys/stat.h>int main()
{struct stat infobuf;           if ( stat( "/etc/passwd", &infobuf) == -1 )perror("/etc/passwd");elseprintf(" The size of /etc/passwd is %d\n", infobuf.st_size );
}

spwd

#include    <stdio.h>
#include    <stdlib.h>
#include    <string.h>
#include    <sys/types.h>
#include    <sys/stat.h>
#include    <dirent.h>ino_t   get_inode(char *);
void    printpathto(ino_t);
void    inum_to_name(ino_t , char *, int );int main()
{printpathto( get_inode( "." ) );    putchar('\n');              return 0;
}void printpathto( ino_t this_inode )
{ino_t   my_inode ;char    its_name[BUFSIZ];if ( get_inode("..") != this_inode ){chdir( ".." );              inum_to_name(this_inode,its_name,BUFSIZ);my_inode = get_inode( "." );        printpathto( my_inode );        printf("/%s", its_name );       }
}void inum_to_name(ino_t inode_to_find , char *namebuf, int buflen)
{DIR     *dir_ptr;       struct dirent   *direntp;       dir_ptr = opendir( "." );if ( dir_ptr == NULL ){perror( "." );exit(1);}while ( ( direntp = readdir( dir_ptr ) ) != NULL )if ( direntp->d_ino == inode_to_find ){strncpy( namebuf, direntp->d_name, buflen);namebuf[buflen-1] = '\0';   closedir( dir_ptr );return;}fprintf(stderr, "error looking for inum %d\n", (int) inode_to_find);exit(1);
}ino_t get_inode( char *fname )
{struct stat info;if ( stat( fname , &info ) == -1 ){fprintf(stderr, "Cannot stat ");perror(fname);exit(1);}return info.st_ino;
}

这个代码的功能是列出当前目录:

testioctl

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ioctl.h>int main()
{struct winsize size;if( isatty(STDOUT_FILENO) == 0)exit(1);if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &size) < 0) {perror("ioctl TIOCGWINSZ error");exit(1);}printf("%d rows %d columns\n", size.ws_row, size.ws_col);return 0;
}

转载于:https://www.cnblogs.com/20135202yjx/p/4947272.html

20135202闫佳歆——信息安全系统设计基础第九周学习总结相关推荐

  1. 信息安全系统设计基础第九周学习总结

    第十章 系统级I/O的实践 学习目标: 代码阅读理解: 1.编译运行代码 2.使用man学习理解相关系统调用, 理解参数.返回值的含义 3.会用grep -nr xxx /usr/include 查宏 ...

  2. 20135219洪韶武——信息安全系统设计基础第九周学习总结

    信息安全系统设计基础第九周学习总结 学习时间:7小时 学习内容:课本第十章 一.第十章知识点(1~6) 第一部分:文件的概念及相关操作 1.每个unix文件都是一个m字节的序列:所有I/O设备如网络. ...

  3. 信息安全系统设计基础第一周学习总结

    学习计时:共15小时 读书:1 代码:10 作业:13 博客:1 一.学习目标 1. 能够独立安装Linux操作系统 2. 能够熟练使用Linux系统的基本命令 3. 熟练使用Linux中用户管理命令 ...

  4. 信息安全系统设计基础第二周学习总结

    信息安全系统设计基础第二周学习总结 学习目标 1. 熟悉Linux系统下的开发环境    2. 熟悉vi的基本操作    3. 熟悉gcc编译器的基本原理    4. 熟练使用gcc编译器的常用选项 ...

  5. 信息安全系统设计基础第九周总结

    第十章 系统级I/O 输入操作是从I/O设备拷贝数据到主存,输出操作是从主存拷贝数据到I/O设备 10.1Unix I/O 一个Unix文件就是一个m个字节的序列: 所有的I/O设备都被模型化为文件, ...

  6. 20135304刘世鹏——信息安全系统设计基础第九周总结

    第十章.系统I/O 一个Unix文件就是一个m个字节的序列:B0,B1,-,BK,-,Bm-1 一.unix i/o 596 Unix I/O:一种将设备优雅地映射为文件的方式,允许Unix内核引出一 ...

  7. 20135304刘世鹏——信息安全系统设计基础第一周学习总结

    第一节 Linux系统简介 一.Linux是一个操作系统 1. Linux 本身只是操作系统的内核.内核是使其他程序能够运行的基础. 它实现了多任务和硬件管理,用户或者系统管理员交互运行的所有程序 实 ...

  8. 20135323符运锦---信息安全系统设计基础第一周学习总结

    学习计时:共15小时 读书:1小时 代码:8小时 作业:4小时 博客:2小时 一.学习目标 1. 能够独立安装Linux操作系统   2. 能够熟练使用Linux系统的基本命令   3. 熟练使用Li ...

  9. LINUX信息安全系统设计基础第一周学习总结

     Linux系统简介 一.实验内容 了解 Linux 的历史,Linux 与 Windows 的区别等入门知识. 二.实验要求 阅读linux简介与历史 三.实验步骤 二.Linux 与 Window ...

  10. 20135306-信息安全系统设计基础第一周学习总结

    第二周(9.14-9.20): 学习计时:共14小时 读书:2h 代码:4h 作业:4h 博客:4h 一.学习目标 1. 能够独立安装Linux操作系统 2. 能够熟练使用Linux系统的基本命令 3 ...

最新文章

  1. 正则化技巧:标签平滑(Label Smoothing)以及在 PyTorch 中的实现
  2. JSON数据解析及gson.jar包
  3. oclick vue 传参 函数_详解Vue计算属性和侦听属性
  4. java类验证和装载顺序_Java类的加载机制和双亲委派模型
  5. 近期机器学习竞赛汇总~总奖池超千万人民币!
  6. js文件复制_note
  7. Vmware拓展虚拟机硬盘容量
  8. hdu 5443 The Water Problem 线段树
  9. 局域网内如何设置多个ip地址分配
  10. 继承性(HTML、CSS)
  11. 更改linux主机名称
  12. 2019牛客多校第三场F Planting Trees(单调队列)题解
  13. MacOS Big Sur 11.5 (20G71) OC 0.7.1 / Cl 5138 / PE 三分区原版黑苹果镜像
  14. 超自动化如何提升保险业?
  15. html打印去掉页码和日期,PPT打印讲义时如何去掉日期页码?
  16. 大量图片数据导出为excel导致内存溢出解决方案落地
  17. 机器学习方法:回归(三):最小角回归Least Angle Regression(LARS),forward stagewise selection
  18. CAS操作实际上包含了哪两个步骤?
  19. Java8-Stream流整合(1)
  20. MATLAB—Simulink与Flowmaster联合仿真

热门文章

  1. 华三交换机配置vrrp_h3c vrrp配置实例
  2. 浏览器的“心脏”——内核
  3. IPC之消息队列(Message Queue)
  4. LABjs、RequireJS、SeaJS 哪个最好用?为什么?
  5. 【数据工具】地理坐标拾取器V.1.01(支持WGS-84、GCJ-02、BD-09)
  6. 计算机网络实验二:网络基础编程实验
  7. python实现海康sdk二次开发,移动侦测事件(一)
  8. 前端面试题—2021年web前端开发面试题
  9. 计算机变异指标可以,变异系数公式_在EXCEL中怎样计算样本标准差和变异系数
  10. IPv4与IPv6地址转换