Linux APUE学习:1、文件I/O操作

文件源代码:https://github.com/RoxyKko/APUE.git

image-20230308215731466

文件描述符

stdout.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <stdio.h>
#include <unistd.h>
#include <string.h>

#define MSG_STR "Hello World\n"

int main(int main, char *argv[])
{
printf("%s", MSG_STR);
// fputs 写入什么 写入到哪
fputs(MSG_STR, stdout);
// write 写入(在哪写入, 写入什么, 写入多少)
write(STDOUT_FILENO, MSG_STR, strlen(MSG_STR));

return 0;
}

image-20230225211326219

文件I/O操作

file_io.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
#include <stdio.h>
#include <errno.h>
// write 和 read close都在 unistd.h下
#include <unistd.h>
// memset的头文件
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#define BUFSIZE 1024
#define MSG_STR "Hello World\n"

int main(int argc, char *argv[])
{
// 文件句柄,正常应该为非负的整数
int fd = -1;
// 返回值return value
int rv = -1;
char buf[BUFSIZE];

// open返回一个文件描述符保存在fd中
fd = open("test.txt", O_RDWR|O_CREAT|O_TRUNC, 0666);
if(fd < 0)
{
/* perror()函数可以打印系统调用出错的具体原因,其参数只能跟一个字符串提示符 */
perror("Open/Create file test.txt failure");
return 0;
}
printf("Open file returned file descriptor [%d]\n", fd);
// write参数 写到哪里 写什么 写的大小
// 用strlen是因为MSG_STR是宏定义,宏定义是地址
if( (rv=write(fd, MSG_STR, strlen(MSG_STR))) < 0 )
{
printf("Write %d bytes into failure: %s\n", rv, strerror(errno));
// 疑问?:与其使用goto为什么不封装直接一个函数close和return呢
goto cleanup;
}

/*
程序中定义的buf是局部变量存放在栈中,而栈中的数据是随机数据,所以这里会显示两个乱码字符。
所以在使用局部变量之前,我们都应该对其进行初始化。
*/
// 初始化数组,在读数组前要先memset清空数组
/******************!!!只要是栈中的数据都应该初始化!!!****************************/
lseek(fd, 0, SEEK_SET);
// close(fd);
memset(buf, 0, sizeof(buf));
// read的返回值是你实际读取了多少字节
if( (rv=read(fd, buf, sizeof(buf))) < 0)
{
printf("Read data from file failure : %s\n", strerror(errno));
goto cleanup;
}

// 疑问?:rv的值一直是0,但test.txt里是有成功写入的,为什么?
// 解决:在Hello World写入后,文件偏移量(类似光标)位于World的后方,读或写入时从光标所在位置开始读写
// 所以是读不到值的,解决方法是写入后先close文件再读或者使用leeek(fd, 0, SEEK_SET),两种方法都将文件偏移量设置到文件开始的第一个字节上
printf("Read %d bytes data from file: %s\n", rv, buf);

cleanup:
close(fd);
// 非main函数return只会导致函数退出
// 而main函数的return会调用exit(),导致整个进程中止
// 同理,其他非main函数调用exit()一样会导致整个进程中止
return 0;
}

image-20230225211345083

image-20230225215855084

dup() 和 dup2()系统调用

常用在标准输入、标准输出、标准出错重定向。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>

int main(int argc, char *argv[])
{
int fd = -1;
fd = open("std.txt", O_RDWR|O_CREAT|O_TRUNC, 0666);
if(fd < 0)
{
printf("Open file failure: %s\n", strerror(errno));
return 0;
}

// close(0);
// close(1);
// close(2);

// dup(fd); //0 标准输入
// dup(fd); //1 标准输出
// dup(fd); //2 标准错误输出

dup2(fd, STDIN_FILENO); // 标准输入重定向到fd
dup2(fd, STDOUT_FILENO); // 标准输出重定向到fd
dup2(fd, STDERR_FILENO); // 标准错误输出重定向到fd

printf("fd = %d\n", fd);

close(fd);
return 0;
}

image-20230225231153462

image-20230225231230101

stat()和fstat()系统调用

stat()可以直接使用,fstat()要先open()后使用,两者都是系统调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h> //open()函数
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h> // malloc()

int main(int argc, char *argv[])
{
// struct stat stbuff;
struct stat *stbuff;
// 指针一定要指向一个合法的内存地址,否则会出现段错误
stbuff = malloc(sizeof(struct stat));

// stat("stat.c", &stbuff);
stat("stat.c", stbuff);
// fstat 用法
// int fd = -1;
// fd = open("stat.c", O_RDONLY);
// fstat(fd, stbuff);
printf("File Mode: %o Real Size: %luB, Space Size: %luB\n", stbuff->st_mode, stbuff->st_size, stbuff->st_blksize);
return 0;

}

image-20230225234807364

access()系统调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#include <stdio.h>
#include <unistd.h>

// 改成可执行文件看看结果
#define TEST_FILE "access"

int main(int argc, char *argv[])
{
// 测试文件是否存在
if(access(TEST_FILE, F_OK) != 0)
{
printf("File %s not exist!\n", TEST_FILE);
return 0;
}

printf("File %s exist!\n", TEST_FILE);
// 测试读许可权
if(access(TEST_FILE, R_OK) == 0)
{
printf("READ OK\n");
}
// 测试写许可权
if(access(TEST_FILE, W_OK) == 0)
{
printf("WRITE OK\n");
}
// 测试执行许可权
if(access(TEST_FILE, X_OK) == 0)
{
printf("EXECUTE OK\n");
}

return 0;
}