系统调用和库函数

1、系统调用的本质

  所有操作的本质都是要下发命名到硬件,在Linux操作系统中提供了linux内核来驱动操作硬件、为了方便用户操作相关硬件功能,所以提供了一些函数接口,用于操作Linux内核,进而由内核来操作硬件。这些接口称之为系统调用。
  在Linux内核中,大多数的代码也是系统调用。在应用层调的一些函数接口,有些也是在函数接口内在调用内核中的系统调用函数。
在这里插入图片描述

文件io:称之为系统调用
标准io:称之为库函数

2、库函数

  库函数的本质是系统调用,系统调用函数只要执行一次,就需要对Linux内核访问一次,这样虽然效率较高,但是占用的资源比较大。
  库函数提供了一种方式,如果需要执行多次系统调用,在执行之前,会在内存中开辟一块空间,称之为缓冲区,缓冲区的目的就是将多次执行的系统调用的数据先保存起来,然后只需要执行一次系统调用即可,这样节省空间开销。库函数的目的就是为了减少系统调用的次数。
  不论是系统调用还是库函数,最终目的都是操作物理内存。
在这里插入图片描述

3、系统调用函数

文件io是系统调用,没有缓冲区,例如:open、read、write、lseek等。
标准io是库函数,有缓冲区,例如:fopen、fread、fwrite、ftell、fseek、fgets、fputs等。

标准io通过文件指针(FILE *)来操作文件
文件io通过文件描述符(int)对文件进行操作
当程序运行时或者创建了一个新的进程时,系统都会自动分配三个文件描述符

 标准io		   文件io
标准输入			stdin		0
标准输出			stdout		1
标准出错输出		stderr		2

下面对一些常用的系统调用函数简单介绍。

1、open()

#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);
功能:打开或者创建一个文件,返回一个文件描述符
参数:
    pathname:文件名,可以添加路径,默认为当前路径
    flags:访问权限标志位
        O_RDONLY 只读
        O_WRONLY 只写
        O_RDWR   可读可写
        O_CREAT  如果文件不存在则创建,如果会用此标志位,必须有第三个参数
        O_TRUNC  如果文件存在则清空
        O_APPEND 如果文件存在则追加
        O_EXCL   常与O_CREAT一起使用,表示如果文件存在则报错
   mode:文件创建的权限,一般传八进制数即可,例如0664   
返回值:
    成功:文件描述符
    失败:-1

2、close()

#include <unistd.h>
int close(int fd);
功能:关闭一个文件描述符,如果关闭文件描述符,
    则无法再通过这个文件描述符对文件进行操作
参数:
    fd:指定的文件描述符
返回值:
    成功:0
    失败:-1

3、perror()

#include <stdio.h>
void perror(const char *s);
功能:当函数调用失败后可以输出错误信息
参数:
    s:错误信息的提示信息
返回值:
    无

4、errno

errno变量在errno.h头文件中定义,
是一个全局变量,可以在任意位置使用,用于保存函数调用后的错误码

例如:

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>

int main(int argc, const char *argv[])
{
	int fd;

	//使用open函数创建或者打开一个文件
	//if((fd = open("file.txt", O_RDONLY)) == -1)
	//if((fd = open("file.txt", O_RDONLY | O_CREAT | O_TRUNC, 0664)) == -1)
	//if((fd = open("file.txt", O_RDONLY | O_CREAT | O_APPEND, 0664)) == -1)
	if((fd = open("file.txt", O_RDONLY | O_CREAT | O_EXCL, 0664)) == -1)
	{
		//errno:用于函数执行失败后保存其失败的错误码,需要头文件,是一个全局变量
		//printf("errno = %d\n", errno);

		//perror:当函数调用失败时,会输出错误信息
		perror("fail to open");
		return -1;
	}

	printf("fd = %d\n", fd);

	//关闭文件描述符
	close(fd);
	return 0;
}

5、read()

#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);
功能:从文件中读取数据
参数:
    fd:指定的文件描述符
    buf:保存读取到的数据
    count:最大一次读取多少个字节
返回值:
    成功:实际读取的字节数
   失败:-1
   注意:如果读取到文件末尾,返回0

例如:

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

#define N 6

int main(int argc, char const *argv[])
{
    //从终端读取数据
    //从终端读取数据时,会把\n也读取到
    //如果输入的数据大于设置读取的最大个数,会读取设置的字节数
#if 0
    char buf[N] = "";
    if(read(0, buf, sizeof(buf)) == -1)
    {
        perror("fail to read");
        return -1;
    }
    
    printf("buf = %s", buf);
#endif
    //从文件中读取数据
    int fd;
    if((fd = open("file.txt", O_RDONLY)) == -1)
    {
        perror("fail to open");
        return -1;
    }

    //注意:使用read函数读取文件内容是,要以返回值为标准表示实际读取的个数
    ssize_t bytes;
    char buf[40] = "";
    if((bytes = read(fd, buf, sizeof(buf))) == -1)
    {
        perror("fail to read");
        return -1;
    }

    printf("buf = %s\n", buf);
    printf("bytes = %ld\n", bytes);
    
    char buf1[40] = "";
    if((bytes = read(fd, buf1, sizeof(buf1))) == -1)
    {
        perror("fail to read");
        return -1;
    }

    printf("buf1 = %s\n", buf1);
    printf("bytes = %ld\n", bytes);

    return 0;
}

