跳至主要內容

嵌入式学习之(七)| Linux 高级程序设计 3

Kevin 吴嘉文大约 8 分钟知识笔记Linux嵌入式学习

系统调用

嵌入式自学开始几个月啦~ 为了自己的贾维斯 ????!!!!!

真想快点了解到计算机和这个世界是怎么运转的,可能只有真的理解了物理原理,才能没有拘束的创造

笔记总结 课程链接:千峰嵌入式教程open in new window

系统调用

linux 中,通过操作 shell,applications 或 library routines 来操作 system calls,进而调用 kernel 来操作硬件

image-20210224144726745
image-20210224144726745
  • 系统调用的返回值
    • 通常,用一个负的返回值来表明错误,返回一个 0 值表明成功。 错误信息存放在全局变量 errno 中,用户可用 perror 函数打印出错信
  • Linuxzhong ,API 遵循 POSIX 标准

系统调用 I/O 函数

文件描述符

文件描述符是非负整数。打开现存文件或新建文件时,系统(内核)会返回一个文件描述符。文件描述符用来指定已打开的文件。 在系统调用(文件 IO)中,文件描述符对文件起到标识作用,如果要操作文件,就是对文件描述符的操作 当一个程序运行或者一个进程开启时,系统会自动创建三个文件描述符 0 标准输入 1 标准输出 2 标准输出出错

文件 IO 的文件描述符和标准 IO 的文件指针的对应关系 文件 IO 标准 IO 0 stdin 1 stdout 2 stderr

如果自己打开文件,会返回文件描述符,而文件描述符一般按照从小到大依次创建的顺序

Open

标准 IO 文件 IO 权限含义

标准 IO文件 IO (flags)权限含义
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以读写的方式打开文件,如果文件不存在则创建,如果文件存在则追加

使用 C 文件进行操作

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

int main()
{
open("file.txt", O_RDONLY);
return 0;
}
\\int open(const char *pathname, int flags);
\\int open(const char *pathname, int flags, mode_t mode);

函数调用出错后输出错误信息

  • 错误码信息可以通过cat /usr/include/asm-generic/errno-base.h查询
通过全局变量 errno
#include <errno.h>
errno 是一个全局变量,当函数调用失败后,可以通过 errno 获取错误码

通常我们会更多的通过一个函数 perror
#include <stdio.h>
void perror(const char *s);
功能:输出函数调用失败的错误信息
参数:
s:打印错误信息的提示消息 c
返回值:无
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>

int main(int argc, char const *argv[])
{
//使用 open 函数打开或者创建一个文件
int fd;
fd = open("file.txt", O_RDONLY);

if(fd ==1)
{
//通过全局变量 errno 打印错误码
//注意需要添加头文件 errno.h
//printf("errno = %d\n", errno);

//通过 perror 函数输出函数调用失败的错误信息
perror("fail to open");
return1;
}

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

return 0;
}

Close 函数

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

int main(int argc, char const *argv[])
{
int fd;
fd = open("file.txt", O_RDONLY);

if(fd ==1)
{
perror("fail to open");
return1;
}
printf("fd = %d\n", fd);

//当不对文件进行任何操作时,就会关闭文件描述符
//使用 close 函数关闭文件描述符
//一旦关闭了文件描述符,就不能再通过原有的文件描述符对文件进行操作
close(fd);

return 0;
}
  • 一个进程最多可以创建 1024 个描述符,0-1023
  • 一但文件被关闭了,最后创建地文件会优先考虑补齐之前被关闭地文件地文件描述符。最后地文件并不一定是最大地。

write

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

int main(int argc, char const *argv[])
{
//向终端写入数据
 //对 1 这个文件描述符进行操作
 //调用函数要考虑函数调用错误
 //内核地调用没有缓冲区,他会直接对磁盘进行操作
if(write(1, "hello world\n", 12) ==1) 
{
perror("fail to write");
return1;
}

//向文件中写入数据

return 0;
}

向文件写入数据

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

