有关系统IO的程序解析

#读取文件元数据

应用程序能通过调用stat和fstat函数,检索到关于文件的信息(有时也称为文件的元数据)

函数声明如下

#include <unistd.h>
#include <sys/stat.h>

int stat(const char *filename,struct stat *buf);/*第一个参数为待读取文件的文件名(加了const表示不能修改),第二个参数为struct stat类型的指针,用于指向读取的文件元数据所在的存储单元,若成功则返回0,若出错则返回-1*/

int fstat(int fd,struct stat *buf);//除了第一个参数为待读取文件的文件描述符以外,其他与stat函数相同

stat数据结构中st_mode成员编码了文件的访问许可位和文件类型。

实际应用中,只需将一个对应位为1,其余位为0的数,与st-mode成员做按位与运算。然后根据所得结果的真假,就可以判断文件的各种访问许可情况或文件类型。

利用上述方法,Linux在sys/stat.h中定义了宏谓词来确定st_mode成员的文件类型。如:S_ISREG(m)用于判断是否是一个普通文件,S_ISDIR(m)用于判断是否是一个目录文件等等。

例子1

/* $begin statcheck */
#include "csapp.h"

int main (int argc, char **argv)//主函数,入口参数argc用于存放参数个数,argv表示字符串数组(argv[0]用于存放“./可执行目标文件名”,argv[1]用于存放待读取文件名)
{
    struct stat stat;//用struct stat类型定义一个变量stat
    char *type, *readok;//定义字符串变量type和readok

    /* $end statcheck */
    if (argc != 2) {//如果参数个数不等于2(出错情况,没输入文件名或输入了多个文件名)
	fprintf(stderr, "usage: %s <filename>\n", argv[0]);//向标准出错文件stderr,输出错误信息
	exit(0);//退出
    }
    /* $begin statcheck */
    Stat(argv[1], &stat);//调用Stat函数,用于读取文件argv[1]的原数据,并存放在stat中
    if (S_ISREG(stat.st_mode))//如果是普通文件,将type赋值为regular
	type = "regular";
    else if (S_ISDIR(stat.st_mode))//如果是目录文件,将type赋值为directory
	type = "directory";
    else 
	type = "other";//其他情况,将type赋值为other
	
    if ((stat.st_mode & S_IRUSR))//将stat的st_mode成员与S_IRUSR做按位与运算
	readok = "yes";//结果为真,将readok赋值为yes
    else
	readok = "no";//结果为假,将readok赋值为no

    printf("type: %s, read: %s\n", type, readok);//输出文件类型和当前用户是否可读的信息
    exit(0);
}

其中的S_IRUSR就是一个对应位(st_mode在该位用1表示当前用户可读,用0表示当前用户不可读)为1,其余位全为0的数。将它与st_mode做按位与运算,结果为真表示当前用户可读,结果为假表示当前用户不可读。

运行结果如下:

#其他一些Linux系统调用

1.进程通过调用open函数来打开一个已存在的文件或者创建一个新文件。open函数将filename转换为一个文件描述符,并且返回描述符数字。(返回的描述符总是在进程中当前没有使用的最小描述符)

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int open(char *filename, int flags, mode_t mode);//入口参数filename用于存放待打开文件的文件名,flags用于表示打开文件方式,mode指明了新文件的访问权限位
//若成功则返回新文件描述符,若出错则返回-1

2.进程通过调用close函数关闭一个打开的文件。(关闭一个已关闭的描述符会出错)

#include <unistd.h>

int close(int fd);//入口参数fd表示某个文件描述符,若成功返回0,出错返回-1

3.应用程序通过分别调用read和write函数来执行输入和输出

注意这里size_t被定义为无符号整型,ssize_t被定义为有符号整型(因为出错要返回-1)。这导致读入很大的文件时,返回会出错。

ssize_t read(int fd, void *buf, size_t n);//入口参数用于指明读哪一个文件,buf用于指明读到内存区域的哪一个位置,n用于指明要求读入的字节数
//若成功则返回实际读到的字节数,若读的是空文件则返回0,若出错则返回-1

ssize_t write(int fd, const void *buf, size_t n);//入口参数fd用于指明写哪一个文件,buf用于指明将内存哪块区域写到文件里,n用于指明要求写的字节数
//若成功则返回实际写入的字节数,若出错则返回-1

4.IO重定向:

dup2函数用于复制描述符表表项oldfd到描述符表表项newfd,覆盖newfd以前的内容。(若newfd已经打开,dup2会在复制oldfd前关闭newfd)

dup函数用于产生一个新的文件描述符,它与入口参数fd描述符共享一个打开文件表项。

#include <unistd.h>

int dup(int fd);//函数返回一个新的文件描述符,该描述符与入口参数fd共享一个打开文件表项

int dup2(int oldfd, int newfd);//入口参数newfd为待重定向的文件描述符,oldfd用于指明newfd重定向的位置
//若成功则返回非负的描述符,出错则为-1

例子1:

#include "csapp.h"

int main(int argc, char *argv[])//主函数,入口参数argc用于存放参数个数,argv表示字符串数组(argv[0]用于存放“./可执行目标文件名”,argv[1]用于存放待修改文件名)
{
    int fd1, fd2, fd3;//定义三个整型变量,用于存放文件描述符
    char *fname = argv[1];//定义一个字符指针变量,用于存放文件名字符串
    fd1 = Open(fname, O_CREAT|O_TRUNC|O_RDWR, S_IRUSR|S_IWUSR);/*调用Open函数打开fname文件,返回代表fname文件的一个文件描述符。(入口参数O_CREAT|O_TRUNC|O_RDWR表示如果文件不存在
	就创建一个它的空文件,如果文件已经存在就清空它,以可读可写的方式打开文件;入口参数S_IRUSR|S_IWUSR在创建时生效,制定新文件的访问权限为当前用户可读可写)*/
    Write(fd1, "pqrs", 4);//向空文件写入4个字节“pqrs”	

    fd3 = Open(fname, O_APPEND|O_WRONLY, 0);/*调用Open函数再次打开fname文件,返回代表fname文件的一个新文件描述符。(入口参数O_APPEND|O_WRONLY表示每次写操作前,
	设置文件位置到文件的结尾处,以只写的方式打开文件;入口参数0无意义,因为该参数只有在文件创建时才生效)*/
    Write(fd3, "jklmn", 5);//向文件末尾写入5个字节“jklmn”
    fd2 = dup(fd1); //dup函数返回一个新的文件描述符,它与fd1共享一个打开文件表项
    Write(fd2, "wxyz", 4);//从文件第五个字节开始,写入“wxyz”(fd1与fd2共享一个打开文件表项,所以共享文件位置)
    Write(fd3, "ef", 2);//在文件末尾写上“ef”

    Close(fd1);//关闭fd1
    Close(fd2);//关闭fd2
    Close(fd3);//关闭fd3
    return 0;
}

/*abcde.txt(原数据为abcde)
运行后为pqrswxyznef
*/

这段代码的核心思想就是重定向后fd1与fd2共享了一个打开文件表项,所以共享了文件位置。一开始我以为fd2复制fd1的文件访问方式TRUNC,所以当fd2再次去写文件时,会清空文件内容后再写(也就是结果为wxyzef)。但实际上,fd2和fd1共享的是打开文件表项,文件已被打开,当fd2去写文件时不会再打开再清空一次了。实际情况如下图所示


运行结果如下

发布了25 篇原创文章 · 获赞 6 · 访问量 2000

猜你喜欢

转载自blog.csdn.net/weixin_44711653/article/details/103333140