6、write()

#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);
功能:向文件写入数据
参数:
    fd:指定的文件描述符
    buf:要写入的数据
    count:要写入的数据的长度
返回值:
    成功:实际写入的字节数
   失败:-1

例如:

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

int main(int argc, char const *argv[])
{
    //向终端写入数据
#if 0
    if(write(1, "hello world\n", 13) == -1)
    {
        perror("fail to write");
        return -1;
    }
#endif

    //向文件中写入数据
    int fd;
    if((fd = open("file.txt", O_WRONLY | O_CREAT | O_TRUNC, 0664)) == -1)
    {
        perror("fail to open");
        return -1;
    }

    if(write(fd, "hello world", 12) == -1)
    {
        perror("fail to write");
        return -1;
    }

    close(fd);

    return 0;
}

7、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  文件末尾位置(最后一个字符的下一个位置)
返回值:
    成功:返回文件的当前偏移量
    失败:-1

例如

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>

int main(int argc, char const *argv[])
{
    int fd;
    //if((fd = open(argv[1], O_RDWR | O_CREAT | O_TRUNC, 0664)) == -1)
    //注意:如果文件io中的open函数使用O_APPEND或者标准io的fopen函数使用a或者a+
    //     这样做无法修改写操作的偏移量,只能修改读操作的偏移量
    //     写操作只能在末尾处去写,而读操作可以指定偏移量
    if((fd = open(argv[1], O_RDWR | O_CREAT | O_APPEND, 0664)) == -1)
    {
        perror("fail to open");
        return -1;
    }

    char buf[128] = "hello world, nihao beijing, nihao chengdu\n";
    if(write(fd, buf, strlen(buf)) == -1)
    {
        perror("fail to write");
        return -1;
    }

    //不管是read还是write,执行完毕后都会修改文件的偏移量
    //获取当前文件的偏移量
    printf("offset = %ld\n", lseek(fd, 0, SEEK_CUR));

    //修改文件的偏移量
    lseek(fd, 0, SEEK_SET);

    printf("offset = %ld\n", lseek(fd, 0, SEEK_CUR));

    char text[128] = "";
    if(read(fd, text, sizeof(text)) == -1)
    {
        perror("fail to read");
        return -1;
    }

    printf("text = %s\n", text);

    lseek(fd, 10, SEEK_SET);

    write(fd, "666666", 6);

    return 0;
}

8、remove()

#include <stdio.h>
int remove(const char *pathname);
功能:删除文件或者目录
参数:
    pathname:文件名或者目录名
返回值:
    成功:0
    失败:-1

4、库函数

1、fopen()

#include <stdio.h>
FILE * fopen(const char * path,const char * mode);
参数path字符串包含欲打开的文件路径及文件名,参数mode字符串则代表着流形态。
返回值:文件顺利打开后,指向该流的文件指针就会被返回。如果文件打开失败则返回NULL,并把错误代码存在errno 中
r  为 读操作 打开 文本文件. 流 被定位于 文件 的 开始. 
r+  为读写操作打开文本文件. 流 被定位于 文件 的 开始. 
w  为写操作创建文本文件, 或者 将 已经 存在的 文件长度 截断为 零. 流 被定位于 文件 的 开始. 
w+  为读写操作打开文件. 如果 文件 不存在, 就 创建 它, 否则 将它 截断. 流 被定位于 文件 的 开始. 
a  为追加操作 (在文件尾写) 打开 文件. 如果 文件 不存在, 就 创建 它. 流 被定位于 文件 的 末尾. 
a+  为追加操作 (在文件尾写) 打开 文件. 如果 文件 不存在, 就 创建 它. 读文件的初始位置 是 文件 的 开始, 但是 写文件 总是 被追加到 文件 的 末尾.

与open对比
在这里插入图片描述

2、fread()

#include <stdio.h>
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
功能:从文件中读取数据
参数:
    ptr:保存读取的数据
    size:每次读取的字节数
    nmemb:一共读取的次数
    stream:文件指针
返回值:
    成功:实际读取的次数(对象数、块数)
    失败:0
    如果文件内容读取完毕,返回0

例如:将文件中信息读到结构体中

#include <stdio.h>

typedef struct{
    int a;
    int b;
    char c;
    char d[32];
}MSG;

int main(int argc, char const *argv[])
{
    //使用fread从文件中读取数据
    FILE *fp;
    if((fp = fopen("file.txt", "r")) == NULL)
    {
        perror("fail to fopen");
        return -1;
    }

    // char buf[32] = {0};
    // fread(buf, 2, 5, fp);
    // printf("buf = %s\n", buf);
    
    // int num;
    // fread(&num, 4, 1, fp);
    // printf("num = %d\n", num);

    // int b[4] = {0};
    // fread(b, 4, 4, fp);
    // printf("%d %d %d %d\n", b[0], b[1], b[2], b[3]);
    
    MSG msg;
    fread(&msg, sizeof(msg), 1, fp);
    printf("%d - %d - %c - %s\n", msg.a, msg.b, msg.c, msg.d);
    
    return 0;
}

