系统调用-常用IO函数

ops/2025/2/8 11:01:44/

系统调用

系统调用概述

如果想操作内核(kernel),需要调用内核的系统调用(system calls)

系统调用有三种方式:

  • shell, 用户通过shell命令,有shell解释器操作内核的系统调用

  • 库函数,用户通过 应用层库函数 的接口,比如fread对内核的系统调用进行操作

    在 Linux 中,**应用程序编程接口(API)**遵循 POSIX 标准。 POSIX 标准基于当时现有的 UNIX 实践和经验,描述了操作系统的系统调用编程接口(实际上就是 API),用于保证应用程序可以在源代码一级上在多种操作系统上移植运行。

  • 应用层系统调用,他可以直接对内核的系统调用进行操作

分类:系统调用按照功能逻辑大致可分为

  • 进程控制、进程间通信、文件系统控制、系统控制、

  • 内存管理、网络管理、socket控制、用户管理。

返回值:

  • 返回值为**0 ** 表明成功
  • 返回值为表明错误

错误信息

错误信息存放在全局变量errno中,用户可用perror函数打印出错信息。

查看 全局变量 errno

errno是一个全局变量,当函数调用失败后,用于获取错误码。

#include <errno.h>int fd;
fd = open("file.txt",O_RDONLY);
if (fd = -1)
{printf("errno=%d\n",errno);errno=2
}

对照错误信息码:

cat /usr/include/asm-generic/errno-base.h# 得到错误码信息:
define	EPERM		 1	/* Operation not permitted */
define	ENOENT		 2	/* No such file or directory */
....

输出 函数perror

输出函数调用的错误信息·

#include <stdio.h>void perror(const char *s);		perror("fail to open"); 

参数: s 需要打印的错误提示消息

#include <stdio.h>
#include <sys/types.h> 
#include <sys/stat.h> 
#include <fcntl.h>
#include <errno.h>
int main()
{// open 函数打开或者创建一个文件int fd;fd = open("demo.txt",O_RDONLY | O_CREAT,0664);// 保存文件描述符 从小到大依次创建的顺序 3,4,5..printf("fd = %d\n",fd);if(fd ==1){perror("fail to open"); return1;}return 0;
}

系统调用I/O函数

系统调用中操作 I/O 的函数,都是针对文件描述符的. 通过文件描述符可以直接对相应的文件进行操作。

# include <unistd.h>

文件描述符

创建规则

文件描述符是非负整数。打开现存文件或新建文件时,系统(内核)会返回一个文件描述符。文件描述符用来指定已打开的文件,文件描述符对文件起到标识作用。如果要操作文件,就是对文件描述符的操作。

如果有文件描述符被关闭了,则再创建的文件描述符会先补齐之前的,然后依次递增创建。3,4,5…

一个程序运行的时候最多可以创建2014个文件描述符,0~1023

特殊文件描述符

当一个程序运行或者一个进程开启时,系统会自动创建三个文件描述符。

#define STDIN_FILENO   0 //标准输入的文件描述符 
#define STDOUT_FILENO  1 //标准输出的文件描述符
#define STDERR_FILENO  2 //标准错误的文件描述符
特殊文件描述符含义
0标准输入 stdin
1标准输出 stdout
2标准报错 stderr
复制 dup dup2

dup 和 dup2 都是用来复制一个文件的描述符,使新的文件描述符也标识旧的文件描述符所标识的文件。 复制文件描述符

dup 和 dup2 经常用来重定向进程的 stdin、stdout 和 stderr。

#include <unistd.h>
int dup(int oldfd); 
int dup2(int oldfd, int newfd); 
dup 函数
#include <unistd.h>int dup(int oldfd);

功能:复制 oldfd 文件描述符,并分配一个新的文件描述符。新的文件描述符是调用进程文件描述符表中最小可用的文件描述符。

参数:要复制的文件描述符 oldfd。

返回值:

  • 成功:新文件描述符。 (自动分配的)

  • 失败:返回-1,错误代码存于 errno 中。

示例

