内容:常用、进阶命令汇总,含解释和例程
注意:此部分不包括标准I/O库的函数介绍,标准I/O库见下一篇
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main(void) {
char buff[1024];
int fd1, fd2;
int ret;
/*轴对称开关*/
/* 打开源文件 src_file(只读方式) */
fd1 = open("./src_file", O_RDONLY);
if (-1 == fd1)
return fd1;
/* 打开目标文件 dest_file(只写方式) */
fd2 = open("./dest_file", O_WRONLY);
if (-1 == fd2) {
ret = fd2;
goto out1;
}
/* 读取源文件 1KB 数据到 buff 中 */
ret = read(fd1, buff, sizeof(buff));
if (-1 == ret)
goto out2;
/* 将 buff 中的数据写入目标文件 */
ret = write(fd2, buff, sizeof(buff));
if (-1 == ret)
goto out2;
ret = 0;
out2:
/* 关闭目标文件 */
close(fd2);
out1:
/* 关闭源文件 */
close(fd1);
return ret; }
文件描述符
Tips:Linux 系统下,一切皆文件,也包括各种硬件设备,使用 open 函数打开任何文件成功情况下便会返回对应的文件描述符 fd。每一个硬件设备都会对应于 Linux 系统下的某一个文件,把这类文件称为设备文件。所以设备文件对应的其实是某一硬件设备,应用程序通过对设备文件进行读写等操作、来使用、操控硬件设备,
在linux下可以通过man手册查看各类linux提供的应用函数的用法
open
int open ( const char * pathname , int flags );
![](https://i-blog.csdnimg.cn/blog_migrate/5e9430023da87fbf8081ac94547e26c9.png)
Tips:man 命令后面跟着两个参数,数字 2 表示系统调用,man 命令除了可以查看系统调用的帮助信息外,还可以查看Linux 命令(对应数字 1)以及标准 C 库函数(对应数字 3)所对应的帮助信息;最后一个参数 open 表示需要查看的系统调用函数名。
write
ssize_t write(int fd, const void *buf, size_t count);fd : 文件描述符。关于文件描述符,前面已经给大家进行了简单地讲解,这里不再重述!我们需要将进行写操作的文件所对应的文件描述符传递给 write 函数。buf : 指定写入数据对应的缓冲区。count : 指定写入的字节数。
read
ssize_t read(int fd, void *buf, size_t count);fd : 文件描述符。与 write 函数的 fd 参数意义相同。buf : 指定用于存储读取数据的缓冲区。count : 指定需要读取的字节数。
close
int close(int fd);fd : 文件描述符,需要关闭的文件所对应的文件描述符。
lseek(重要,偏移量记录)man 2 lseek
off_t lseek(int fd, off_t offset, int whence);
fd:文件描述符。
offset : 偏移量,以字节为单位。whence : 用于定义参数 offset 偏移量对应的参考值返回值: 成功将返回从文件头部开始算起的位置偏移量(字节为单位),也就是当前的读写位置;发生错误将返回-1 。
SEEK_SET:读写偏移量将指向 offset 字节位置处(从文件头部开始算);
SEEK_CUR:读写偏移量将指向当前位置偏移量 + offset 字节位置处,offset 可以为正、也可以为负,如果是正数表示往后偏移,如果是负数则表示往前偏移;
SEEK_CUR:读写偏移量将指向当前位置偏移量 + offset 字节位置处,offset 可以为正、也可以为 负,如果是正数表示往后偏移,如果是负数则表示往前偏移;
=========================================================================
I/O进阶
之前是简单的打开 读 写 关闭
静态文件
文件存放在磁盘文件系统中,称为静态文件,硬盘的最小存储单位叫做“扇区”,每个扇区储存 512 字节
动态文件
静态文件以块为单位操作,不方便 动态文件对内存操作,以字节为单位
返回错误处理与 errno
#include <stdio.h>
#include <errno.h>
int main(void)
{
printf("%d\n", errno);
return 0;
}
#include <string.h>char *strerror(int errnum);errnum : 错误编号 errno 。返回值: 对应错误编号的字符串描述信息
/* 打开文件 */
fd = open("./test_file", O_RDONLY);
if (-1 == fd) {
printf("Error: %s\n", strerror(errno));
return -1;
}
perror 函数 (查看具体的错误类型)(man 3 perror)
#include <stdio.h>void perror(const char *s);s : 在错误提示字符串信息之前,可加入自己的打印信息,也可不加,不加则传入空字符串即可。返回值: void 无返回值。
/* 打开文件 */
fd = open("./test_file", O_RDONLY);
if (-1 == fd) {
perror("open error");
return -1;
}
exit、_exit、_Exit
#include <unistd.h>void _exit(int status);eg:_exit(0);
exit()函数 (C库函数)
空洞文件(预先申请好地址,就可以从多个地址头中实现多线程操作)
在使用迅雷下载文件时,还未下载完成,就发现该文件已经占据了全部文件大小的空间,这也是空洞文件;下载时如果没有空洞文件,多线程下载时文件就只能从一个地方写入,这就不能发挥多线程的作用了;如果有了空洞文件,可以从不同的地址同时写入,就达到了多线程的优势;
O_APPEND 和 O_TRUNC 标志
复制文件描述符
dup 函数 (man 2 dup)
#include <unistd.h>int dup(int oldfd);oldfd : 需要被复制的文件描述符。返回值: 成功时将返回一个新的文件描述符,由操作系统分配,分配置原则遵循文件描述符分配原则; 如果复制失败将返回-1 ,并且会设置 errno 值。
/* 复制文件描述符 */
fd2 = dup(fd1);
if (-1 == fd2) {
perror("dup error");
ret = -1;
goto err1;
}
printf("fd1: %d\nfd2: %d\n", fd1, fd2);
#include <unistd.h>int dup2(int oldfd, int newfd);oldfd : 需要被复制的文件描述符。newfd : 指定一个文件描述符(需要指定一个当前进程没有使用到的文件描述符)。返回值: 成功时将返回一个新的文件描述符,也就是手动指定的文件描述符 newfd ;如果复制失败将返回 -1 ,并且会设置 errno 值。
/* 复制文件描述符 */
fd2 = dup2(fd1, 100);
if (-1 == fd2) {
perror("dup error");
ret = -1;
goto err1;
}
printf("fd1: %d\nfd2: %d\n", fd1, fd2);
原子操作与竞争冒险
线程A、B都对同一个文件操作,就会出问题。原子操作可以保护文件
1)O_APPEND 实现原子操作
2)pread()和 pwrite()(重要)
pread()和 pwrite()都是系统调用,与 read()、write()函数的作用一样,用于读取和写入数据。区别在于, pread()和 pwrite()可用于实现原子操作,调用 pread 函数或 pwrite 函数可传入一个位置偏移量 offset 参数, 用于指定文件当前读或写的位置偏移量,所以调用 pread 相当于调用 lseek 后再调用 read;同理,调用 pwrite 相当于调用 lseek 后再调用 write。所以可知,使用 pread 或pwrite 函数不需要使用 lseek 来调整当前位置偏移量,并会将“移动当前位置偏移量、读或写”这两步操作组成一个原子操作。
#include <unistd.h>ssize_t pread(int fd, void *buf, size_t count, off_t offset);ssize_t pwrite(int fd, const void *buf, size_t count, off_t offset);fd 、 buf 、 count 参数与 read 或 write 函数意义相同。offset : 表示当前需要进行读或写的位置偏移量。返回值: 返回值与 read 、 write 函数返回值意义一样。虽然 pread (或 pwrite )函数相当于 lseek 与 pread (或 pwrite )函数的集合,但还是有下列区别:⚫ 调用 pread 函数时,无法中断其定位和读操作(也就是原子操作);⚫ 不更新文件表中的当前位置偏移量。