3、fwrite()

#include <stdio.h>
size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
功能:向文件中写入数据
参数:
    ptr:要写入的数据
    size:一次写入的字节数
    nmemb:一共写入的次数
    stream:文件指针
返回值:
    成功:实际写入的次数
    失败:0

例如:

#include <stdio.h>

typedef struct{
    int a;
    int b;
    char c;
    char d[32];
}MSG;

int main(int argc, char const *argv[])
{
    //使用fwrite向文件写入数据
    FILE *fp;
    if((fp = fopen("file.txt", "w")) == NULL)
    {
        perror("fail to fopen");
        return -1;
    }

    //写入字符串
    //char buf[] = "1234567890";
    //fwrite(buf, 2, 5, fp);
    
    //写入整数
    //int a = 97868;
    //fwrite(&a, 4, 1, fp);

    //写入数组
    //int a[4] = {100, 200, 300, 400};
    //fwrite(a, 4, 4, fp);

    //写入结构体
    MSG msg = {666, 888, 'w', "zhangsan"};
    fwrite(&msg, sizeof(msg), 1, fp);

    return 0;
}

4、fgets()、fputs()

fgets()

#include <stdio.h>
char *fgets(char *s, int size, FILE *stream);
功能:从文件中读取内容
参数:
    s:保存读取到的内容
    size:每次读取的最大个数
    stream:文件指针
返回值:
    成功:读取的数据的首地址
   失败:NULL
   如果文件内容读取完毕,也返回NULL

fputs()

#include <stdio.h>
int fputs(const char *s, FILE *stream);
功能:向文件写入数据
参数:
    s:要写入的内容
    stream:文件指针
返回值:
    成功:写入文件内容的字节数
    失败:EOF

5、fseek()

#include <stdio.h>
int fseek(FILE *stream, long offset, int whence);
功能:设置文件位置指针的偏移量
参数:
    stream:文件指针
    offset:偏移量
        可正可负也可为0
    whence:相对位置
        SEEK_SET 文件起始位置
        SEEK_CUR 文件当前位置
       SEEK_END 文件末尾位置(最后一个字符后面一个位置)
返回值:
    成功:0
    失败:-1

6、rewind()

#include <stdio.h>
void rewind(FILE *stream);
功能:将文件位置定位到起始位置
参数:
    stream:文件指针
返回值:无

rewind(fp) <==> fseek(fp, 0, SEEK_SET);

7、ftell()

#include <stdio.h>
long ftell(FILE *stream);
功能:获取当前文件的偏移量
参数:
    stream:文件指针
返回值:
    获取当前文件的偏移量

例如:

#include <stdio.h>

int main(int argc, char const *argv[])
{
    //写操作的文件偏移量
#if 0
    FILE *fp;
    if((fp = fopen("file.txt", "w")) == NULL)
    {
        perror("fail to fopen");
        return -1;
    }

    fputs("abcdefghijklmnopq", fp);
    //使用ftell获取文件偏移量
    printf("offset = %ld\n", ftell(fp));

    //使用fseek修改文件偏移量
    //rewind(fp);
    //fseek(fp, 0, SEEK_SET);
    
    fseek(fp, -5, SEEK_END);
    fputs("12345", fp);
#endif

#if 0
    FILE *fp;
    if((fp = fopen("file.txt", "r")) == NULL)
    {
        perror("fail to fopen");
        return -1;
    }

    char buf[32] = {0};
    fgets(buf, 6, fp);
    printf("buf = %s\n", buf);

    printf("offset = %ld\n", ftell(fp));
    fseek(fp, 3, SEEK_SET);
    printf("offset = %ld\n", ftell(fp));

    fgets(buf, 6, fp);
    printf("buf = %s\n", buf);
#endif

    FILE *fp;
    //如果fopen打开或者创建一个文件时使用的是的a或者a+权限
    //则写操作的偏移量无法改变,只能在文件的末尾位置
    //但是读操作没有任何影响
    if((fp = fopen("file.txt", "a+")) == NULL)
    {
        perror("fail to fopen");
        return -1;
    }

    // char buf[32] = {0};
    // fgets(buf, 6, fp);
    // printf("buf = %s\n", buf);

    // printf("offset = %ld\n", ftell(fp));
    // fseek(fp, 3, SEEK_SET);
    // printf("offset = %ld\n", ftell(fp));

    // fgets(buf, 6, fp);
    // printf("buf = %s\n", buf);

    fputs("888888888", fp);
    printf("offset = %ld\n", ftell(fp));
    fseek(fp, 3, SEEK_SET);
    printf("offset = %ld\n", ftell(fp));
    
    fputs("666666", fp);

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值