int main(int argc, char const *argv[])
{
//向文件写入数据
//以只写的方式打开文件,如果文件不存在则创建,如果文件存在则清空
fd = open("file.txt", O_WRONLY | O_CREAT | O_TRUNC, 0664);
if(fd ==1)
{
 perror("fail to open");
 return1;
 }

//使用 write 函数向文件写入数据
//两个等号的优先级大于一个
ssize_t bytes;
 if((bytes = write(fd, "hello world\n", 12)) ==1)
 {
 perror("fail to write");
 return1;
 }

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

 write(fd, "nihao beijing", 5);

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

return 0;
 }

read

3 功能:从文件中读取数据 4 参数: 5 fd:指定的文件描述符 6 buf:保存读取到的数据 7 count:最大一次读取多少个字节 8 返回值: 9 成功:实际读取的字节数 10 失败:‐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[])
 {
//使用 read 函数从终端读取数据
 //使用 0 文件描述符从终端读取数据

 //如果终端输入的字节数大于第三个参数
 //则只会读取第三个参数对应的字节数,返回值也是与第三个参数一致

//如果终端输入的字节数小于第三个参数‘
//则只会读取输入的数据加上换行符,返回值就是实际输入的数据+1

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

printf("str = [%s]\n", str);
 printf("bytes = %ld\n", bytes);

return 0;
 }
 //注意:如果读取到文件末尾,返回 0
  • 从文件读取数据
#include <stdio.h>
2 #include <sys/types.h>
3 #include <sys/stat.h>
4 #include <fcntl.h>
5 #include <unistd.h>
6
7 #define N 64
8
9 int main(int argc, char const *argv[])
10 {
11 //使用 read 从文件中读取数据
12 int fd;
13 if((fd = open("test.txt", O_RDONLY | O_CREAT, 0664)) ==1)
14 {
15 perror("fail to open");
16 return1;
17 }
18
19 //读取文件内容
20 char buf[N] = "";
21 ssize_t bytes;
22 #if 1
23 if((bytes = read(fd, buf, 32)) ==1)
24 {
25 perror("fail to read");
26 return1;
27 }
28
29 printf("buf = [%s]\n", buf);
30 printf("bytes = %ld\n", bytes);
31
32 char buf1[N] = "";
33 bytes = read(fd, buf1, 32);
34 printf("buf1 = [%s]\n", buf1);
35 printf("bytes = %ld\n", bytes);
37 //如果文件中的数据都读取完毕,则 read 会返回 0
38 char buf2[N] = "";
39 bytes = read(fd, buf2, 32);
40 printf("buf2 = [%s]\n", buf2);
41 printf("bytes = %ld\n", bytes);
42 #endif
43
44 #if 0
45 //读取文件中的所有内容
46 while((bytes = read(fd, buf, 32)) != 0)
47 {
48 printf("buf = [%s]\n", buf);
49 printf("bytes = %ld\n", bytes);
50 }
51 #endif
52 //关闭文件描述符
53 close(fd);
54
55 return 0;
56 }

Remove 函数

 #include <stdio.h>
int main(int argc, char const *argv[])
{
//使用 remove 函数删除文件
if(remove("./file.txt") ==1)
{
perror("fail to remove");
return1;
}
printf("delete done\n");
return 0;
}

系统调用和库函数

库函数由两类函数组成 (1)不需要调用系统调用 不需要切换到内核空间即可完成函数全部功能,并且将结果反馈给应用程序,如 strcpy、bzero 等字符串操作函数。 (2)需要调用系统调用 需要切换到内核空间,这类函数通过封装系统调用去实现相应功能,如 printf、fread 等 库函数与系统调用的关系 系统提供的很多功能都必须通过系统调用才能实现

image-20210225130245871
image-20210225130245871

库函数通过缓冲区提高了系统调用的效率

image-20210225130540877
image-20210225130540877

系统调用是需要时间的,程序中频繁的使用系统调用会降低程序的运行效率 当运行内核代码时,CPU 工作在内核态,在系统调用发生前需要保存用户态的栈和内存环境,然后转入内核态工作。 系统调用结束后,又要切换回用户态。这种环境的切换会消耗掉许多时间库函数访问文件的时候根据需要,设置不同类型的缓冲区,从而减少了直接调用 IO 系统调用 的次数,提高了访问效率。 总结:大多数库函数的本质也是系统调用,只不过库函数有了缓冲区,用于减少系统调用的次数

上次编辑于:
贡献者: kevinng77