目录
1.什么是底层文件I/O
底层I/O指的是与硬件设备之间的直接输入输出操作。这些操作通常涉及文件系统和设备驱动程序,并且可以通过系统调用进行访问,如open()、read()、write()等。底层I/O允许程序直接与硬件设备进行通信,例如磁盘驱动器、网络接口卡、键盘、鼠标等
2.底层文件I/O常用库函数
(通常来说,我们学习一个函数就是从 参数,返回值,函数作用三个方面去学习一个函数。)
在Linux中当我们打开一个程序是一般来说将会打开三个文件,可以理解为终端上的控制台的输入输出,其中0,1,2即为文件描述符。
- 标准输入流:0
- 标准输出流:1
- 标准错误流:2
2.1 write函数
- 参数:
fd
:要写入的文件描述符。buf
:要写入的数据缓冲区。count
:要写入的字节数。
- 返回值:
- 若成功,则返回写入的字节数(非负整数)。
- 若失败,则返回-1,并设置errno。
- 作用:将缓冲区buf的count个字节写入文件描述符相关联的文件中。
#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);
例子
向终端控制台输出Hello world 错误则输出Write error
#include <stdio.h>
#include <unistd.h>
int main()
{
if(writr(1,"Hello world\n",12)!=18)
{
write(2,"Write error.\n",13);
}
exit(0);
}
输出
2.2 read函数
- 参数:
fd
:要读取的文件描述符。buf
:用于存放读取数据的缓冲区。count
:要读取的字节数。
- 返回值:
- 若成功,则返回读取的字节数(非负整数)。
- 若已到达文件末尾,则返回0。
- 若失败,则返回-1,并设置errno。
- 作用:从文件中读取count个字节数存入到buf中。
#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);
例子
读取终端输入并将读到的数据打印在屏幕上
#include <unistd.h>
#include <stdio.h>
int main()
{
char buf[1024];
int ret = read(0,buf,128);
if(ret >1)
write(1,"Read success\n",13);
if(ret == 0)
write(1,"End of file\n",12);
if(ret =-1)
write(1,"Read error\n",10);
write(1,"Read data:",10);
write(1,buf,ret);
exit(0);
}
输出
2.3 open函数
- 参数:
pathname
:要打开的文件路径。flags
:打开文件的方式和标志,可以是以下常量的组合:O_RDONLY
、O_WRONLY
、O_RDWR
、O_CREAT
、O_TRUNC
等。mode
:创建文件时的权限,仅当O_CREAT
被指定时有效。
- 返回值:
- 若成功,则返回文件描述符(非负整数),表示打开的文件。
- 若失败,则返回-1,并设置errno。
- 作用:用于打开文件。
当你使用带有O_CREAT标志的open调用来创建文件时,你必须使用有3个参数格式的open调用第三个参数mode是几个标志按位或后得到的,这些标志在头文件sys/stat.h.中定义,如下所示。
S_IRUSR:读权限,文件属主。
S_IWUSR:写权限,文件属主。
S_IXUSR:执行权限,文件属主。
S_IRGRP:读权限,文件所属组。
S_IWGRP:写权限,文件所属组。
S_IXGRP:执行权限,文件所属组。
S_IROTH:读权限,其他用户。
S_IWOTH:写权限,其他用户。
te:执行权限,其他用户。
#include <fcntl.h>
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
例子
创建一个可读可写文件;
#include <fcntl.h>
#include <unistd.h>
#inc;ude <stdio.h>
int main()
{
int fd = open("test.txt",O_RDWR|O_CREAT,S_IWUSR|S_IRUSR);
write(fd,"Hello world\n",12);
close(fd);
exit(0);
}
输出
2.4 close函数
- 参数:
fd
:要关闭的文件描述符。
- 返回值:
- 若成功,则返回0。
- 若失败,则返回-1,并设置errno。
- 作用:用于关闭一个已打开的文件描述符
#include <unistd.h>
int close(int fd);
例子
#include <fcntl.h>
#include <unistd.h>
#inc;ude <stdio.h>
int main()
{
int fd = open("test.txt",O_RDWR|O_CREAT,S_IWUSR|S_IRUSR);
write(fd,"Hello world\n",12);
close(fd);
exit(0);
}
2.5 lseek函数
- 参数:
fd
:要定位的文件描述符。offset
:偏移量。whence
:起始位置,可以是SEEK_SET
、SEEK_CUR
、SEEK_END
。
- 返回值:
- 若成功,则返回新的文件偏移量。
- 若失败,则返回-1,并设置errno。
- 作用:移动文件描述符的读写位置。
#include <unistd.h>
off_t lseek(int fd, off_t offset, int whence);
例子
当前位置偏移5个字节
#include<fcntl.h>
#include <unistd.h>
#include <stdio.h>
int main()
{
char buf[1024];
int ret;
int fd = open("test.txt",O_RDWR);
lseek(fd,5,SEEK_CUR);
if((ret = read(fd,buf,128))<1)
{
write(2,"Read error\n",11);
}
write(1,"Data read after movement:",25);
write(1,buf,ret);
}
输出
2.6 ioctl函数
- 参数:
fd
:文件描述符。request
:请求代码。...
:根据请求代码可能需要的额外参数。
- 返回值:
- 根据不同的请求代码和操作可能返回不同的值。
- 作用:对设备进行各种控制操作,例如设置设备参数等。
#include <sys/ioctl.h>
int ioctl(int fd, unsigned long request, ...);
例子
获取终端长宽
#include<stdio.h>
#include <unistd.h>
#include <sys/ioctl.h>
int main()
{
struct winsize ws;
if(ioctl(STDOUT_FILENO<TIOCGWINSZ,&WS)== -1)
{
write(2,"Error\n",6);
}
printf("Terminal windows size: %d rows x %d columns\n",ws.ws_row,ws_col);
return 0;
}
输出
2.7 fcntl()函数
- 参数:
fd
:文件描述符。cmd
:命令类型。arg
:根据不同的命令类型可能需要的额外参数。
- 返回值:
- 根据不同的命令类型和操作可能返回不同的值。
- 作用:提供了对文件描述符更加灵活的控制能力,可以用于实现各种高级的文件操作,如非阻塞IO、文件锁、文件描述符的异步通知。暂时先了解
#include <fcntl.h>
int fcntl(int fd, int cmd, ... /* arg */ );
2.8 pread()函数
- 参数和返回值同
read()
函数,但可以指定读取文件的偏移量。 - 作用:类似于read(),但可以指定读取文件的偏移量。
#include <unistd.h>
ssize_t pread(int fd, void *buf, size_t count, off_t offset);
2.9 pwrite()函数
- 参数和返回值同
write()
函数,但可以指定写入文件的偏移量。 - 作用:类似于write(),但可以指定写入文件的偏移量。
#include <unistd.h>
ssize_t pwrite(int fd, const void *buf, size_t count, off_t offset);
3.文件和目录的系统调用
系统调用为文件和目录的创建与维护提供了全面的支持
3.1 chmod函数
#include <sys/stat.h>
int chmod(const char *path, mode_t mode);
- 参数:
path
:要修改权限的文件或目录的路径名。mode
:新的权限掩码,使用八进制表示,如0644。
- 返回值: 返回0表示成功,-1表示失败,并设置errno。
- 功能: 修改文件或目录的权限。
- 使用注意:
- 需要有足够的权限来修改文件或目录的权限。
- 权限掩码应该使用八进制表示法,如0644表示rw-r--r--权限。
demo
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
int main()
{
int ret = chmod("test.txt",S_IWGRP|S_IRUSR|S_IXUSR);
//int ret = chmod("test.txt",0777);
if(ret == -1)
{
printf("error\n");
return 0;
}
}
3.2 chown()函数
#include <sys/types.h>
int chown(const char *path, uid_t owner, gid_t group);
- 参数:
path
:要修改所有者和所属组的文件或目录的路径名。owner
:新的所有者的用户ID。group
:新的所属组的组ID。
- 返回值:
- 成功时返回0,失败时返回-1,并设置errno。
- 功能:
- 修改指定文件或目录的所有者和所属组。
- 使用注意:
- 调用进程需要有足够的权限来修改目标文件或目录的所有者和所属组。
demo
#include <stdio.h>
#include <sys/types.h>
int main()
{
int ret = chown("test.txt",1002,1002);
if(ret == -1)
{
perror("error:");
}
return 0;
}
3.3 unlink函数
#include <sys/stat.h>
int unlink(const char *path);
- 参数:
path
:要删除的文件的路径名。
- 返回值:
- 成功时返回0,失败时返回-1,并设置errno。
- 功能:
- 删除指定路径名的文件。
- 使用注意:
- 删除文件后不可恢复,请谨慎使用。
3.4 link函数
int link(const char *oldpath, const char *newpath);
- 参数:
oldpath
:要创建硬链接的已存在文件的路径名。newpath
:新的硬链接的路径名。
- 返回值:
- 成功时返回0,失败时返回-1,并设置errno。
- 功能:
- 创建一个新的硬链接,使其指向已存在的文件。
- 使用注意:
- 只能用于文件,不能用于目录。
3.5 symlink函数
int symlink(const char *target, const char *linkpath)
- 参数:
target
:符号链接要指向的目标路径。linkpath
:符号链接文件的路径名。
- 返回值:
- 成功时返回0,失败时返回-1,并设置errno。
- 功能:
- 创建一个新的符号链接,使其指向目标路径。
- 使用注意:
- 符号链接是一个特殊的文件,可以指向文件或目录。
demo
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
int main() {
const char *target_path = "/path/to/target"; // 替换为目标路径
const char *link_path = "/path/to/symlink"; // 替换为符号链接路径
// 测试 symlink()
if (symlink(target_path, link_path) == 0) {
printf("Symbolic link created successfully.\n");
} else {
perror("symlink");
exit(EXIT_FAILURE);
}
return 0;
}
3.5 mkdir函数
int mkdir(const char *path, mode_t mode);
- 参数:
path
:要创建的目录的路径名。mode
:新目录的权限。
- 返回值:
- 成功时返回0,失败时返回-1,并设置errno。
- 功能:
- 创建一个新的目录。
- 使用注意:
- 需要有足够的权限来在指定位置创建目录。
demo
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
int main() {
const char *dir_path = "/path/to/testdir"; // 替换为目录路径
// 测试 mkdir()
if (mkdir(dir_path, 0777) == 0) {
printf("Directory created successfully.\n");
} else {
perror("mkdir");
exit(EXIT_FAILURE);
}
return 0;
}
3.6 rmdir函数
int rmdir(const char *path);
- 参数:
path
:要删除的目录的路径名。
- 返回值:
- 成功时返回0,失败时返回-1,并设置errno。
- 功能:
- 删除指定路径名的目录。
- 使用注意:
- 只能删除空目录,非空目录删除会失败。
demo
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
int main() {
const char *dir_path = "/path/to/testdir"; // 替换为目录路径
// 测试 rmdir()
if (rmdir(dir_path) == 0) {
printf("Directory removed successfully.\n");
} else {
perror("rmdir");
exit(EXIT_FAILURE);
}
return 0;
}
3.7 chdir函数
int chdir(const char *path);
- 参数:
path
:要设置为当前工作目录的路径名。
- 返回值:
- 成功时返回0,失败时返回-1,并设置errno。
- 功能:
- 修改当前进程的工作目录。
- 使用注意:
- 需要有足够的权限来访问目标目录。
demo
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
int main() {
const char *dir_path = "/path/to/testdir"; // 替换为目录路径
// 测试 chdir() 和 getcwd()
if (chdir(dir_path) == 0) {
char cwd[1024];
if (getcwd(cwd, sizeof(cwd)) != NULL) {
printf("Current working directory: %s\n", cwd);
} else {
perror("getcwd");
exit(EXIT_FAILURE);
}
} else {
perror("chdir");
exit(EXIT_FAILURE);
}
return 0;
}
3.8 getcwd函数
- 原型:
char *getcwd(char *buf, size_t size);
- 参数:
buf
:存储当前工作目录路径名的缓冲区。size
:缓冲区的大小。
- 返回值:
- 成功时返回指向
buf
的指针,失败时返回NULL,并设置errno。
- 成功时返回指向
- 功能:
- 获取当前工作目录的路径名。
- 使用注意:
- 需要提供足够大的缓冲区来存储路径名,建议使用动态分配的缓冲区来避免溢出。
demo
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main() {
char cwd[1024]; // 定义一个缓冲区来存储当前工作目录的路径名
// 测试 getcwd()
if (getcwd(cwd, sizeof(cwd)) != NULL) {
printf("Current working directory: %s\n", cwd);
} else {
perror("getcwd");
exit(EXIT_FAILURE);
}
return 0;
}