Linux系统编程学习笔记_002_系统调用文件IO

系统调用

系统调用是由操作系统实现并提供给外部应用程序的编程接口API,是应用程序同系统之间数据交互的桥梁。

open函数

作用是打开文件,返回文件描述符

函数原型

常用参数

需要使用头文件<fcntl.h>

只读、只写、可读写、追加、创建、判断是否存在、截断(将文件原本的内容丢弃)、非阻塞

文件权限 = mode & ~umask

read&write函数

#include <unistd.h>

函数原型:

read函数

参数:fd 文件描述符  buf 存数据的缓冲区 count 缓冲区大小

返回值:成功——读到的字节数  失败—— -1,设置errno  0——读到了文件末尾

返回-1且errno = EAGIN 或 EWOULDBLOCK时,并非read失败,而是read在以非阻塞方式读一个设备/网络文件,且文件无数据。

write函数

参数:fd 文件描述符  buf 待写出数据的缓冲区 count 数据大小

返回值:成功——写入的字节数  失败—— -1,设置errno

使用系统调用read和write实现模拟cp操作:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <pthread.h>
#define N 1

int main(int argc, char *argv[]){
	char buf[N];
	int n = 0;
	int fd1 = open(argv[1], O_RDONLY);
	if(fd1 == -1) {
		perror("open argv1 error");
		exit(1);
	}

	int fd2 = open(argv[2], O_RDWR|O_CREAT|O_TRUNC, 0664);
	if(fd2 == -1){
		perror("open argv2 error");
		exit(1);
	}
	
	while ((n = read(fd1, buf, 1024)) != 0){
		if(n<0){
			perror("read error");
			break;
		}
		write(fd2, buf, n);
	}
	
	close(fd1);
	close(fd2);
	return 0; 
}

使用库函数fputc&fgetc完成拷贝程序:

#include<stdio.h>
#include<stdlib.h>
 
int main(void)
{
    FILE *fp,*fp_out;
    int n;
    
    fp = fopen("dict.txt","r");
    if(NULL== fp)
    {
        perror("fopen dict.txt error");
        exit(1);
    }
    fp_out = fopen("dict.cp","w");
    if(fp_out == NULL)
    {
        perror("fopen dict.cp  error");
        exit(1);
    }
    while((n = fgetc(fp)) != EOF)
    {
	fputc(n,fp_out);
    }
    fclose(fp);
    fclose(fp_out);
    return 0;
}

两者时间比较,使用库函数快于系统调用。使用strace命令追踪程序执行时的系统调用,使用read/write为一个字节一个字节的读写,而使用fputc/fgetc一次读写4k。

预读入缓输出机制

缓输出:

黑线代表内核区kernal和用户区user的分隔,需要通过系统调用才能跨过该线。

耗时在于跨越用户和内核(用时<磁盘访问)

write会多次切换用户区和内核区,指定N为1时(左黑框),会一个字节一个字节的写入内核buf

标准IO函数fputc自带用户缓冲区(蓝色方框),满4K后再进入内核,因此切换少,速度快

read&write函数被称为Unbuffered I/O,因为其无用户级缓冲区,但是可以通过定义buf(左黑框)的大小N来提升写入的速度。

预读入:

从磁盘读取数据到内核空间,先缓冲一批到缓冲区,然后读入用户。

尽量使用库函数(速度),不方便使用缓输出机制(处理即时消息)的情况,使用系统调用。

文件描述符

PCB进程控制块:本质是结构体,描述进程消息

PCB中有一个指针指向文件描述符表,其中的每个元素均为指针,指向文件结构体struct file(文件信息),文件描述符即为该表中的下标(0~1023)。

file结构体:

阻塞和非阻塞

阻塞:发起调用完成某功能,若不具备条件则一直等待,直到满足条件完成

非阻塞:发起调用完成某功能,具备条件直接输出,不具备条件报错返回

产生阻塞(block)的场景:读设备文件,读网络文件(读常规文件无阻塞概念)

/dev/tty  终端文件

open("/dev/tty", O_RDWR|O_NONBLCK)  --设置/dev/tty为非阻塞状态(默认为阻塞)

非阻塞配合循环使用,设置timeout值防止无限调用。

fcntl函数

改变一个已经打开的文件的访问控制属性(不需要重新打开文件)

int flgs = fcntl(fd, F_GETFL);

flgs |= O_NONBLOCK

fcntl(STDIN_FILENO, fd, F_SETFL);

获取文件状态:F_GETFL

设置文件状态:F_SETFL

lseek函数

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

参数:1. 文件描述符 2. 偏移量 3. 起始偏移地址

返回值: 成功:较起始位置向后偏移量  失败:-1,errno

whence取值:SEET_SET(开头), SEEK_CUR(当前), SEEK_END(结尾)

应用:

1. 文件的”读“和”写“使用的是同一偏移,使用lseek来修改文件偏移量(读写位置)

2. 使用lseek获取文件大小

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <fcntl.h>

int main(int argc, char *argv[]){
	int fd = open(argv[1], O_RDWR);
	if(fd == -1){
		perror("open error");
		exit(1);
	}
	int length = lseek(fd, 0, SEEK_END);
	printf("file size: %d", length);
	return 0; 
}

3. 使用lseek拓展文件大小,真正改变文件大小需要IO操作

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <fcntl.h>

int main(int argc, char *argv[]){
	int fd = open(argv[1], O_RDWR);
	if(fd == -1){
		perror("open error");
		exit(1);
	}
	int length = lseek(fd, 111, SEEK_END);
	printf("file size: %d", length);
	write(fd, "a", 1);
	close(fd); 
	return 0; 
}

使用truncate函数直接拓展文件: int ret = truncate("dict.cp", 250);

传入/传出参数

传入参数:1. 指针作为函数参数

                  2. 通常由const关键字修饰

                  3. 指针指向有效区域,在函数内部做读操作

传出参数:

                  1. 指针作为函数参数

                  2. 在函数调用前,指针指向的空间可以无意义但必需有效

                  3. 在函数内部做写操作

                  4. 在函数调用结束后充当函数返回值

传入传出参数:

                  1. 指针作为函数参数

                  2. 在函数调用前,指针指向的空间有实际意义

                  3. 在函数内部,先做读操作后做写操作

                  4. 在函数调用结束后充当函数返回值

eg.

char *strcpy(char *dest //传出, const char *src //传出); 

char *strtok_r(char *str, const char *delim, char **savept //传入传出);

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值