嵌入式学习笔记--LINUX系统编程阶段--DAY02系统编程
系统编程
- 系统编程:在有操作系统的前提下进行编程,并且借助操作系统的系统调用以及各种库,对系统资源进行访问。学会了C语言之后,再知道一些系统调用的方法,就可以进行系统编程。
- 操作系统:用来管理所有硬件资源,并且将不同的设备进行关联的系统。
系统调用
类UNIX系统的软件层次
系统调用(syste call)是操作系统提供给用户的一层特殊的函数接口。
Linux的不同版本提供了两三百个系统调用。
用户程序可以通过这组接口获得操作系统内核提供的服务。
例如:
用户可以通过文件系统相关的系统调用,打开相应的文件,进行相关的操作。
系统调用按照逻辑功能大致可以分为:进程管理、进程间通信、内存管理、文件管理、设备控制、网络管理等。
系统调用的返回值:通常,系统调用通常用负的返回值来表示错误,用0来表示正确。用户可以用perror来打印错误信息。
系统调用遵循的规范:在Linux中,应用程序编程接口(API)遵循POSIX标准。
POSIX标准基于当时现有的UNIX 实践和经验,描述了操作系统的系统调用编程接口(实际上就是API),用于保证应用程序可以在源代码一级上在多种操作系统上移植运行。
例如:Linux下的write read 可以直接移植到unix中
用户态和内核态
用户态:通常情况下不允许访问内核数据,也无法使用内核函数,它们只能在用户空间操作用户数据,调用用户空间函数。
内核态:内核态是系统调用函数通过系统对应预留接口,利用内核对系统资源的管理和提供。
运行在内核态的进程可以毫无限制的访问各种资源,而在用户态下的用户进程的各种操作都有着限制,比如不能随意的访问内存、不能开闭中断以及切换运行的特权级别。
我是这么理解的:内核态就像是一个银行的金库,这个银行的金库不是谁都有权限访问,只有相应的管理员才行(操作系统内核),而其他用户就相当于用户态,用户可以存钱取钱,但是银行不可能直接把你扔进去进行存钱取钱,需要借助中间人(操作系统内核)。来进行存取操作。
系统调用与库函数的区别
如图,部分库函数是可以调用系统调用的,但是不是所有的系统调用都被封成了库函数。
库函数由两类函数组成:
1 不需要调用系统调用,不需要切换到内核空间即可完成函数全部功能,并且将结果反馈给应用程序,如strcpy 等字符串操作函数。
2 需要调用系统调用,需要切换到内核空间,这类函数通过封装系统调用去实现相应功能,如 printf、fread 等
系统调用是需要时间的,程序中频繁的使用系统调用会降低程序的运行效率。当运
行内核代码时,CPU 工作在内核态,在系统调用发生前需要保存用户态的栈和内
存环境,然后转入内核态工作。系统调用结束后,又要切换回用户态。这种环境的
切换会消耗掉许多时间 。
系统调用I/O函数
在 Linux 的世界里,一切设备皆文件。我们可以系统调用中 I/O 的函数(I:
input,输入;O:output,输出),对文件进行相应的操作( open()、close()、
write() 、read() 等)。
1.文件描述符
打开现存文件或新建文件时,系统(内核)会返回一个文件描述符,文件描述符用来指定已打开的文件。这个文件描述符相当于这个已打开文件的标号,文件描述符是非负整数,是文件的标识,操作这个文件描述符相当于操作这个文件描述符所指定的文件。
进程(程序)运行起来的时候,会产生一张文件运行表,记录当前程序打开的所有文件描述符。
每个进程会默认打开3个文件描述符。
0 标准输入设备;(scanf)
1 标准输出设备;(printf)
2 标准错误设备;(perror)
新打开的文件描述符,为最小可用文件描述符。
一个进程最多可以打开1024个文件,即描述符从0~1023
2.open函数
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); //打开不存在的文件,要给磁盘权限
函数解释:
- 返回值:
- 成功时,得到最小的可用的文件描述符
- 失败时返回-1,可以利用perror查看信息
- pathname:文件路径以及文件名
- flags:open函数的行为标志(打开的模式,读或者写)
- mode:文件权限(磁盘权限)
flag值
mode值
3.close函数
作用:关闭文件描述符
#include <unistd.h>
/*
功能:关闭已打开的文件
参数:fd : 文件描述符,open()的返回值
返回值:成功:0;失败: -1, 并设置 errno*/
int close(int fd);
4.write函数
作用:把指定内存的指定数目的数据写到文件中
#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);
参数:
fd:文件描述符
buf:内存首地址
count:写入数据的字节个数
返回值:
成功:返回写入成功的字节个数
失败:返回-1,可以用perror查看信息
5.read函数
把指定的数目的数据读到内存中
#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);
参数:
fd:文件描述符
buf:内存首地址
count:读取数据的字节个数
返回值:
成功:返回读取成功的字节个数
失败:返回-1,可以用perror查看信息
6.remove函数
删除文件
#include <stdio.h>
int remove(const char *pathname);
参数:
pathname:文件名和文件路径
返回值:
成功返回0;
失败返回-1,可以用perror查看信息
练习:使用系统调用函数实现cp指令
源文件 ./a.txt 读
目的文件:./test下的a.txt 写
代码编写后的 可执行文件 a.out
先了解外部传参:
运行结果
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include<string.h>
#include <fcntl.h>int main(int argc, char const *argv[])
{if (argc != 3){printf("./a.out ./a.txt ./test");}int fd_r = open(argv[1],O_RDONLY);if (fd_r < 0){perror("open");return 0;}printf("%d\n",fd_r);char dest_name[64] = "";strcpy(dest_name,argv[2]);strcat(dest_name,"/");strcat(dest_name,argv[1]); //將路徑拼接好,放入dest_name中printf("%s\n",dest_name); int fd_w = open(dest_name,O_WRONLY|O_CREAT,0666);while (1){char buff[128] = "";int len = read(fd_r,buff,sizeof(buff)); write(fd_w,buff,len);printf("%d\n",len);printf("%lu\n",sizeof(buff));if (len < sizeof(buff)){break;}}close(fd_r);close(fd_w);return 0;
}