int fd1; 
int fd2; 
fd2 = dup(1); // fd2=3 指向标准输出
printf("new:fd2 = %d\n",fd2); // fd2=3
fd1 = open("test.txt", O_RDWR | O_CREAT, 0664); 
close(1); 
int fd3 = dup(fd1); // fd3=1 和fd1=4一起指向 test.txt
printf("hello world\n"); 
printf("fd = %d\n", fd3); //这些都被重定向 test.txt
close(1);
int fd4 = dup(fd2);  // fd4此时=1 和 fd2 都指向标准输出
printf("nihao beijing\n");
printf("fd = %d\n", fd4); 
dup2 函数 重定向
#include <unistd.h>int dup2(int oldfd, int newfd);//首先关闭1文件描述符,然后将fd1复制给1,意味着1和fd1都标识test.txt文件, 返回值跟1是一样的
fd1 = open("test.txt", O_CREAT | O_WRONLY, 0664);
fd2 = dup2(fd1, 1);

功能:复制一份打开的文件描述符 oldfd,并分配新的文件描述符 newfd。如果 newfd 是一个已经打开的文件描述符,则首先关闭该文件(自动close newfd),然后再复制。

参数:

  • oldfd 要复制的文件描述符
  • newfd 分配我们指定的新的文件描述符

返回值:

  • 成功:返回 指定的 新的文件描述符 newfd

  • 失败:返回-1,错误代码存于 errno 中

示例

//实现输出重定向后,再恢复标准输出
int fd1; 
//如果使用dup2,则需要实现给第二个参数对应的变量赋一个初始值
int fd2 = 3; 
//将1复制一份为fd2,所以fd2标识的是标准输出 
dup2(1, fd2); 
printf("fd2 = %d\n", fd2); 
fd1 = open("test.txt", O_CREAT | O_RDWR, 0664);
//输出重定向:
//  关闭文件描述符1,将fd1复制一份为1,
//  1此时标识的是test.txt 文件
dup2(fd1, 1); 
printf("hello world\n");//重定向
//再次实现标准输出:
//  关闭文件描述符1,将fd2复制一份为1,
//  1此时标识的是标准输出
dup2(fd2, 1); 
printf("嘻嘻哈哈哈哈哈哈哈哈哈哈\n"); //标准输出

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:open 函数的行为标志,可取位或
  • mode:文件权限(可读、可写、可执行)的设置。

返回值:

  • 成功,打开的文件描述符。
  • 失败,返回 -1,可以利用 perror

flags 文件IO和标准IO权限对比

标准IO文件IO权限含义
rO_RDONLY以只读的方式打开文件,如果文件不存在则报错
r+O_RDWR以读写的方式打开文件,如果文件不存在则报错
wO_WRONLY | O_CREAT | O_TRUNC 0664以只写的方式打开文件,如果文件不存在则创建,如果文件存在则清空
w+O_RDWR | O_CREAT | O_TRUNC, 0664以读写的方式打开文件,如果文件不存在则创建, 如果文件存在则清空
aO_WRONLY | O_CREAT | O_APPEND, 0664以只写的方式打开文件,如果文件不存在则创建, 如果文件存在则追加
a+O_RDWR | O_CREAT | O_APPEND, 0664以读写的方式打开文件,如果文件不存在则创建, 如果文件存在则追加

flags 的取值及其含义:

取值权限含义
O_RDONLY以只读的方式打开
O_WRONLY以只写的方式打开
O_RDWR以可读、可写的方式打开
除了取上述值外,还可与下列值位或
O_CREAT文件不存在则创建文件,使用此选项时需使用 mode 说明文件的权限
O_EXCL如果同时指定了 O_CREAT,且文件已经存在,则出错
O_TRUNC如果文件存在,则清空文件内容
O_APPEND写文件时,数据添加到文件末尾
O_NONBLOCK当打开的文件是 FIFO、字符文件、块文件时,此选项为非阻塞标志位

mode 的取值及其含义
只要指定了O_CREAT 就需使用 mode 说明文件的权限

