嵌入式学习之(七)| Linux 高级程序设计 3
系统调用
嵌入式自学开始几个月啦~ 为了自己的贾维斯 ????!!!!!
真想快点了解到计算机和这个世界是怎么运转的,可能只有真的理解了物理原理,才能没有拘束的创造
笔记总结 课程链接:千峰嵌入式教程
系统调用
linux 中,通过操作 shell,applications 或 library routines 来操作 system calls,进而调用 kernel 来操作硬件
- 系统调用的返回值
- 通常,用一个负的返回值来表明错误,返回一个 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) | 权限含义 |
---|---|---|
r | O_RDONLY | 以只读的方式打开文件,如果文件不存在则报错 |
r+ | O_RDWR | 以读写的方式打开文件,如果文件不存在则报错 |
w | O_WRONLY , O_CREAT , O_TRUNC,0664 | 以只写的方式打开文件,如果文件不存在则创建,如果文件存在则清空 |
w+ | O_RDWR , O_CREAT , O_TRUNC,0664 | 以读写的方式打开文件,如果文件不存在则创建,如果文件存在则清空 |
a | O_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");
return ‐1;
}
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");
return ‐1;
}
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");
return ‐1;
}
//向文件中写入数据
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");
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
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");
return ‐1;
}
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 return ‐1;
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 return ‐1;
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");
return ‐1;
}
printf("delete done\n");
return 0;
}
系统调用和库函数
库函数由两类函数组成 (1)不需要调用系统调用 不需要切换到内核空间即可完成函数全部功能,并且将结果反馈给应用程序,如 strcpy、bzero 等字符串操作函数。 (2)需要调用系统调用 需要切换到内核空间,这类函数通过封装系统调用去实现相应功能,如 printf、fread 等 库函数与系统调用的关系 系统提供的很多功能都必须通过系统调用才能实现
库函数通过缓冲区提高了系统调用的效率
系统调用是需要时间的,程序中频繁的使用系统调用会降低程序的运行效率 当运行内核代码时,CPU 工作在内核态,在系统调用发生前需要保存用户态的栈和内存环境,然后转入内核态工作。 系统调用结束后,又要切换回用户态。这种环境的切换会消耗掉许多时间库函数访问文件的时候根据需要,设置不同类型的缓冲区,从而减少了直接调用 IO 系统调用 的次数,提高了访问效率。 总结:大多数库函数的本质也是系统调用,只不过库函数有了缓冲区,用于减少系统调用的次数