1. Linux 下系统调用和库函数简介
1.1 操作系统概念
操作系统(opearting OS)是管理和控制硬件与软件的计算机程序,它是直接运行在“裸机”上的最基本的系统软件,任何其他软件都必须在操作系统的支持下才能运行。操作系统是用户和计算机的接口,同时也是计算机硬件和其他软件的接口。
1.2 操作系统的功能总结
- 操作系统的功能包括管理计算机系统的硬件、软件及数据资源。
对于程序员来讲,操作系统管理硬件资源,使得我们不必了解硬件的使用方法,而直接使用操作系统提供的接口,即系统调用去操作硬件。比如说分配内存,在屏幕上显示等。 - 控制程序运行。
- 改善人机界面等以使计算机系统所有资源最大限度地发挥作用。
1.3 操作系统提供的接口
系统给我们程序员提供了一系列接口,被称为系统调用。以便于我们不需要直接访问硬件,而是通过它提供的接口间接的操作硬件。通过提供接口访问硬件资源这种机制有许多好处:
- 用户空间进程访问内核的接口:所有的操作系统都提供多种服务的入口点,通过这些入口点,用户态程序而是要通过设置软中断的方式陷入内核才能执行内核的函数,
- 向内核请求服务把用户从底层的硬件编程中解放出来。
- 极大的提高了系统的安全性:受限地访问硬件设备资源。实际上这些接口主要是为了保护系统稳定可靠,防止应用程序恣意妄为
1.4 应用程序编程接口
而对于我们程序员而言,直接使用系统接口有诸多不便,因为系统接口提供的功能非常的单一。因此,一些前辈们开发了在系统接口之上的函数库,对于我们程序员而讲,使用这些函数进行软件开发可以大大的提供我们的开发效率。这些开发的库遵循一定的规则,我们把它称作标准。其中有一个标准叫POSIX ,POSIX的中文翻译是统一的编程接口,也就是遵循这个标准的系统的同一种语言的库,使用起来是一样的,只是底层的实现不一样。GNU C库:就是linux下 POSIX标准的实现者之一,提供基本的POSIX功能。其他POSIX功能可以由第三方的库再提供,比如pthread。
1.5 为什么不直接使用系统调用接口而使用用户编程接口(API)?
- 不同操作系统系统调用不兼容,程序移植工作量大,使用API,减少代码移植的工作。
- 系统调用接口功能简单单一(每个系统调用都应该有一个明确的用途。Linux中不提倡采用多用途的系统调用),无法满足程序要求,使用API,可以提供大量的库函数。
1.6 应用程序 使用接口 编程序的分类
由上图可以看出,我们编写应用程序使用的接口包括三种:
- 直接使用系统调用函数。
- 使用GUN C库为我们封装好的 多个系统调用组成的函数库,一完成特定的功能。
- 使用 没有使用到系统调用的 GNU C 库
1.7系统调用概念总结
系统调用是对应操作系统级别的原子操作,提供一个访问内核的最小界面。而库函数是面向应用为了方便应用某些功能实现进行了封装,内部可能调用系统调用,也可能没有。
2.文件描述符概念
2.1 linux 下一切皆文件
Linux操作系统是基于文件概念的。文件是以字符序列构成的信息载体。根据这一点,可以把I/O设备当作文件来处理。因此,与磁盘上的普通文件进行交互所用的同一系统调用可以直接用于I/O设备。这样大大简化了系统对不同设备的处理,提高了效率。
- “-”普通文件;
- “d”目录;
- “l”符号链接;
- “c” 字符设备;
- “b” 块设备;
- “p” 有名管道;
- “s” Socket文件。
2.2 文件描述符
文件描述符实际上就是操作系统对于一个文件的标识。在我们看来,我们区别文件的方式是通过文件名,但是操作系统是给了不同文件一个文件描述符,以区别不同的文件。
2.3文件描述符的特点
- 文件描述符是文件IO的的操作对象。
- 文件描述符是顺序分配(最小未用)的非负整数,内核以此来标识一个特定进程已打开的文件。每当打开一个现存文件或创建一个新文件时,内核将向进程返回一个文件描述符,以供读、写文件时使用。
- 文件描述符是per-进程的(范围)。它的作用域就是一个进程上下文。内核会在每个进程空间中维护一文件描述符表,所有打开的文件都将通过此表中的文件描述符来引用。
2.4 三个特殊的文件描述符
幻数0、1、2应被代换成符号常数STDIN_FILENO、STDOUT_FILENO、STDERR_FILENO。这些常数都定义在头文件<unistd.h>中。每个进程运行的时候都会默认打开三个文件0、1、3, 标识标准输入、标准输出、标准出错。这三个文件描述符用于标识设备 ,以和用户交互。
2.5 文件掩码
umask是控制生成文件的缺省属性。
- 一般来说,文件的默认权限是666,目录的权限是777。而如果此时umask 的值为002,那么实际创建一个文件的权限就是 666 & ~ 002 以后的权限。
- 如果umask设置为002,则新增文件的权限为664,目录的权限是775。
设定的权限(例如0664) & ~umask
得到的就是实际的权限。
即新生成的文件的读写执行权限和mode以及umask的值有关
权限 = mode & ~(umask)
注意:
- chang mode 是直接修改权限,与掩码无关
- 当前程序如果想要将umask 值修改,调用umaks(x)函数即可,x 是掩码想要改成的值。
3.unix错误处理
3.1 出错码概念
- 在unix中,当程序出现错误,会设置全局的一个错误码errno。全局出错码在errno.h中定义,全局可见。
- 错误值被定义成:EXXX形式 例如EACESS
3.2 出错处理的规则
- 如果没有出错,错误码不会被一个例程清除,只有出错时才会检查出错码
- 任何函数都不会将出错码的值设置为0
3.3 错误信息输出
- strerror () 映射error 对应的出错信息
- perror() 输出用户信息以及error对应的出错信息
3.4 perror 函数简介
#include <stdio.h>
#include <errno.h>
void perror(const char *s);
参数 为用户信息。
当执行一个函数失败时,进程会将全局的errno 变量设置成相应的值。并且我们可以通过perror函数打印上一次出错的函数的errno值,调用perror函数中需要指定的指针是用户提示的值,而errno 对应着实际出错的原因。
//错误码对应的错误信息
E2BIG Argument list too long (POSIX.1-2001).
EACCES Permission denied (POSIX.1-2001).
EADDRINUSE Address already in use (POSIX.1-2001).
EADDRNOTAVAIL Address not available (POSIX.1-2001).
EAFNOSUPPORT Address family not supported (POSIX.1-2001).
..
3.5 strerror
#include <string.h>
char *strerror(int errnum);
- 参数errnum 传入的是错误码
- 返回值char* 代表错误码对应的错误信息
使用方法,引入error.h 头文件,当出错时将error 传递给strerror即可。
一般的用法:
printf("%s",strerr(error));
cat /proc/devices 查看当前系统支持的驱动
创建设备节点
mknod c 180 0 /dev/myusb
主设备号
usb 180