一.LINUX中缓存类型
1.用户空间缓存机制
1.行缓存:stdin/stdout
2.无缓存:普通文件
3.全缓存 :stderr
行缓存:遇到\n换行符
进程正常终止的时候,从main函数 return
二 linux中文件的类型:共有7种
-:表示普通文件
d: 表示文件夹文件
c: 表示字符设备文件
b: 表示块设备文件
s: 表示socket文件
l:表示链接文件
p: 表示管道文件
三. 标准IO之定位流
fseek:
int fseek(FILE *stream, long offset, int whence);
功能:
定位文件流
参数:
fp: 改变偏移量的文件
offset: 偏移的字节个数
whence: SEEK_SET 定位文件流在文件的开头
SEEK_CUR 定位文件流在文件的当前位置
SEEK_END 定位文件流在文件的末尾
返回值:
成功 返回0
错误 -1 errno被设置
rewind:
rewind(3)
rewind() == fseek (fp, 0L, SEEK_SET);
功能:将文件流定位到文件的开头位置
ftell:
ftell(3)
功能:获取当前文件位置并返回
返回值: 成功 返回文件当前位置
四.系统IO,文件IO标准IO的区别
1.文件IO是linux内核向上提供的接口,都是系统调用函数
2.系统调用函数是内核直接提供的函数,在linux操作系统中有470+
库函数是在系统调用的基础上再次根据不同的需求进行封装的,标准IO都是通过系统调用函数而实现的
可以简单的说明
标准IO:
标准IO是C语言标准库中定义的一组用于输入输出操作的函数,如printf、scanf、fopen、fclose等。
它提供了一种抽象层,使得程序员不必关心底层硬件的细节,就可以进行文件和设备的数据交换。
标准IO通常使用缓冲机制来提高I/O操作的效率。
系统IO:
-
- 系统IO通常指的是操作系统层面的输入输出操作,它可能包括对硬件设备的直接访问。 - 系统IO可能涉及到更底层的接口,如中断、DMA(直接内存访问)等,这些通常由操作系统的设备驱动程序来管理。 - 系统IO可能不使用缓冲,或者使用不同的缓冲策略。
文件IO(File I/O):
文件IO是指对文件进行读写操作的过程,它是I/O操作的一种特例。
文件IO可以通过标准IO函数实现,也可以通过系统调用(如Unix/Linux的read和write系统调用)直接进行。
文件IO操作可能涉及到文件系统的交互,如文件的打开、关闭、定位和同步等。
系统IO
open:
int open(const char *pathname, int flags, mode_t mode);
功能:
参数:
pathname: 文件的路径
flags: 文件打开的标记信息和文件状态信息
三选一
O_RDONLY
O_WRONLY
O_RDWR
文件的创建信息或者文件的状态信息可以或上以上三个宏
O_APPEND:
以追加的方式打开文件, 在每次写操作之前将文件的读写位置定位到文件的末尾
O_CREAT:
如果文件不存在 则创建文件 如果标识信息中有该宏, 则必须指定mode
mode指定了将要创建的文件的权限, mode的权限可能会被进程的umask修改
mode & ~(umask)
O_EXCL:
确信创建文件, 如果和O_CREAT一起指定, 如果要创建的文件存在则报错 EEXIST
O_TRUNC:
如果以写的方式打开的是普通文件且文件存在, 则将文件截断为0
返回值:
成功 文件描述符
错误 -1 errno被设置
read:
ssize_t read(int fd, void *buf, size_t count);
功能:
从文件描述符中读数据
参数:
fd:文件描述符
buf: 保存读到数据
count: 指定读取的数据的个数
返回值:
0 读到文件的末尾
成功:返回读到的字节个数 < count
错误:-1 errno被设置
write(2):
ssize_t write(int fd, const void *buf, size_t count);
功能:从buf指向的缓存中写最多count个字节到fd
参数:fd 要写入的文件描述符
buf 保存要写入的数据
count 数据的个数 字节
返回值:
成功:成功写入的字节个数 有可能 < count
错误:-1 errno被设置
lseek (2) :定位
off_t lseek(int fd, off_t offset, int whence);
功能:
定位文件
参数:
offset:
whence:
SEEK_SET 将文件的偏移设置到offset的位置
SEEK_CUR 将文件的偏移设置到当前位置 + offset的位置
SEEK_END 将文件的偏移设置到文件末尾 + offset的位置
返回值:
成功:返回文件的偏移位置
错误:-1 errno被设置
close(2) :关闭
int close(int fd);
功能:关闭文件描述符
参数:文件描述符
返回值:成功 0
错误 -1 errno被设置
stdin 对应的文件描述符是0
stdout 对应的文件描述符是1
stderr 对应的文件描述符是2
系统错误处理:
errno(3)
errno -l 可以查看错误编号和它对应的错误信息
文件描述符
dup(2)
int dup(int oldfd);
功能:复制文件描述符
参数:oldfd, 指定了源文件描述符
返回值:
成功 新的文件描述符 最小的 未使用的
错误 -1 errno被设置
dup2(2)
int dup2(int oldfd, int newfd); 值-结果参数 传入传出
功能:复制文件描述符
参数:oldfd 指定了源文件描述符
newfd 指定了新的文件描述符, 如果原来已经被打开会关闭
五. 文件和目录(文件夹)
-
文件的状态信息
stat filename
-
文件的属性信息就是文件的元数据
1.文件的元数据和文件的数据都在硬盘存放
大小:4096 字节(B) 一个字节由8个二进制位(bit)组成
硬盘在分配大小是按照块分配的 :一块=512B
文件的类型:
- 普通 d目录文件 c字符文件 b块文件 l 链接文件 p管道文件 s socket文件
硬链接:
所谓的硬链接就是文件的文件名个数
目录文件的硬链接是子目录的个数
目录文件不允许用户构建软连接文件符号链接:
也叫软连接, 存储源文件的路径, 类似快捷方式
系统中会为每一个目录文件提供两个子目录 分别是. 和 … . 就是当前目录的硬链接
… 就是上级目录的硬链接文件的拥有者和所属组
默认创建文件的用户就是文件的拥有者 每个用户都有uid
用户都会有一个默认的用户组 作为文件的所属组 称为gid3.查看文件的元数据
ls -l filename
ls -li filename
第一项:文件inode编号
一个文件只有一个文件编号,如果两个文件编号一样,就说明建立了硬链接
1.软链接的建立
ln -s path name
path:文件或者路径
name:软连接名字
2.硬连接的建立:
ln path name
path:文件或者路径
name:硬连接名字
删除一个硬链接文件的时候, 只是删除了文件的名字, inode节点并没有被删除
4.函数的使用
stat :
int stat(const char *pathname, struct stat *statbuf);
功能:
获取文件的身份信息(元数据)
参数:
pathname:指定文件的名字
statbuf: 值-结果参数, 保存文件身份信息
返回值:
成功 0
错误 -1 errno被设置
getpwuid (3):获取密码
结构体:
struct passwd {
char pw_name; / username */
char pw_passwd; / user password /
uid_t pw_uid; / user ID /
gid_t pw_gid; / group ID */
char pw_gecos; / user information */
char pw_dir; / home directory */
char pw_shell; / shell program */
功能:
找到密码数据库中与uid相匹配的字段
参数:
uid 指定要找的uid, 查找该uid的用户名字
返回值:
成功 返回passwd结构体的指针 NULL 没有匹配的用户信息
错误 NULL
#include <pwd.h>
#include <stdio.h>
int main() {
// 假设我们想要获取 UID 为 1000 的用户信息
uid_t user_id = 1000;
struct passwd *pw;
pw = getpwuid(user_id);
if (pw != NULL) {
printf("Username: %s\n", pw->pw_name);
printf("Password: %s\n", pw->pw_passwd);
printf("User ID: %ld\n", (long)pw->pw_uid);
printf("Group ID: %ld\n", (long)pw->pw_gid);
printf("User Info: %s\n", pw->pw_gecos);
printf("Home directory: %s\n", pw->pw_dir);
printf("Shell: %s\n", pw->pw_shell);
} else {
fprintf(stderr, "User not found with UID: %ld\n", (long)user_id);
}
return 0;
}
getgrnam(3):
功能:
返回与组名相匹配的字段
参数:
name
返回值:
成功 返回group结构体的指针 NULL 没有匹配的用户信息
错误 NULL
组名结构体
struct group {
char gr_name; / group name */
char gr_passwd; / group password /
gid_t gr_gid; / group ID */
char *gr_mem; / NULL-terminated array of pointers
to names of group members */
指针常量和常量指针的区别:
const char *p 常量指针
char const *p 指针常量
1.常量指针:指针本身的值不能被改变的指针。也就是说,一旦指针被初始化之后,你不能让它指向另一个地址。但是,通过这个指针所访问的数据是可以被修改的
2.指针常亮:指针指向的数据是常量,即通过这个指针所访问的数据不能被修改。但是,指针本身的值是可以改变的,也就是说,你可以让这个指针指向另一个不同的常量或数据。
六.文件锁
文件锁分为两种,读锁和写锁
1.访问文件有两种结果
加锁:1.成功,读写文件内容,读写完毕,解锁
2.失败,阻塞等待
加锁函数fcntl
函数来对文件进行锁定
int fcntl(int fd, int cmd, ... /* arg */ );
功能:
操作/控制文件描述符
参数:
fd: 要操作的文件描述符
cmd: 指定了对文件描述符操作的命令
文件的状态信息:
F_GETFL
F_SETFL
建议锁的类型:
F_GETLK 测试锁, 并不实际加锁, 如果可以加锁, 会在l_type中返回F_UNLCK
如果不能加锁, 在l_pid返回持有互斥锁进程PID
F_SETLK 设置锁, 如果锁是互斥锁(写锁), 返回-1, errno被设置 EAGAIN EACCES
F_SETLKW 设置锁, 如果锁是互斥锁, 等待其他进程释放锁
返回值:
成功 0
错误 -1 errno被设置
实例:
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
int main() {
int fd = open("example.txt", O_RDWR); // 打开文件,需要读写权限
if (fd < 0) {
perror("open");
return 1;
}
// 锁定文件
struct flock lock;
lock.l_type = F_WRLCK; // 写锁
lock.l_start = 0; // 锁的起始位置
lock.l_whence = SEEK_SET;
lock.l_len = 0; // 0 表示锁定整个文件
if (fcntl(fd, F_SETLK, &lock) == -1) {
if (errno == EACCES || errno == EAGAIN) {
perror("fcntl: file is locked");
} else {
perror("fcntl");
}
close(fd);
return 1;
}
// ... 对文件进行操作 ...
// 解锁文件
lock.l_type = F_UNLCK;
fcntl(fd, F_SETLK, &lock);
close(fd); // 关闭文件
return 0;
}
读锁又叫共享锁
写锁又叫互斥锁
lock.l_len = 0; // 0 表示锁定整个文件
if (fcntl(fd, F_SETLK, &lock) == -1) {
if (errno == EACCES || errno == EAGAIN) {
perror("fcntl: file is locked");
} else {
perror("fcntl");
}
close(fd);
return 1;
}
// ... 对文件进行操作 ...
// 解锁文件
lock.l_type = F_UNLCK;
fcntl(fd, F_SETLK, &lock);
close(fd); // 关闭文件
return 0;
}
读锁又叫共享锁
写锁又叫互斥锁
操作文件的类型与加锁的类型必须保持一致