linux应用编程:文件I/O (非缓冲)

一、文件I/O描述符

  • 内核的一个重要功能就是文件管理,系统有非常多的文件,内核怎样认识每一个文件呢?------ 内核采用ID号的方式来标识这些文件:inode号。(节点号)
  • 那么这些内核的文件的ID号,在每个用户的程序中是怎样映射的呢? ------即:文件描述符。所以,文件描述符是指在一个进程中用于区分内核空间中不同的文件。
  • 文件描述符是一个非负整数(0~OPEN_MAX-1),即0~1023。
  • 在shell中,描述符 STDIN_FILENO(0):标准输入,STDOUT_FILENO(1):标准输出,STDERR_FILENO(2):标准错误。

二、文件I/O操作函数—基础

2.1 open & creat
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
int creat(const char *pathname, mode_t mode);

  • creat 函数等价于 open(pathname,O_CREAT|O_TRUNC|O_WRONLY,mode);
  • pathname:文件路径名
  • flags
    • O_RDONLY 以只读的方式打开
    • O_WRONLY 以只写的方式打开
    • O_RDWR 以读写的方式打开
    • O_CREAT 如果文件不存在,则创建文件
    • O_APPEND 已追加的方式打开文件,每次调用 write 时,文件指针自动先移到文件尾,用于多进程写同一个文件的情况。
    • O_NONBLOCK 非阻塞方式打开,无论有无数据读取或等待,都会立即返回进程之中。
    • O_NODELAY 非阻塞方式打开
    • O_SYNC 同步打开文件,只有在数据被真正写入物理设备设备后才返回
    • O_EXCL 仅与 O_CREAT 连用,如果文件已存在,则强制 open 失败
    • O_SYNC 同步打开文件,只有在数据被真正写入物理设备设备后才返回
    • O_TRUNC 如果文件存在,将文件的长度截至 0
  • mode:类似于chmod 775 filename 这功能,mode就相当于775。
  • 返回值:成功:文件描述符, 失败:-1,并设置errno值。
2.2 read & write
#include <unistd.h>

ssize_t read(int fd, void *buf, size_t count);
ssize_t write(int fd, const void *buf, size_t count);

  • fd:文件描述符
  • buf:读&写的数据缓存区
  • count:需要读&写的长度
  • 返回值:出错时,返回-1;
    • 读成功时,将返回读取的字节数(0表示文件结束),并按此数值设定文件的光标指针位置。如果这个值是小于请求的字节数,可能是因为我们接近文件结尾,或我们正在从管道或终端读取数据,实际可用的字节数较少;也可能因为read()被信号中断。
    • 写成功时,将返回写入的字节数(0表示未写入任何内容)。如果这个数字小于请求的字节数,则不是错误;例如,这可能是因为磁盘设备已满。
  • size_t 为unsigned long类型 ,ssize_t 为long类型,具体查看内核源码相关定义。
2.3 close
#include <unistd.h>

int close(int fd);

  • fd:文件描述符
  • 返回值:0成功, -1失败
2.4 lseek
#include <sys/types.h>
#include <unistd.h>

off_t lseek(int fd, off_t offset, int whence);

  • fd:文件描述符
  • offset:偏移量
  • whence :从哪个位置开始偏移
  • SEEK_SET 从文件头开始计算
  • SEEK_CUR 从当前指针开始计算
  • SEEK_END 从文件尾开始计算
  • 返回值:成功完成后,返回从文件开头开始以字节为单位测量的结果偏移位置。出错时,返回值(off_t)-1。并且errno设置为指示错误。
  • off_t 为long 类型。
int main(int argc,char *argv[])
{
	int fd = open("test.txt", O_WRONLY | O_CREAT);
	lseek(fd, 1024, SEEK_SET);
	write(fd, "a", 1);
	close(fd);
	return 0;
}

在这里插入图片描述
在这里插入图片描述

  • 相当于前面填充了1024个空格字符,然后再加一个字符a,刚好1025个字符
2.5 读写例程
#include <stdio.h>
#include <stdlib.h> //包含 exit
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h> //用 perror 输出错误
#include <unistd.h>

