Linux基础知识之IO操作

简介

  IO顾名思义就是input/output.俗话说linux一切皆文件,对文件的操作不外乎就读和写.这里我们来介绍read和write函数.

函数原型

read

SYNOPSIS

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

RETURN VALUE
  On success, the number of bytes read is returned(zero indicates end of file)
  On error, -1 is returned, and errno is set appropriately

write

SYNOPSIS

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

RETURN VALUE
  On success, the number of bytes written is returned(zero indicates nothing was written)
  On error, -1 is returned, and errno is set appropriately

参考
Linux常见错误码

例子

  这里以C语言为例.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>

int main(int argc __unused, char **argv __unused) {
int fd[2] = { 0 };
char *sendMsg = "Hello World";
char recvMsg[128] = { 0 }; //作为例子,这里buffer只用了128字节.实际应以需求为准

if (pipe(fd) < 0) { //管道,fd[0] 为读管道,fd[1] 为写管道
printf("Create pipe fail, reason : %s\n", strerror(errno));
return -1;
}

if (write(fd[1], sendMsg, strlen(sendMsg) + 1) < 0) {
printf("write fail, reason : %s\n", strerror(errno));
return -1;
}


if (read(fd[0], recvMsg, sizeof(recvMsg)/sizeof(*recvMsg)) < 0) {
printf("read fail, reason : %s\n", strerror(errno));
return -1;
}

printf("read message success : %s\n", recvMsg);

//注意打开fd 和关闭fd 应成对出现,防止fd泄露.
close(fd[0]);
close(fd[1]);
return 0;
}

运行结果

1
2
3
generic_arm64:/ # example
example
read message success : Hello World

关于errno

  如果你对errno这个错误码值赋值流程感兴趣,可以继续往下看,需要一点汇编基础.否则本章完.
  以下基于ARMv8 64bit体系架构分析

read.S

1
2
3
4
5
6
7
8
9
10
#include <private/bionic_asm.h>
ENTRY(read)
mov x8, __NR_read //read函数的系统调用号赋值给x8寄存器
svc #0 //svc中断,进入系统调用

cmn x0, #(MAX_ERRNO + 1)
cneg x0, x0, hi
b.hi __set_errno_internal
ret
END(read)

系统调用返回的错误码都是相反数,这里以权限不足的错误码EACCES(13)为例来分析该流程.

1.系统调用read返回错误码EACCES的相反数-13,并将该值保存在x0寄存器.

2.cmn比较指令做加法操作,会引起PSTATE寄存器C标志位发生变化
-13的16进制:0xFFFF FFFF FFFF FFF3,MAX_ERRNO + 1的16进制:0x1000.
两者相加,会导致-13的最高位符号位溢出,因此PSTATE寄存器的C标志位 置1.

3.hi条件码代表PSTATE的C标志位为1且Z标志位为0.因此此时hi条件code为true

4.cneg条件否定指令,cneg x0,x0,hi 等价于if hi == true,then x0 = -x0 else x0 = x0
因此此处指令执行完后x0 = -x0 也就是x0 = -(-13) = 13

5.b.hi条件跳转指令,当hi条件码成立,跳转执行__set_errno_internal函数,此处会跳转执行该函数

1
2
3
4
extern "C" __LIBC_HIDDEN__ long __set_errno_internal(int n) {
errno = n; //形参n的值为x0寄存器,也就是错误码13,此处完成对errno的赋值
return -1; //返回-1,该值会赋给x0寄存器,因此read函数发生错误时,得到的返回值为-1
}

6.跳转lr地址

本章完