系统调用-常用IO函数

系统调用

系统调用概述

如果想操作内核(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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值