int main(int argc,char *argv[])
{
	char buf[512] = {0};
	int fd1, fd2;
	
	fd1 = open("./test1.txt", O_RDONLY); //打开文件
	if (fd1 == -1) {
		perror("test1.txt open failed"); //用于输出错误信息.类似于:fputs(”open failed\n”,stderr);
		exit(-1);
	}
	
	fd2 = creat("test2.txt", 0755); //创建文件
	if (fd1 == -1) {
		perror("test2.txt creat failed");
		exit(-1);
	}

	int read_cnts = 0, write_cnts = 0;
	while ((read_cnts = read(fd1, buf, sizeof(buf))) > 0) {//如果 read 读取成功,返回的是长度,否则,返回-1
		write_cnts = write(fd2, buf, fr);
		if (write_cnts == -1) {
			perror("write failed!");
			exit(-1);
		}
	}
	close(fd1);
	close(fd2);
}

三、文件I/O操作函数—进阶

3.1 pread & pwrite
  • 如果对文件的读写操作很重要,不能被打断,可以用原子操作函数。对文件的读写原子操作有 perad、pwrite。
#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:读&写数据的字节数

  • offset:读&写的起始地址的偏移量,读取&写入地址=文件开始+offset。注意,执行后,文件偏移指针不变

  • 返回值:成功时,将返回读取或写入的字节数(对于pwrite(),零表示未写入任何内容,或如果是pread(),则为文件结尾,如果是错误,则为-1,如果是错误,则设置errno以指示错误。

3.2 dup & dup2
  • 函数功能:文件描述符的复制
#include <unistd.h>

int dup(int oldfd);
int dup2(int oldfd, int newfd);

  • 返回值:成功:文件描述符,失败 -1
  • 如果说将文件描述符直接赋值给变量类似于ln -s 源文件 目标文件(删除源文件之后,链接变成无效的了) 的话,那么上面这两个就类似于ln 源文件 目标文件(删除源文件之后,目标没有影响)了。
  • eg: fd1 = fd2; close(fd2); 关闭fd2,fd1也会无效; 引用次数变化1—>1—>1
  • eg: fd1=dup(fd2); close(fd2); 关闭fd2;fd1依然有效;引用次数变化 1—>2—>1
  • 变量直接赋值,内核引用次数不会增加;使用dup&dup2内核引用次数会增加,每调用一次+1,close一次-1。
  • dup与dup2的区别就是dup2可以用户自己设定新的文件描述符(前提是该文件描述符没有被其他文件引用);dup是系统内核自动分配的(由小到大原则)
3.3 fcntl
  • 函数功能:改变已打开文件的属性
#include <unistd.h>
#include <fcntl.h>

int fcntl(int fd, int cmd, ... /* arg */ );

  • fcntl函数有5种功能:
  1. 复制一个现有的描述符(cmd=F_DUPFD).
  2. 获得/设置文件描述符标记(cmd=F_GETFD或F_SETFD).
  3. 获得/设置文件状态标记(cmd=F_GETFL或F_SETFL).
  4. 获得/设置异步I/O所有权(cmd=F_GETOWN或F_SETOWN).
  5. 获得/设置记录锁(cmd=F_GETLK , F_SETLK或F_SETLKW).
  • fd:文件描述符
  • cmd:
    在这里插入图片描述
  • 第三个参数:
  • 当fcntl中的cmd参数与第2~4功能有关时,第三个参数arg 指向一个 long 整型
    int fcntl(int fd, int cmd, long arg);
  • 当fcntl 中的cmd参数与记录锁有关时,第三个参数arg指向一个 struct flock 结构体
    int fcntl(int fd, int cmd, struct flock * lock);
  • 返回值: 失败 -1 , 成功 :与cmd相关的值
3.3.1 复制一个现有的描述符
int arg = 4;
int newfd = fcntl(fd1, F_DUPFD, arg); 

  • 新文件描述符将作为一个返回值返回,它是尚未打开的各描述符中大于或等于arg的数(由小到大原则:最小值)。即 newfd >= 4
3.3.2 获得/设置文件描述符标记

在这里插入图片描述
在这里插入图片描述

  • 发现有没有第三个参数都对这个值没影响
  • 在这里插入图片描述
3.3.3 获得/设置文件状态标记
  • 状态值同 open()中参数 flags 一样
#include <sys/types.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>

int main(int argc,char *argv[])
{
    int ret = 0;
    int fd1 = open("test.txt", O_WRONLY | O_CREAT);

    ret = fcntl(fd1, F_GETFL, 0);
    if (ret < 0) {
        printf("fcntl failed \n");
    }
    switch(ret & O_ACCMODE) { 
        case O_RDONLY: printf("O_RDONLY \n"); break;
        case O_WRONLY: printf("O_WRONLY \n"); break;
        case O_RDWR: printf("O_RDWR \n"); break;
        default: printf("unknown acc mode");
    }
    if (ret & O_APPEND) 
        printf("O_APPEND \n");
    if (ret & O_NONBLOCK) 
        printf("O_NONBLOCK \n");
    if (ret & O_SYNC) 
        printf("O_SYNC  \n");

    close(fd1);
    return 0;
}

在这里插入图片描述

3.3.4 获得/设置异步I/O所有权
3.3.5 获得/设置记录锁
struct flock
{
	short int l_type; /*锁定的状态,用 F_RDLCK 和 F_WRLCK 和 F_UNLCK*/ 
	short int l_whence; /*决定 l_start 位置,用 SEEK_SET,SEEK_CUR,SEEK_END*/ 
	off_t l_start; /*锁定区域的开头位置*/
	off_t l_len; /*锁定区域的大小,用 struct stat 结构体 中的 st_size 可以获得*/ 
	pid_t l_pid; /*锁定动作的进程,用 getpid()函数获得*/ 
};

l_type:

  • F_RDLCK:共享锁(或读锁)
  • F_WRLCK:独占锁(或写锁)
  • F_UNLCK:解锁,用来清除锁。

l_whence:

  • SEEK_SET 以文件开头为锁定的起始位置
  • SEEK_CUR 以目前文件读写位置为锁定的起始位置
  • SEEK_END 以文件结尾为锁定的起始位置
3.4 stat & fstat & lstat
  • 函数功能: 获取文件信息
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>

int stat(const char *path, struct stat *buf);
int fstat(int fd, struct stat *buf);
int lstat(const char *path, struct stat *buf);

  • lstat 函数类似于 stat, 但是当命名的文件是一个符号链接时,lstat 得到该符号链接文件的有关信息,而不是该符
    号链接指向的文件的信息。
struct stat {
	dev_t st_dev; /*如果是设备,返回设备表述符,否则为 0*/
	ino_t st_ino; /* i 节点号 */
	mode_t st_mode; /* 文件类型 */
	nlink_t st_nlink; /* 链接数 */
	uid_t st_uid; /* 属主 ID */
	gid_t st_gid; /* 组 ID */
	dev_t st_rdev; /* 设备类型*/
	off_t st_size; /* 文件大小,字节表示 */
	blksize_t st_blksize; /* 块大小*/
	blkcnt_t st_blocks; /* 块数 */
	time_t st_atime; /* 最后访问时间*/
	time_t st_mtime; /* 最后修改时间*/
	time_t st_ctime; /* 创建时间 */
};

  • 对于结构体的成员 st_mode,有一组宏可以进行文件类型的判断,通常用于判断:if(S_ISDIR(st.st_mode)){/*...*/}
    在这里插入图片描述
  • 示例:得到文件大小
#include<sys/stat.h>
#include<unistd.h>

int main(int argc,char *argv[])
{
	struct stat buf;
	stat (/etc/passwd”,&buf);
	printf(/etc/passwd file size = %d \n”,buf.st_size);//st_size 可以得到文件大小
}

3.5 access
  • 函数功能:Linux 下不同的用户对文件的可操作权限不同,access 函数可测试当前用户(运行该函数程序的用户)对某文件是否有相关权限。其是按实际用户 ID 和实际组 ID 进行权限许可测试的。
#include <unistd.h>

int access(const char *pathname, int mode);

  • mode :
    • R_OK 可读
    • W_OK 可写
    • X_OK 可执行
    • F_OK 测试文件是否存在
  • 返回值: 成功时(授予所有请求的权限),返回零。出错时(模式中至少有一位请求如果被拒绝,或发生其他错误),则返回-1,并正确设置errno。
  • 示例 :

#include<stdio.h>
#include<unistd.h>

#define FILE_NAME  "./test.txt"

int main(int argc,char *argv[])
{
	if(access(FILE_NAME, F_OK) == -1) {
		printf("文件不存在\n");
		return 0;
	}
	if(!access(FILE_NAME, R_OK))
		printf("r");
	else
		printf("-");
	if(!access(FILE_NAME, W_OK))
		printf("w");
	else
		printf("-");
	if(!access(FILE_NAME, X_OK))
		printf("x");
	else
		printf("-");
	return 0;
}

3.6 ioctl
  • ioctl函数一直是I/O操作的杂物箱,上述函数不能进行的I/O操作通常都能用ioctl表示,终端I/O是用ioctl最多的地方。
#include <sys/ioctl.h>

int ioctl(int d, int request, ...);

  • 详情见后续

四、 总结

参考:
https://www.cnblogs.com/zxc2man/p/7649240.html
UNIX环境高级编程(中文第三版.pdf

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值