取值八进制含义
S_IRWXU00700文件所有者的读、写、可执行权限
S_IRUSR00400文件所有者的读权限
S_IWUSR00200文件所有者的写权限
S_IXUSR00100文件所有者的可执行权限
S_IRWXG00070文件所有者同组用户的读、写、可执行权限
S_IRGRP00040文件所有者同组用户的读权限
S_IWGRP00020文件所有者同组用户的写权限
S_IXGRP00010文件所有者同组用户的可执行权限
S_IRWXO00007其他组用户的读、写、可执行权限
S_IROTH00004其他组用户的读权限
S_IWOTH00002其他组用户的写权限
S_IXOTH00001其他组用户的可执行权限

只要指定了O_CREAT 就需使用 mode 说明文件的权限

close函数

# include <unistd.h>
int close(int fd);  
int fd;
fd = open("demo.txt",O_RDONLY); 
close(fd);

功能:关闭一个文件描述符。

当不对文件进行任何操作时,就关闭文件描述符 使用close函数关闭文件描述符

参数

  • fd:指定文件的文件描述符,open函数的返回值

返回值: 成功:0 。失败:‐1

write函数

# include <unistd.h> 
ssize_t write(int fd, const void *buf, size_t count);

功能:向文件写入数据

参数:

  • fd:指定的文件描述符
  • buf:要写入的数据
  • count:要写入的数据的长度

返回值:

  • 成功:实际写入的字节数 类型 ssize_t (int 的别名)

  • 失败:‐1

示例:

向文件标识符 1 标准输出,从而向终端写入数据

int main(int argc, char const *argv[]) 
{ //向终端写入数据 //对1这个文件描述符进行操作if(write(1, "hello world\n", 12) == -1){ perror("fail to open"); return -1; } return 0; 
}

向文件写入数据

int main(int argc, char const *argv[]) 
{ //向文件写入数据 int fd; //以只写的方式打开文件,如果文件不存在则创建,如果文件存在则清空 fd = open("file.txt", O_WRONLY | O_CREAT | O_TRUNC, 0664); if(fd == -1) { perror("fail to open"); return -1; } //使用write函数向文件写入数据 ssize_t bytes; if((bytes = write(fd, "hello world\n", 12))== -1) { perror("fail to write"); return -1; } printf("bytes = %ld\n", bytes); write(fd, "nihao beijing", 5); //关闭文件描述符 close(fd); return 0; 
}

read 函数

把指定数目的数据读到内存

#include <unistd.h> ssize_t read(int fd, void *addr, size_t count); 

参数:

  • fd:文件描述符。
  • addr:内存首地址。
  • count:读取的字节个数。

返回值:类型 ssize_t

  • 成功,返回实际读取到的字节个数。
  • 失败
    • 返回0,读取到文件末尾
    • 返回-1,可以利用 perror 去查看原因。

如果输入的字节数大于第三个参数count,返回值与第三个参数一致。如果输入的字节数小于第三个参数count,读取输入的数据加上换行符,返回值就是实际输入的数据+1

    ssize_t bytes;char str[32]="";if((bytes = read(0,str,6))==-1){perror("fail to read");return -1;}printf("str = [%s]\n bytes = %ld\n", str, bytrs)

使用0文件描述符从终端读取数据

ssize_t bytes;
char str[32] = "";
if((bytes = read(0, str, 6)) == -1)
{ perror("fail to read"); return1; 
}

fread和fwrite 成块读写

fread 和 fwrite 函数可以对数据进行成块读写。

需要注意的是,尽管 fread 和 fwrite 函数并不是说一次想读写多少数据就能全部读写多少数据,毕竟缓存有限,不同的操作系统的缓存大小也可能不一样。

fread为封装好的库函数,而read为系统函数,一般来说,fread效率更高fread功能更强大,可以的结构体的二进制文件。如果底层的操作,用到文件描述符,用read更好

size_t fread(void *buf, size_t size, size_t count, FILE *fp);size_t fwrite(const void * buf, size_t size, size_t count, FILE *fp);

fread 函数从文件 fp 中读出 size* count 个字节保存到 buf 中

