系统调用概述
系统调用是操作系统提供给用户程序的一组“特殊”接口。程序可以通过这些接口向内核请求服务。这些接口被称为系统调用。
系统调用按照功能逻辑大致可分为: 进程控制、进程间通信、文件系统控制、系统控制、内存管理、网络管理、socket控制、用户管理。
系统调用I/O函数
系统调用中操作I/O的函数,都是针对文件描述符的。通过文件描述符可以直接对相应的文件进行操作。如:open、close、write 、read、ioctl
文件描述符是非负整数。打开现存文件或新建文件时,系统(内核)会返回一个文件描述符。文件描述符用来指定已打开的文件。
默认打开的三个文件描述符:
#define STDIN_FILENO 0 //标准输入的文件描述符
#define STDOUT_FILENO 1 //标准输出的文件描述符
#define STDERR_FILENO 2 //标准错误的文件描述符
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);
参数:
pathname:文件的路径及文件名。
flags:open函数的行为标志。
mode:文件权限(可读、可写、可执行)的设置。
返回值:
成功返回打开的文件描述符。
失败返回-1,可以利用perror去查看原因
flag取值 | 意义 |
---|---|
O_RDONLY | 以只读的方式打开 |
O_WRONLY | 以只写的方式打开 |
O_RDWR | 以可读、可写的方式打开 |
O_CREAT | 文件不存在则创建文件,使用此选项时需使用mode说明文件的权限 |
O_EXCL | 如果同时指定了O_CREAT,且文件已经存在,则出错 |
O_TRUNC | 如果文件存在,且为只读或只写的方式打开,则清空文件内容 |
O_APPEND | 写文件时,数据添加到文件末尾 |
O_NONBLOCK | 当打开的文件是FIFO、字符文件、块文件时,此选项为阻塞标志位 |
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去查看原因
库函数
库函数一般是指编译器提供的可在c源程序中调用的函数。
库函数与系统调用的关系:
并不是所有的系统调用都被封装成了库函数,系统提供的很多功能都必须通过系统调用才能实现。
系统调用函数与库函数各自有什么优缺点?
系统调用优点:
1、当要处理的数据量比较大时,例如当输入输出的数据量超过文件系统定义的尽寸时,利用系统调用可获得较高的效率。
2、 从程序执行效率来看,系统调用的执行效率大多要比函数高,尤其是处理输入输出的函数。
3、用于内核空间的驱动程序中对设备的操作
系统调用缺点:
1、系统调用是使用时间的,频繁进行系统调用会降低程序运行效率
2、移植性差
库函数优点
1、设置不同类型缓冲区,减少之间调用IO调用的次数,提高了访问效率
2、可移植性好
库函数缺点
1、不可能用于内核空间的驱动程序中对设备的操作
2、一般情况下,执行效率不如系统调用
标准I/O库函数
打开流
#include <stdio.h>
FILE* fopen(const char *pathname,
const char *mode);
函数说明:
pathname: 文件的路径及文件名。
mode: 流的打开方式。
返回值:
成功:返回指向该流的指针。
失败:则返回NULL,并把错误代码存在errno中
关闭流
#include <stdio.h>
int fclose(FILE *stream);
函数说明:
fclose用来关闭fopen打开的文件。此动作会让缓 冲区的数据写入文件中,并释放系统所提供的文件资源。
返回值:
成功返回0;失败返回EOF,并把错误代码存到errno中
读、写流
每次读、写一个字符
int getchar(void);
int getc(FILE *stream);
int fgetc(FILE *stream);
int putchar(int c);
int putc(int c, FILE *stream);
int fputc(int c, FILE *stream);
每次读、写一行字符
char *gets(char *buf);
char *fgets(char *buf, int n, FILE *stream);
fgets从stream指定的文件中最多读取n-1个字符 放到buf所指向的数组中。读到换行符或文件结束符后 不再向后读,最后一个字符读入后接着写入一个空字符。
返回值:
成功返回buf;失败返回NULL
注意:
gets()丢掉输入里的换行符。
fgets()存储输入中的换行符
每次一个数据块
size_t fread(void *ptr, size_t size,size_t nobj, FILE *stream);
size_t fwrite(const void *ptr, size_t size, size_t nobj, FILE *stream);
size是数据块大小,nobj指要读取或写入的数据块个数,stream指定要操作的数据流。
注意:
两个函数返回的是实际读或写的数据的个数,而不是整个数据的字节数。
示例题目
使用系统调用函数编程,实现文件的复制(类似Linux命令cp),例如生成的可执行程序为app,运行程序时:./app a.txt b.txt
将a.txt文件内容复制到b.txt文件中
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(int argc,char **argv)
{
int fd1,fd2,count=0;
char buf[512];
if(argc < 3)
{
printf("please input ./app test1 test2");
}
fd1 = open(argv[1],O_RDONLY,0755);
if(fd1 < 0)
{
printf("error");
return -1;
}
fd2 = open(argv[2],O_RDWR|O_CREAT,0755);
if(fd2 < 0)
{
printf("error");
return -1;
}
while((count = read(fd1,buf,100))>0)
{
write(fd2,buf,count);
}
close(fd1);
close(fd2);
return 0;
}
使用库函数方法,实现上面的功能
#include <stdio.h>
int main(int argc,char **argv)
{
char *buf[1024] = {0};
if(argc < 3)
{
printf("please input ./app test1 test2");
return -1;
}
FILE *file1 = fopen(argv[1],"r");
if(file1 == NULL)
{
printf("error");
return -1;
}
FILE *file2 = fopen(argv[2],"w");
if(file2 == NULL)
{
printf("error");
return -1;
}
while(!(feof(file1)))
{
int count = fread(buf,sizeof(void),1024,file1);
if(ferror(file1))
{
perror("file1");
fclose(file1);
fclose(file2);
return -4;
}
int count2 = fwrite(buf,sizeof(void),count,file2);
if(count2 == 0)
{
perror("file2");
fclose(file1);
fclose(file2);
return -5;
}
}
fclose(file1);
fclose(file2);
return 0;
}