一:系统调用
3~4G系统的空间,这部分是由操作系统来管理的,如果我们需要操作这块的空间,只能通过系统提供的函数才能进行访问,系统提供了大概200多个系统函数,我们把系统函数的调用称之为系统调用。
二:系统函数
我们在C代码中经常使用 printf() 函数,但是最终的输出是显示在屏幕上的,那么怎么就能够在屏幕上进行打印输出结果呢?其实,printf() 函数最终是通过调用系统函数 write(),然后调用显示驱动程序,最终显示在界面上的。write() 函数就是系统函数,我们可以通过 man 2 write 的命令来查看 write() 函数的相关用法。
三:普通函数和内核函数查看方法
open close read write 这是比较常见的四种内核函数,在Linux中,我们一般用 man 2 xx 来查看某系统函数的用法, man 3 xx 来查看普通函数的用法。
1. 比如 执行 man 2 read 就可以得到 read 内核函数的用法
2. 比如执行 man 3 fread 就可以查看 fread 普通函数的用法
四:open 函数的介绍
int open(const char *pathname, int flags, mode_t mode);
pathname: 文件的路径。
flags: 1. O_APPEND 追加内容,2. O_CREAT 没有文件的时候创建, 3.O_TRUNC 会截断,即每次打开文件的时候都会清空内容。
mode_t: 就是创建文件权限。如果需要只是当前用户可读可写的:S_IRUSR|S_IWUSR
返回的是一个文件句柄fd,我们可以直接通过fd 执行包括 read, write, close 等操作。
五:read 函数介绍
ssize_t read(int fd, void *buf, size_t count);
fd:文件句柄
void *buf:读入的缓冲区
count: 每次读入的字节数
ssize_t:返回的是读到的真实个数
六:write 函数介绍
ssize_t write(int fd, const void *buf, size_t count);
int fd:文件句柄
void *buf: 写入的缓冲区
size_t count: 每次写入到缓冲区的字节数
ssize_t: 返回的真实写入的个数
七:文件操作错误提示
文件操作错误,会有一个宏定义 errno,通过 strerror(errnum) 获取错误信息,需要打印错误快捷的方式是 perror。strerror 和 perror 都是普通函数,可以通过man 3 来查看用法。
void perror(const char *s); s 是错误信息的描述,一般使用 perror("perror msg"); 这种会将具体的错误信息跟在后面,如 perror msg: No such file or directory 。
八:拷贝文件
1. 使用普通的 fgetc(),fputc() 函数进行文件读写操作
// f_fget_fput.c
#include<stdio.h>
#include<stdlib.h>
int main(int argc, const char *argv[]){
if (argv[1] == NULL){
printf("please input inputfile name\n");
return 1;
}
if (argv[2] == NULL){
printf("please input outputfile name\n");
return 1;
}
FILE *rfp = NULL;
FILE *wfp = NULL;
// 打开文件
rfp = fopen(argv[1],"r");
wfp = fopen(argv[2],"w");
// 检查文件是否打开成功
if (NULL == rfp){
printf("Open the %s error!\n", argv[1]);
perror("perror msg"); // perror msg: No such file or directory
exit(1);
}
if (NULL == wfp){
printf("Open the %s error!\n", argv[2]);
perror("perror msg");
exit(1);
}
// 读取文件并将读取的字符保存到另外一个文件中
// 定义一个char 变量,用来打开和接收文件内容
char ch = 0;
while( (ch=fgetc(rfp)) !=EOF){
fputc(ch, wfp);
}
printf("the file %s has copied to %s\n", argv[1], argv[2]);
// 关闭文件
fclose(rfp);
fclose(wfp);
return 0;
}
zfz:01-C++简单介绍 zhangfengzhou$ gcc f_fget_fput.c -o f_fget_fput
zfz:01-C++简单介绍 zhangfengzhou$ ./f_fget_fput a.txt b.txt
the file a.txt has copied to b.txt
zfz:01-C++简单介绍 zhangfengzhou$
2. 使用内核函数 read(),write() 来进行读写操作
#include<stdio.h>
#include<unistd.h>
#include<errno.h>
#include<string.h>
#include<fcntl.h>
int main(int argc, char* argv[]){
int fd1, fd2, flag;
char buff[1];
fd1 = open(argv[1], O_RDONLY); // 打开文件失败,则异常返回
if (fd1 == -1) {
printf("%s:%s\n", strerror(errno), argv[1]);
return -1;
}
//664 表示的是权限
fd2 = open(argv[2], O_WRONLY|O_CREAT, 664); // 打开文件失败,则异常返回
if (fd2 == -1){
printf("%s:%s\n", strerror(errno), argv[2]);
return -1;
}
while ((flag= read(fd1, buff, 1))>0)
{
write(fd2, buff, flag);
}
close(fd1);
close(fd2);
return 0;
}
zfz:01-C++简单介绍 zhangfengzhou$ gcc f_read_write.c -o f_read_write
zfz:01-C++简单介绍 zhangfengzhou$ ./f_read_write a.txt b.txt
zfz:01-C++简单介绍 zhangfengzhou$
// 引入 unistd.h 就可以很简便的引入系统内核函数的头文件
#include <unistd.h>
errno 是表示创建或者访问文件错误的编号
strerro(errnum) 是将errno 编号翻译成可读的信息
在使用open函数创建文件的时候,设置的文件权限不一定是最终的文件权限,最终权限是和umask一起决定的。
3. 内核函数和普通函数的区别
其实通过测试情况来看,普通函数比内核函数执行效率更高,原因在于调用内核函数会切换到内核态,且反复切换上下文环境会比较耗时,所以内核函数就慢了,同时普通的写函数是带有缓冲区的,当缓冲区满的时候,即 buf = 4096 长度的时候,就会将数据刷新到内核区域,避免了多次进入内核区域,提高了效率,所以应该尽量减少内核函数调用。
内核函数什么时候调用?
1. 当你已经了解普通函数的实现机制,认为自己当前项目需要优化。
2. 没有较好的封装时,可以自己直接调用系统函数。
3. 有普通函数时,尽量调用普通函数。
九:系统中断
系统中断是计算机学中的一个名词,一般是指硬件中断和软件中断的综合,中断发生后,系统会停止响应,并执行中断响应。
系统中断,一般是硬件中断和软件中断的综合,“中断”是一个计算机术语,意思跟我们的请求差不多,鼠标、键盘、板卡或者是一些系统内核组件,要想为你服务,都要向系统提出申请,然后等待操作系统的分配。如果没有这个过程,你什么也干不了。CPU占用高的原因就是,系统要保持“随时”能为你提供服务,就必须保证它的优先权力。
所谓中断是指CPU对系统发生的某个事件做出的一种反应,CPU暂停正在执行的程序,保留现场后自动地转去执行相应的处理程序,处理完该事件后再返回断点继续执行被“打断”的程序。
中断可分为三类,第一类是由CPU外部引起的,称作中断,如I/O中断、时钟中断、控制台中断等。第二类是来自CPU的内部事件或程序执行中的事件引起的过程,称作异常,如由于CPU本身故障(电源电压低于1.05V或频率在47~63Hz之外)、程序故障(非法操作码、地址越界、浮点溢出等)等引起的过程。第三类由于在程序中使用了请求系统服务的系统调用而引发的过程,称作“陷入”(trap,或者陷阱)。前两类通常都称作中断,它们的产生往往是无意、被动的,而陷入是有意和主动的。
中断分为硬中断和软中断,函数调用属于软中断。
十: PCB 进程控制块
PCB 进程控制块就是一个结构体 task_struct,存在于内核中,这个结构体内有很多成员变量,其中之一就是文件描述符表。一个进程中的文件描述符表默认有三个进程描述符,STDIN(0),STDOUT(1),STDERR(2),接下来,在这个进程中每打开一次文件,就会有一个文件描述符添加到这个文件描述符表中,而每关闭一个文件,就会在文件描述符表中减少一个占用。在一个进程中,一般最大的文件打开数目是1024,但是如果文件句柄超过800就会比较危险。多次打开同一个文件而没有关闭,那么打开多少次,就会多占用多少个文件描述符表中的位置。
十一:lseek 和 fseek
十二:stat 和 lstat
stat f_fget_fput 可以查看文件的去权限(0755/-rwxr-xr-x),inode
Inode: 411892,记录文件的权限和位置
man 2 stat 查看stat 用法
stat 和 lstat的区别:stat 会穿透的,lstat 不会穿透
利用 stat 获取文件的属性,大小
#include<sys/types.h>
#include<sys/stat.h>
#include<unistd.h>
#include<stdint.h>
#include<time.h>
#include<stdio.h>
#include<stdlib.h>
#include<sys/sysmacros.h>
int main(int argc, char* argv[]){
struct stat sb;
const char* pathname = argv[1];
stat(pathname, &sb);
printf("I-node number: %ju\n", (uintmax_t) sb.st_ino);
printf("Mode: %jo (octal)\n", (uintmax_t) sb.st_mode);
printf("Link count: %ju\n", (uintmax_t) sb.st_nlink);
printf("Ownership: UID=%ju GID=%ju\n", (uintmax_t) sb.st_uid, (uintmax_t) sb.st_gid);
printf("Preferred I/O block size: %jd bytes\n", (intmax_t) sb.st_blksize);
printf("File size: %jd bytes\n",(intmax_t) sb.st_size);
printf("Blocks allocated: %jd\n",(intmax_t) sb.st_blocks);
printf("Last status change: %s", ctime(&sb.st_ctime));
printf("Last file access: %s", ctime(&sb.st_atime));
printf("Last file modification: %s", ctime(&sb.st_mtime));
return 0;
}
[root@localhost c_learn]# gcc stat_learn.c -o stat_learn
[root@localhost c_learn]# ./stat_learn a.txt
I-node number: 411895
Mode: 100644 (octal)
Link count: 1
Ownership: UID=0 GID=0
Preferred I/O block size: 4096 bytes
File size: 12 bytes
Blocks allocated: 8
Last status change: Mon Apr 19 20:36:13 2021
Last file access: Mon Apr 19 20:36:39 2021
Last file modification: Mon Apr 19 20:36:13 2021
十三:目录 opendir
man 3 opendir 查看 opendir 函数
#include<stdio.h>
#include<errno.h>
#include<dirent.h>
#include<sys/stat.h>
#include<stdbool.h>
bool isdirs(const char* fileName){
struct stat buf;
stat(fileName, &buf);
mode_t st_mode = buf.st_mode;
return S_ISDIR(st_mode);
}
int main(int argc, char* argv[]){
// 打开目录
DIR *dir = opendir(argv[1]);
if (dir==NULL){
printf("open dir error\n");
return -1;
}
struct dirent* dirp;
while((dirp=readdir(dir))!=NULL){
//printf("%s\n", dirp->d_name);
bool isdir = isdirs(dirp->d_name);
printf("%d %s\n", isdir, dirp->d_name);
}
return 0;
}
[root@localhost c_learn]# ./opendir xuexi
0 orange.txt
1 ..
1 .
0 apple.txt
[root@localhost c_learn]#
注意:C语言中没有bool 类型,如果要使用,需要引入头文件 #include<stdbool.h> 。