1.操作系统的概念
多线程多任务
操作系统(英语:Operating System,缩写:OS)是一种内置的程序,用来协作计算机的各种硬件,以与用户进行交互。常见有Windows,macOS 和开源的 Linux。
分类:操作系统可以分为桌面操作系统,手机操作系统,服务器操作系统,嵌入式操作系统(RT_THREAD)等。
嵌入式操作系统:基本上都是实时操作系统,体积较小,方便移植,主要是为了实现多任务,提高MCU的工作效率。
问题:
MCU和SOC的区别??
操作系统是人与计算机之间的接口,也是计算机的灵魂
系统--大型应用软件,帮助我们使用人员更好的操作使用底层的资源,作为用户不需要了解底层的工作原理,即可快速的上手使用产品
为什么选择Linux操作系统??
目前我们学习系统编程最好的方式就是通过linux操作系统,主要是因为开源,可裁剪
2.系统调用
系统调用主要是侧重于操作系统内部的工作实现机制,我们需要学习的都是基于linux操作系统内部的函数调用实现。
系统调用以及后续的系统编程的学习,都是去学习linux操作系统的工作原理
linux操作系统中的分层:
kernel--linux的内核,最核心的部分
Ubuntu--操作系统,包含kernel 系统调用(system calls)shell app
centos--操作系统,适合用于服务器端的开发
redhat--红帽系统
系统调用是操作系统提供给用户程序的一组“特殊”函数接口
系统调用按照功能逻辑大致可分为:
进程控制、进程间通信、线程的使用、文件系统控制、系统控制、内存管理、网络管理、 socket 控制、用户管理。(每一个不同的颜色都表示一个完整的知识体系)
系统调用的返回值:
通常,用一个负的返回值来表明错误,返回一个 0 值表明成功。错误信息存放在全局
变量 errno 中,用户可用 perror 函数打印出错信息。
可移植系统接口(POSIX):
POSIX线程
(英语:POSIX Threads,常被缩写为Pthreads)是POSIX的线程标准,定义了创建和操纵线程的一套API。
3.系统调用 I/O 函数
重点学习的函数:open、 close、 write 、 read、 ioctl
系统调用中操作 I/O 的函数,都是针对文件描述符的
通过文件描述符可以直接对相应的文件进行操作。
文件描述符
在整个工程中是唯一的,用于标识不同的文件
内核(kernel)利用文件描述符(file descriptor)来访问文件。文件描述符是非负整数。打开现存文件或新建文件时,内核会返回一个文件描述符。读写文件也需要使用文件描述符来指定待读写的文件。
POSIX 定义了 STDIN_FILENO、STDOUT_FILENO 和 STDERR_FILENO 来代替 0、1、2。这三个符号常量的定义位于头文件 unistd.h。
STDIN_FILENO--标准输入 0 scanf
STDOUT_FILENO --标准输出 1 printf
STDERR_FILENO --标准错误 2 error
目前0--2的文件描述符已经被占用了,我们自己定义的文件,内核返回的描述符都是从3开始
IO相关的函数定义
#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 去查看原因。
在我们之前学习C语言的时候会使用到文件操作中的fopen函数
FILE *fopen(const char *filename, const char *mode)
fopen函数和open函数之间的联系???
我们是在应用层中调用了fopen函数,最终起作用的是在系统调用中的open函数,通过open函数连接到内核,进一步操作底层的硬件资源部分
close函数:
关闭一个文件
#include <unistd.h>
int close(int fd);
参数:
fd 是调用 open 打开文件返回的文件描述符。
返回值:
成功返回 0。
失败返回-1,可以利用 perror 去查看原因。
write函数
把指定数目的数据写到文件
#include <unistd.h>
ssize_t write(int fd, const void *addr,size_t count);
参数:
fd:文件描述符。
addr:数据首地址。
count:写入数据的字节个数。
返回值:
成功返回实际写入数据的字节个数。
失败返回-1,可以利用 perror 去查看原因。
read函数
把指定数目的数据读到内存
#include <unistd.h>
ssize_t read(int fd, void *addr, size_t count);
参数:
fd:文件描述符。
addr:内存首地址。
count:读取的字节个数。
返回值:
成功返回实际读取到的字节个数。
失败返回-1,可以利用 perror 去查看原因。
remove函数:
删除文件
#include <stdio.h>
int remove(const char *pathname);
参数:
pathname :文件的路名+文件名。
返回值:
成功返回 0。
失败返回-1,可以利用 perror 去查看原因。
4.系统调用与库函数
库函数访问文件的时候根据需要,设置不同类型的缓冲区,从而减少了直接调用 IO系统调用的次数,提高了访问效率。
CP命令的实现:
cp [options] source dest 或 cp [选项] 源文件 目标文件
#include <stdio.h>
#include <sys/types.h> //open
#include <sys/stat.h> //open
#include <fcntl.h> //open
#include <unistd.h> //read write close
#include <errno.h> //perror
//主函数传参
//argc--代表传入的参数个数
//argv--传入的参数 ./a.out 往代码中传入了一个参数 a.out
//./a.out 5 3
//argv[1]---源
//argv[2]---目的
int main(int argc, char *argv[])
{
int fd_read=0;
int fd_write=0;
char buf[256]={0};
int ret=0;//返回值
//cp指令---将一个文件中的内容复制到另外一个文件中
//int open(const char *pathname, int flags);
fd_read=open(argv[1],O_RDONLY);
if(fd_read<0)
{
//perror("fd_read:"); //标准出错 --当你的代码出错的时候,系统会将错误信息返回
write(2, "fd_read:", 9);
}
printf("fd_read=%d\n",fd_read);
//复制的位置
//int open(const char *pathname, int flags, mode_t mode);
fd_write=open(argv[2],O_WRONLY|O_CREAT|O_TRUNC,0777);
if(fd_write<0)
{
perror("fd_write:"); //标准出错 --当你的代码出错的时候,系统会将错误信息返回
}
printf("fd_write=%d\n",fd_write);
ret=read(fd_read,buf,256);
if(ret==-1)
{
perror("read:");
}
printf("读取到的数据个数为:%d\n",ret);
ret=write(fd_write,buf,ret);
ret=0;
if(ret==-1)
{
perror("write:");
}
close(fd_read);
close(fd_write);
return 0;
}