- A+
系统调用
系统调用: 操作系统提供给用户程序调用的一组“特殊”接口,用户程序可以通过这组“特殊”接口来获得操作系统内核提供的服务
为什么用户程序不能直接访问系统内核提供的服务为了更好地保护内核空间,将程序的运行空间分为 内核空间 和 用户空间(也就是常称的内核态和用户态),它们分别运行在不同的级别上 在逻辑上是相互隔离的 。 因此 用户进程在通常情况下不允许访问内核数据 ,也无法使用内核函数,它们只能在用户空间操作用户数据 ,调用用户空间的函数 。
进行系统调用时 ,程序运行空间从用户空间进入内核空间 ,处理完后再返回到用户空间系统调用并不是直接与程序员进行交互的,它仅仅是一个通过软中断机制向内核提交请求,以获取内核服务的接口 。在实际使用中程序员调用的通常是用户编程接口 API 。
Linux 中的系统调用包含在 Linux 的 libc 库中,通过标准的 C 函数调用方法可以调用系统命令相对 API 更高了一层,它实际上是一个可执行程序,它的内部调用了用户编程接口 (API )来实现相应的功能 。内核如何区分和引用特定的文件
通过文件描述符 。文件描述符是一个非负的整数 ,是一个索引值 ,指向在内核中每个进程打开文件的记录表 。 当打开一个现存文件或创建一个新文件时,内核就向进程返回一个文件描
述符;当需要读写文件时 也需要把文件描述符作为参数传递给相应的函数 。是一个非负的整数(通常是小整数),posix标准要求每次打开一个文件,必须使用当前进程最小的文件描述符号码,因此打开一定是3.
一个进程启动时 通常会打开 3 个文件:
标准输入 | 标准输入 | 标准出错 |
---|---|---|
STDIN_FILENO | STDOUT_FILENO | STDERR_FILENO |
stdin | stdout | stderr |
#include <stdio.h> #include <string.h> #include <unistd.h> #define MSG_STR "hello worldn" int main(int argc, char **argv) { printf("%s",MSG_STR); fputs(MSG_STR,stdout); write(STDOUT_FILENO,MSG_STR,strlen(MSG_STR));//标准输出到屏幕MSG_STR return 0; }
关于fputs和weitey
fputs(const char *s, FILE *stream);
write(int fd, const void *buf, size_t count);
文件的创建/打开
open() 函数用于打开或者创建文件。其在打开或者创建文件时可以指定文件的属性及用户的权限等各种参数。
int open(const char *pathname, int flags);//打开已存在的文件 int open(const char *pathname, int flags, mode_t mode);//打开不存在的文件 //flags:read write操作文件的权限 //mode:该文件在磁盘中 相对于 用户的权限 int open(const char *path, int oflag, [mode_t mode]); args: const char *path: 文件路径,可以是绝对,也可以是相对路径 int oflag : 文件打开的方式 - O_RDONLY 只读打开 - O_WRONLY 只写打开 - O_RDWR 可读可写打开 以上3种必选一个,以下4种可以任意选择 - O_APPEND 追加打开,所写数据附加到文件末 - O_CREAT 若此文件不存在则创建它 - O_EXCL 若文件存在则报错返回 - O_TRUNC 如果文件已存在,并且以只写或可读可写方式打开 则将其长度截断为0字节 [mode_t mode] : 文件权限,只有在创建文件时需要使用 return: 文件描述符,非负整数是成功,-1是失败
#include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> int main(int argc ,char **argv) { int fd = -1; if((fd = open("test.txt",O_CREAT|O_RDWR,0666))<0) if(-1==fd) { printf("文件创建失败n"); } else { printf("文件打开成功,fd = %dn",fd); } return 0; }
写文件
当文件打开后,我们就可以向该文件写数据了。在Linux系统中,用 write() 向打开的文件写入数据,要使用这个函数,需要包含 #include <unistd.h> 。下面是函数的说明:
ssize_t write(int fildes, const void *buf, size_t nbyte); args: int fildes : 写入文件的文件描述符 const void *buf: 写入数据在内存空间存储的地址 size_t nbyte : 期待写入数据的最大字节数 return: 文件实际写入的字节数,非负整数是成功,-1是失败(磁盘已满或者超出该文件的长度等)
if((rv = write(fd, MSG_STR,strlen(MSG_STR)))<0) if(rv == -1) { printf("写入数据失败n"); } else { printf("写入数据成功n"); }
读文件
同写文件类似,要使用读文件函数 read() ,需要包含 #include <unistd.h> 。下面是函数的说明:
ssize_t read(int fildes, void *buf, size_t nbyte); args: int fildes : 读取文件的文件描述符 void *buf : 读取数据在内存空间存储的地址 size_t nbyte: 期待读取数据的最大字节数 return: 文件实际读取的字节数,非负整数是成功,-1是失败
#include <stdio.h> #include <string.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #define BUFSIZE 1024 #define MSG_STR "i love linuxn" int main(int argc ,char **argv) { int fd = -1; int rv = -1; char buf[BUFSIZE]; if((fd = open("test.txt",O_CREAT|O_RDWR,0666))<0) if(fd==-1) { printf("文件创建失败n"); } else { printf("文件打开成功,fd = %dn",fd); } if((rv = write(fd, MSG_STR,strlen(MSG_STR)))<0) if(rv == -1) { printf("写入数据失败n"); } else { printf("写入数据成功n"); } memset(buf,0,sizeof(buf));//把buf的值为0, if((rv = read(fd,buf,sizeof(buf)))<0) if(rv == -1) { printf("读写失败n"); } else { printf("读写成功n"); goto cleanup; } printf("读写的数据 %dn %sn",rv,buf); return 0; }
但是这样读出是空的,所以要使用lseek文件偏离量,通俗来说:就是改变光标的位置,从哪里读数据.
memset函数
memset是计算机中C/C++语言初始化函数。作用是将某一块内存中的内容全部设置为指定的值, 这个函数通常为新申请的内存做初始化工作。
void *memset(void *s, int ch, size_t n); str -- 指向要填充的内存块。 c -- 要被设置的值。该值以 int 形式传递,但是函数在填充内存块时是使用该值的无符号字符形式。 n -- 要被设置为该值的字符数。
文件的偏移量
在每个打开的文件中都有一个文件的偏移量,文件的偏移量会根据文件的读写而改变位置。我们可以通过 lseek() 函数来调整文件的偏移量。默认情况下,新打开文件的文件偏移量在文件的开始。同 write() 和 read() 函数类似,要使用这个函数,需要包含 #include <unistd.h> 。下面是函数的说明:
off_t lseek(int fildes, off_t offset, int whence); args: int fildes : 修改文件的文件描述符 off_t offset: 文件偏移量移动的距离 int whence : 文件偏移量的基址 - SEEK_SET 文件开始处 - SEEK_CUR 文件当前位置 - SEEK_END 文件结束处 return: 当前文件指针的位置,非负整数是成功,-1是失败
lseek(fd , 0 ,SEEK_SET);//将文件偏移量设置到文件开始第一个字节上 lseek(fd , -1 ,SEEK_END);//将文件偏移量设置在文件倒数第一个字节上
在数据写入成功后,把文件偏移量移到文件开始第一行字节上
if((rv = write(fd, MSG_STR,strlen(MSG_STR)))<0) if(rv == -1) { printf("写入失败n"); } else { printf("写入数据成功n"); goto cleanup; } lseek(fd,0,SEEK_SET);
关闭文件
当文件不再被使用时,可以调用 close() 函数来关闭被打开的文件。 除了用 close() 显示地关闭文件外,通过结束进程也能隐式地关闭被该进程打开的所有文件。要使用该函数,需要包含 #include <unistd.h> 。下面是函数的说明:
int close(int fildes); args: int fildes: 要关闭文件的文件描述符 return: 文件关闭状态,0是成功,-1是失败
代码实现
#include <stdio.h> #include <string.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #define BUFSIZE 1024 #define MSG_STR "i love linuxn" int main(int argc ,char **argv) { int fd = -1; int rv = -1; char buf[BUFSIZE]; if((fd = open("test.txt",O_CREAT|O_RDWR,0666))<0) if(fd==-1) { printf("文件创建失败n"); goto cleanup; } else { printf("文件打开成功,fd = %dn",fd); } if((rv = write(fd, MSG_STR,strlen(MSG_STR)))<0) if(rv == -1) { printf("写入数据失败n"); goto cleanup; } else { printf("写入数据成功n"); } lseek(fd,0,SEEK_SET); memset(buf,0,sizeof(buf)); if((rv = read(fd,buf,sizeof(buf)))<0) if(rv == -1) { printf("读写失败n"); goto cleanup; } else { printf("读写成功n"); } printf("读写的数据 %dn %sn",rv,buf); cleanup: close(fd); return 0; }
strerror函数
C 库函数 char *strerror(int errnum) 从内部数组中搜索错误号 errnum,并返回一个指向错误消息字符串的指针。strerror 生成的错误字符串取决于开发平台和编译器。
char *strerror(int errnum)
进一步代码实现
#include <stdio.h> #include <string.h> #include <fcntl.h> #include <errno.h> #include <sys/stat.h> #include <sys/types.h> #define BUFSIZE 1024 #define MSG_STR "hello worldn" int main(int argc ,char **argv) { int fd = -1; int rv = -1; char buf[BUFSIZE]; fd = open("test.txt",O_RDWR|O_CREAT|O_TRUNC,0666); if(fd < 0) { perror("创建/打开文件失败"); return 0; } printf("打开文件成功 [%d]n",fd); if((rv = write(fd,MSG_STR,strlen(MSG_STR))) < 0) { printf("文件写入失败:%sn",strerror(errno)); goto cleanup; } lseek(fd, 0 ,SEEK_SET); memset(buf, 0 ,sizeof(buf)); if((rv = read(fd,buf,sizeof(buf))) < 0) { printf("读文件失败:%sn",strerror(errno)); goto cleanup; } printf("从文件读出%d数据:%sn",rv,buf); cleanup: close(fd); return 0; }