fwrite 把 buf 中的 size* count 个字节写到文件 fp 中

参数:

  • ptr为指向缓冲区保存或读取的数据。
  • size为控制记录大小。
  • nmemb为记录数。
  • 函数返回读取或回写的记录数 count。

返回值:读或写的记录数

  • 成功时返回的记录数等于 count 参数
  • 出错或读到文件末尾时返回的记录数小于 count,也可能返回 0。

remove 删除文件

#include <stdio.h> 
int remove(const char *pathname); 

参数:pathname :文件的路名+文件名。

返回值:

  • 成功返回 0。
  • 失败返回-1,利用 perror 查看原因。
if(remove("./file.txt") ==1)
{perror("fail to remove"); return1;
}

http://www.ppmy.cn/ops/156706.html

相关文章

使用动态协议包,实现客户端与服务器端

思维导图 使用链表记录接受的值 resver.c(服务器) #include <stdio.h> #include <string.h> #include <unistd.h> #include <stdlib.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <pthread.…

高效协同,Tita 助力项目管理场景革新

在当今快节奏、高度竞争的商业环境中&#xff0c;企业面临着前所未有的挑战&#xff1a;如何在有限资源下迅速响应市场变化&#xff0c;确保多个项目的高效执行并达成战略目标&#xff1f;答案就在于优化项目集程管理。而在这个过程中&#xff0c;Tita项目管理产品以其独特的优…

虚拟机搭建---K8S环境

K8S&#xff1a;docker容器其实本质上是一个linux里面的一个进程&#xff0c;容器本身自己是没有可再生能力的&#xff0c;没有高可用&#xff0c;而且生产环境运行容器&#xff0c;不可能只运行一两个&#xff0c;因此为了更加方便的管理众多容器&#xff0c;以及让容器具备高…

《向量数据库指南》——深入解析Mlivus Cloud的索引构建:临时索引与持久化索引的决胜之道

在数据驱动的时代,向量数据库作为支持各种人工智能应用与大数据分析的重要工具,其性能和灵活性至关重要。随着新数据的不断写入,如何高效地处理和管理这些数据成为了数据库设计中的一大挑战。在这一背景下,索引构建技术显得尤为重要。本文将对Mlivus Cloud中的索引构建进行…

在Ubuntu云服务器上使用OneFormer模型进行遥感图像水体提取,并替换为客户数据集的详细步骤

以下是在Ubuntu云服务器上使用OneFormer模型进行遥感图像水体提取&#xff0c;并替换为客户数据集的详细步骤&#xff1a; 1. 数据集准备 首先&#xff0c;你需要确保客户的数据集格式与OneFormer模型兼容。通常&#xff0c;语义分割数据集需要包含图像和对应的标注文件&…

力扣.623. 在二叉树中增加一行(链式结构的插入操作)

Problem: 623. 在二叉树中增加一行 文章目录 题目描述思路复杂度Code 题目描述 思路 1.首先要说明&#xff0c;对于数据结构无非两大类结构&#xff1a;顺序结构、链式结构&#xff0c;而二叉树实质上就可以等效看作为一个二叉链表&#xff0c;而对于链表插入一个节点的操作是应…

【Kubernetes Pod间通信-第2篇】使用BGP实现Pod到Pod的通信

Kubernetes中Pod间的通信 本系列文章共3篇: 【Kubernetes Pod间通信-第1篇】在单个子网中使用underlay网络实现Pod到Pod的通信【Kubernetes Pod间通信-第2篇】使用BGP实现Pod到Pod的通信(本文介绍)【Kubernetes Pod间通信-第3篇】Kubernetes中Pod与ClusterIP服务之间的通信…

网络安全—DDoS攻防

背景简述&#xff1a;DDoS攻击分为很多类型&#xff0c;有消耗网络带宽的流量攻击&#xff0c;有消耗服务器资源的应用层攻击等。影响巨大&#xff0c;且让无论大公司还是小公司都肃然“起敬”的当属&#xff1a;流量攻击。在流量越来越廉价的今天&#xff0c;攻击流量小则几百…