系统调用
OS API:操作系统的各个模块提供给上层开发App用的函数
API:Application Programming Interface 应用程序编程接口
接口:函数声明+调用函数所需的辅助信息的统称,以头文件形式提供
OS API ---- 系统调用 ---- 对应函数被调用时,实际是调用一个操作系统提供的功能
UNIX家族的操作系统:
1) System V: AIX HPUX
2) BSD:FreeBSD OpenBSD NetBSD
3) 类UNIX:Linux
分类:
1) 任务控制 ------ OS任务管理模块提供
进程
线程
2) 文件系统控制 — OS文件管理模块、OS设备管理模块提供
3) 系统控制 ---- 杂类
4) 内存管理 ----- OS内存管理模块提供
malloc — C库
真正动态分配的核心在操作系统里 — brk
5) 网络管理 ---- OS网络管理模块提供
6) 用户管理
7) 进程间通信 ------ OS任务管理模块提供
Unix家族OS的共同特征之一 ------- 一切皆文件
Unix家族OS的三大分支:
1) System-V系列 : AIX HPUX
2) BSD系列:FreeBSD OpenBSD NetBSD
3) 类UNIX系列:Linux Minix
1) - 普通文件,有文件内容存储在外存的文件
2) d directory 目录文件 (目录、文件夹)
3) l link 链接文件
4) p pipe 管道文件
5) b block 块设备文件
6) c char 字符设备文件
7) s socket 本地socket文件
普通文件、字符设备文件、管道文件:open close read write lseek
目录文件:opendir closedir readdir seekdir telldir mkdir rmdir
socket文件:网络编程统一的接口
Link文件:链接文件 使用那套接口要看其链接对象是哪种文件
文件系统定义:如何存放文件三种信息和如何关联文件夹和文件的手段
保存一个文件需要保存哪些信息?
1) 文件内容
2) 文件名
3) 文件属性信息 ---- 元信息
格式化:给分区指定文件系统的过程
windows:FAT32 NTFS
Linux:EXT4
int stat(const char *pathname,struct stat *statbuf)
功能:获取指定文件的元信息
参数:
pathname:指向空间存放着一个字符串,字符串内容是一个带路径的文件名,如果不带路径,默认为当前目录 值参数
statbuf:指向空间用来存放指定文件的元信息 结果参数
返回值:正常返回为0,错误返回-1
用static修饰时:
1)局部变量 ---- 改变了生存期
2) 全局变量 ---- 改变了作用域
3) 函数定义 ---- 改变了作用域
//stat结构体类型
struct stat
{
off_t st_size;//普通文件时为文件大小(bytes)
mode_t st_mode;//可以知道文件种类
。。。。//其它成员暂时不需要知道
};
//stat的使用方式
struct stat meta = {0};
stat("????/file1.dat",&meta);
使用stat函数的例子
/*
从main传参,获取指定文件的元信息
*/
#include
#include
#include
#include
int main(int argc,char *argv[]){
int ret = 0;
struct stat meta;
if(argc < 2){//argc参数小于2个退出
printf("argument too few\n");
return 1;
}
ret = stat(argv[1],&meta);//获取文件的元信息
if(ret < 0){//获取文件元信息失败退出
printf("call stat failed\n");
return 2;
}
/*
st_mode & S_IFMT结果为:
S_IFDIR:指定文件为目录文件
S_IFREG: 指定文件为普通文件
S_IFIFO:指定文件为管道文件
S_IFBLK: 指定文件为块设备文件
S_IFCHR:指定文件为字符设备文件
S_IFSOCK:指定文件为socket文件
S_IFLNK:指定文件为链接文件
*/
switch(meta.st_mode & S_IFMT){
case S_IFDIR:
printf("%s is directory\n",argv[1]);
break;
case S_IFBLK:
printf("%s is block-device\n",argv[1]);
break;
case S_IFCHR:
printf("%s is char-device\n",argv[1]);
break;
case S_IFLNK:
printf("%s is link\n",argv[1]);
break;
case S_IFIFO:
printf("%s is fifo\n",argv[1]);
break;
case S_IFREG:
printf("%s is regular file\n",argv[1]);
printf("The size of %s is %ld\n",argv[1],meta.st_size);
break;
case S_IFSOCK:
printf("%s is socket\n",argv[1]);
break;
default:
printf("type is unkonw\n");
break;
}
return 0;
}
int access(const char *pathname,int mode)
功能:检查对指定文件是否具有指定操作权限
参数:
pathname:指向空间存放着一个字符串,字符串内容是一个带路径的文件名,如果不带路径,默认为当前目录值参数
mode:
R_OK,检查有没有读权限
W_OK,检查有没有写权限
X_OK,检查有没有可执行权限
F_OK,检查有没有文件是否存在 (最常用)
R_OK W_OK X_OK F_OK中几个的位或
/*R_OK | X_OK :检查是否同时具有读权限和可执行权限
返回值:如果具有相应权限则返回0,否则返回-1*/
int ret = 0;
ret = access("????/filename",F_OK);
if(ret < 0)
{//文件不存在
printf("Not exist\n");
//出错处理
}
else
{//文件存在
}
int unlink(const char *pathname)
功能:删除指定文件
参数:
pathname:指向空间存放着一个字符串,字符串内容是一个带路径的文件名,如果不带路径,默认为当前目录 值参数
返回值:删除成功返回0,否则返回-1
int mkdir(const char *pathname,mode_t mode)
功能:创建一个空目录
参数:
pathname:指向空间存放着一个字符串,字符串内容是一个带路径的新目录名,如果不带路径,默认为当前目录 值参数
mode:指定新建目录的使用权限
指定方式1:宏常量位或法(man 2 open)
S_IRWXU S_IRWXG S_IRWXO
S_IRUSR S_IRGRP S_IROTH
S_IWUSR S_IWGRP S_IWOTH
S_IXUSR S_IXGRP S_IXOTH
S_IRWXU | S_IRWXG | S_IRWXO:所有用户具有读写可执行权限
指定方式2:三位八进制数法
0777 – 111 111 111 所有用户具有读写可执行权限
0543 – 101 100 011 主人可读可执行,小组用户可读,其它用户可写可执行
返回值: 成功返回0,否则返回-1
/*
检查文件是否存在,不存在则创建目录文件,存在则判断是目录文件还是普通文件,
若是普通文件输出文件名及文件大小
*/
#include
#include
#include
#include
int main(int argc,char *argv[])
{
int ret = 0;
struct stat meta;
if(argc < 2){
printf("argument too few\n");
return 1;
}
ret = access(argv[1],F_OK);//判断文件是否存在
if(ret != 0){//文件不存在
mkdir(argv[1],0777);//创建文件
}else{//文件存在
stat(argv[1],&meta);//获取文件的元信息
if((meta.st_mode & S_IFMT) == S_IFREG){
printf("The size of %s is %ld\n",argv[1],meta.st_size);
}else{
printf("%s is not a regular file\n",argv[1]);
}
}
return 0;
}
int rmdir(const char *pathname)
功能:删除一个空目录
参数:
pathname:指向空间存放着一个字符串,字符串内容是一个带路径的目录名,如果不带路径,默认为当前目录 值参数
返回值:成功返回0,否则返回-1
DIR *opendir(const char *pathname)
功能:打开指定的目录,以便于对其做下一步操作
参数:
pathname:其指向空间存在着一个字符串 字符串内容为带路径的目录名(无路径则为当前目录下)
返回值:
成功返回一个有效地址值,此后以此地址值代表已打开的目录
失败返回NULL
int closedir(DIR *dirp)
功能:关闭已打开的目录,此后该地址值不再代表opendir关联的目录
参数:
dirp:代表已打开目录的DIR类型的地址值
返回值:
成功0,失败-1
struct dirent *readdir(DIR *dirp)
功能:读目录指示器当前指示的子内容,并且将指示器指示到下一个内容
参数:
dirp:代表已打开目录的DIR类型的地址值
返回值:
成功返回static局部变量(变量类型为struct dirent)的地址,该变量中存放读出的子内容的信息
失败返回NULL
备注:后一次调用会将前一次的结果覆盖
struct dirent
{
char d_name[256];//存放着子内容的名字
unsigned char d_type;//存放着子内容的种类
//DT_BLK DT_CHR DT_DIR DT_REG DT_LNK
//DT_FIFO DT_SOCK DT_UNKNOWN
//其它成员
};
使用readdir函数读取目录内的内容的例子
/*
判断一个带路径文件内的内容,如果是.h或.c文件还要输出文件大小
*/
/*1. 编码时不知具体大小,只有执行到某处通过计算才能知道具体大小,此时应该动态分配
2. 编码时知道具体大小,但所需空间太大,仍然要用动态分配*/
#include
#include
#include
#include
#include
#include
#include
int IsEndCorH(const char *str);//函数功能:判断文件是否是.h或.c文件
int main(int argc,char *argv[]){
DIR *pd = NULL;//创建一个文件指针
struct dirent *pContent= NULL;//为了获取文件夹内容,所使用的结构体指针
struct stat meta;//该变量中存放元信息
char *pathname = NULL;
int len = 0;
if(argc < 2){//参数少于2个退出
printf("argument too few\n");
return 1;
}
/*打开目录*/
pd = opendir(argv[1]);
if(NULL == pd){
printf("opendir failed\n");
return 2;
}
pContent = readdir(pd);//读目录下的内容
while(pContent != NULL){//成功读取到一个文件
if(pContent->d_type == DT_REG && IsEndCorH(pContent->d_name)){
//计算带路径的普通文件名所需空间大小
len = strlen(argv[1]) + strlen(pContent->d_name) + 2;//加2的原因是字符串以'\0'结尾①,下面要用的组合路径'/'也占位置②
pathname = (char *)malloc(len);
if(NULL == pathname){
printf("Malloc failed\n");
}else{
/*组合带路径的普通文件名*/
memset(pathname,0,len);
strcpy(pathname,argv[1]);
strcat(pathname,"/");//加2的原因在这①
strcat(pathname,pContent->d_name);//加2的原因也在这②
stat(pathname,&meta);//获取元信息
printf("The size of %s is %ld\n",pContent->d_name,meta.st_size);
free(pathname);
pathname = NULL;
}
}
pContent = readdir(pd);//读目录里的下一个内容
}
closedir(pd);
pd = NULL;
return 0;
}
int IsEndCorH(const char *str){
int len = strlen(str);
const char *p = NULL;
if(len < 2){
return 0;
}
p = str + strlen(str) - 2;//减2的原因是指针回退要找到.c和.h文件
if(strcmp(p,".c") == 0 || strcmp(p,".h") == 0){
return 1;
}else{
return 0;
}
}
/*
读取一个目录下的所有内容,包括子目录下的内容(递归调用)
*/
#include
#include
#include
#include
int DisplayDir(const char *dirname);
int main(int argc,char *argv[]){
if(argc < 2){//参数少于2个退出
printf("argument too few\n");
return 1;
}
DisplayDir(argv[1]);
return 0;
}
int DisplayDir(const char *dirname){
DIR *pd = NULL;
struct dirent *pt = NULL;
char *path = NULL;
int len = 0;
pd = opendir(dirname);
if(NULL == pd){//打开目录失败
return -1;
}
pt = readdir(pd);//读取一个目录内容
while(pt != NULL){
if(pt->d_type == DT_DIR &&
strcmp(pt->d_name,".") != 0 &&
strcmp(pt->d_name,"..") != 0)
{//该内容为目录
len = strlen(dirname) + strlen(pt->d_name) + 2;//分配空间
path = (char *)malloc(len);
//组合带路径的新目录名
if(path != NULL){
memset(path,0,len);
strcpy(path,dirname);
strcat(path,"/");
strcat(path,pt->d_name);
DisplayDir(path);//继续取目录内的内容,(递归调用)
free(path);//释放存放新目录名的空间
path = NULL;
}else{
printf("Malloc Failed\n");
}
}else{//该内容不是目录
printf("%s/%s\n",dirname,pt->d_name);//显示路径和文件名
}
pt = readdir(pd);
}
closedir(pd);
return 0;
}
/*
判断给定的文件是否存在,如果不存在创建一个空目录,
如果存在是普通文件则显示文件名和文件大小,
如果存在是目录文件则显示该目录下所有子内容的种类和名字(不要求显示子目录的内容)
*/
#include
#include
#include
#include
#include
int DisplayDir(const char *dirname);
int main(int argc,char *argv[])
{
int ret = 0;
struct stat meta;
if(argc < 2){
printf("argument too few\n");
return 1;
}
ret = access(argv[1],F_OK);//查看文件是否存在
if(ret < 0){//文件不存在,则创建新文件
mkdir(argv[1],0777);
}else{
stat(argv[1],&meta);
switch(meta.st_mode & S_IFMT){
case S_IFREG:
printf("The size of %s is %ld\n",argv[1],meta.st_size);
break;
case S_IFDIR:
DisplayDir(argv[1]);
break;
default:
printf("This is other type file\n");
break;
}
}
return 0;
}
int DisplayDir(const char *dirname)
{
DIR *pd = NULL;
struct dirent *pt = NULL;
pd = opendir(dirname);//打开目录文件
if(NULL == pd){
perror("opendir failed");
return -1;
}
pt = readdir(pd);//读取目录的第一个内容
while(pt != NULL){
switch(pt->d_type){
case DT_REG:
printf("The %s file is regular file\n",pt->d_name);
break;
case DT_DIR:
printf("The %s file is directory file\n",pt->d_name);
break;
case DT_SOCK:
printf("The %s file is socket file\n",pt->d_name);
break;
case DT_FIFO:
printf("The %s file is fifo file\n",pt->d_name);
break;
case DT_CHR:
printf("The %s file is char-device file\n",pt->d_name);
break;
case DT_BLK:
printf("The %s file is block-device file\n",pt->d_name);
break;
case DT_LNK:
printf("The %s file is link file\n",pt->d_name);
break;
default:
printf("The %s file is unknow type file\n",pt->d_name);
break;
}
pt = readdir(pd);//读取目录的下一个内容
}
return 0;
}
原生的IO操作函数,其它高级语言的IO操作都是直接或间接调用本套函数实现的,例如:C库中f开头的函数。
本套接口主要侧重于管道文件和字符设备文件,虽然也可以操作普通文件,
但相比于C库中f开头的文件操作函数而言非常不方便。
操作普通文件都是使用C库函数,特殊文件采用原生系统调用
文件的位置指示器:
int open(const char *pathname,int flags)
int open(const char *pathname,int flags,mode_t mode)//当需要创建新文件时调用
功能:按照指定的模式打开指定的文件,以便于后续操作
参数:
pathname:指向空间存在着一个字符串,字符串内容为带路径文件名(无路径则为当前目录)
mode:新建文件时,指定新文件操作权限,同mkdir函数的mode参数
flags:打开文件的模式标记,O_RDONLY O_WRONLY O_RDWR:三者选其一,然后与下列标记选择性进行位或:
O_CREAT: 当有O_WRONLY或者O_RDWR标记,而文件不存在时,有此标记则表示需要创建新文件,只要有此标记则应调用三参数open函数
O_APPEND:有此标记,写操作总是在文件尾追加新内容
O_TRUNC:当支持写操作时,有此标记表示需要清空文件原内容
O_NONBLOCK:有此标记表示对该文件的读写操作为非阻塞,对普通文件的读写操作总是非阻塞的,因此该标记主要针对管道文件和字符设备而言
返回值:
成功返回一个大于等于0的整数,而该整数在此后代码中用代表被操作的文件,这个整数被称为描述符(文件描述符),描述符的取值范围>=0
失败返回-1
几个特殊的描述符:
0 标准输入设备 ----- stdin 是C库中普通文件操作函数帮我们定义好的FILE *类型的全局变量
1 标准输出设备 ----- stdout
2 标准错误设备 ----- stderr
说明:
1. 这三个特殊文件,是系统默认为每个程序main函数调用前就已open的,在程序中可以直接用这三个数字代表的特殊设备文件
2. 从标准输入读一个字符,可以用两种手段
char ch = ‘\0’;
ch = getchar();
read(0,&ch,1);
3. 向标准输出显示一个字符,可以用两种手段
putchar(‘k’);
char ch = ‘k’;
write(1,&ch,1);
int fd = -1;
fd = open(....);
if(fd < 0)
{
//出错处理
}
fopen(char *filename.char *mode)
“r” ----> O_RDONLY
“w” ----> O_WRONLY | O_CREAT | O_TRUNC
“a” ----> O_WRONLY | O_CREAT | O_APPEND
“r+” ----> O_RDWR
“w+” ----> O_RDWR | O_CREAT
“a+” ----> O_RDWR | O_CREAT | O_APPEND
int close(int fd)
功能:关闭已打开的文件,此后fd中描述符不再代表已打开的文件
参数:
fd:代表已打开文件的描述符
返回值:
成功为0,失败-1
//关闭文件后要把指针重新赋值-1
close(fd);
fd = -1;
ssize_t read(int fd,void *buf,size_t count)
功能:从已打开文件中读取数据到指定的内存空间中
参数:
fd:代表已打开的文件的描述符
buf:用来存放数据的内存空间的首地址
count:本次调用期望读的数据总字节数
返回值:
成功返回本次调用实际读到的字节数
0: 到文件尾(普通文件而言)
错误-1
备注:
如果打开文件时,没有O_NONBLOCK标记,该函数读不到数据时会阻塞(普通文件无效),即等待有新数据产生
如果打开文件时,有O_NONBLOCK标记,该函数读不到数据时非阻塞,即立即返回错误
ssize_t write(int fd,const void *buf,size_t count)
功能:将指定内存空间中的数据写入到已打开文件中
参数:
fd:代表已打开的文件的描述符
buf:被写数据所在的内存空间的首地址
count:本次调用期望写的数据总字节数
返回值:
成功返回本次调用实际写成功的字节数
错误-1
备注:
如果打开文件时,没有O_NONBLOCK标记,该函数当设备缓冲区满时会阻塞(普通文件无效),即等待缓冲区有空余空间可以继续写
如果打开文件时,有O_NONBLOCK标记,该函数当设备缓冲区满时非阻塞,即立即返回错误
/*
实现mycat,完成将一个普通文件内容显示到命令行
要求读文件内容、文件内容输出到命令行全部用系统调用实现
文件名通过main函数参数传入
*/
#include
#include
#include
#include
#include
int main(int argc,char *argv[])
{
int fd = -1;
char ch = '\0';
int ret = 0;
if(argc < 2){//参数少于2退出
printf("argument too few\n");
return 1;
}
fd = open(argv[1],O_RDONLY);//自读形式打开文件
if(fd < 0){//打开文件失败退出
printf("open %s failed\n",argv[1]);
return 2;
}
ret = read(fd,&ch,1);//读取文件一个字节内容到ch
while(ret == 1){
write(1,&ch,1);//1 标准输出设备stdout,把ch里的信息写到标准输出设备
ret = read(fd,&ch,1);//读取下一个字节
}
close(fd);//关闭文件
fd = -1;
return 0;
}
/*
实现mycp,完成将一个普通文件内容拷贝到另一个文件
文件名通过main函数参数传入
*/
#include
#include
#include
#include
#include
#include
int main(int argc,char *argv[])
{
int fdr = -1;
int fdw = -1;
char ch = '\0';
int ret = 0;
if(argc < 3){//参数少于3个退出
printf("argument too few\n");
exit(1);
}
fdr = open(argv[1],O_RDONLY);//以只读形式打开文件
if(fdr < 0){//文件打开失败退出
printf("open %s failed\n",argv[1]);
return 2;
}
fdw = open(argv[2],O_WRONLY | O_CREAT | O_TRUNC,0666);//以只写形式打开文件,文件不存在时创建文件
if(fdw < 0){//打开文件失败退出
printf("open %s failed\n",argv[2]);
return 3;
}
ret = read(fdr,&ch,1);//从文件读取一个字节内容到ch
while(ret == 1){
ret = write(fdw,&ch,1);//把ch里的信息写入到fdw描述符所指的文件中
if(ret != 1) {//文件写入失败就不再输出
printf("write data error\n");
break;
}
ret = read(fdr,&ch,1);//从文件读下一个字节
}
close(fdr);//关闭文件
fdr = -1;
close(fdw);
fdw = -1;
return 0;
}
off_t lseek(int fd,off_t offset,int whence)
功能:对于有位置指示器文件,该函数用来改变位置指示器的指示位置
参数:
fd:代表已打开的文件的描述符
whence:
SEEK_SET 将读写位置指向文件头后再增加offset个位移量。
SEEK_END 以目前的读写位置往后增加offset个位移量。
SEEK_CUR 将读写位置指向文件尾后再增加offset个位移量。
offset:相对于指定文件的偏移量
返回值:
成功返回指示器新的指示位置
失败-1
int loc = 0;
loc = lseek(fd,0,SEEK_CUR); //与ftell功能相同
/*
有一个Person结构体含如下成员:人名、性别、职业,定义并初始化
一个含5个该结构体类型元素的数组,
将这些元素保存到文件person.dat中
(前4字节存放男士数目,紧接4个字节存放女士数目,后续存放每个Person的数据)
然后再从文件中读出所有女性的信息
要求所有文件读写用原生系统调用函数完成
*/
#include
#include
#include
#include
#include
#include
#include
struct Person
{
char name[20];
char sex;
char worker[32];
};
#define N 5
struct Person *ReadAllFemale(int fd,int *numout);
int main()
{
struct Person arr[N] = {{"XiaoCai",'F',"Maiguoze"},
{"Laote",'M',"Dazuiba"},
{"XiaoFang",'F',"Teacher"},
{"LaoLiu",'M',"Manager"},
{"Bingbing",'F',"Mingxing"}
};
int malenum = 0;//男人数
int femalenum = 0;//女人数
int i = 0;
int fd = -1;//打开文件的返回值
int ret = 0;//write函数的返回值
struct Person *pst = NULL;
for(i = 0;i < N;i++)//统计男女人数
{
if(arr[i].sex == 'F')//统计女人数
{
femalenum++;
}
else if(arr[i].sex == 'M')//统计男人数
{
malenum++;
}
}
do
{
fd = open("person.dat",O_RDWR | O_CREAT | O_TRUNC,0666);//以写的方式打开文件
if(fd < 0)//打开文件失败
{
perror("open failed");
break;
}
ret = write(fd,&malenum,sizeof(int));//写入男人数
if(ret != sizeof(int))//写入文件的字节数不符合预期要写入的字节数,即写入文件失败
{
perror("write malenum error");
break;
}
ret = write(fd,&femalenum,sizeof(int));//写入女人数
if(ret != sizeof(int))
{
perror("write femalenum error");
break;
}
ret = write(fd,arr,sizeof(arr));//写入所有person信息
if(ret != sizeof(arr))
{
perror("write person data error");
break;
}
pst = ReadAllFemale(fd,&femalenum);//读取所有女人信息
if(NULL == pst)//读取失败后的处理
{
printf("read female num failed\n");
break;
}
for(i = 0;i < femalenum;i++)//读取成功后的处理
{
printf("Name:%s,Sex:%c,Work:%s\n",(pst+i)->name,(pst+i)->sex,(pst+i)->worker);
}
}while(0);//这个目的是为了好处理,简化if---else
if(fd >= 0)//关闭文件,因为打开文件失败时-1不用关闭文件
{
close(fd);
fd = -1;
}
if(pst != NULL)//因为申请空间失败不用释放空间
{
free(pst);
pst = NULL;
}
return 0;
}
struct Person *ReadAllFemale(int fd,int *numout)
{
int num = 0;
int ret = 0;
struct Person *pst = NULL;
struct Person data = {""};
int i = 0;
lseek(fd,sizeof(int),SEEK_SET);//让指示器指示到4号字节,SEEK_SET表示从文件开始的位置
ret = read(fd,&num,sizeof(int));//读取女人数,之后位置指示器指示到8号字节
if(ret != sizeof(int))
{
perror("read famalenum error");
return NULL;
}
if(num <= 0)
{
printf("No female data\n");
return NULL;
}
*numout = num;//女人数
//申请大小空间为女人数*person大小的空间
pst = (struct Person *)malloc(num * sizeof(struct Person));
if(NULL == pst)//申请空间失败后的处理
{
perror("Malloc Failed");
return NULL;
}
memset(pst,0,num * sizeof(struct Person));//申请空间成功后的处理
ret = read(fd,&data,sizeof(data));//读取一个人的信息
while(ret == sizeof(data))//读取的信息没有丢失或增多,即读取成功
{
if(data.sex == 'F')//女
{
memcpy(pst+i,&data,sizeof(data));
i++;
}
ret = read(fd,&data,sizeof(data));//读取下一个人的信息
}
return pst;//返回申请空间的首地址
}