一.系统调用的定义:
操作系统提供给用户的一组“特殊”接口(API);
用户通过这些接口(API)使用内核停供的服务;(用户不能越过接口使用内核服务)
流程:
系统命令 ——>用户编程api——>系统调用内核
注意:linux 将程序空间分为内核空间和用户空间,这样可以更好的保护内核空间。
所以用户一般不允许访问内核空间数据,也不能使用内核函数;只能在用户空间操作用户数据,调用用户函数;
也就是说用户空间有:(系统命令和其他应用程序)在上层,通过用户编程接口api访问底层。
内核空间在底层靠系统调用接受命令;
linux文件:普通文件,目录文件,链接文件(快捷方式),设备文件;
linux对目录或者对设备操作都是对文件操作
二。文件描述符
当打开一个存在的文件,或者创建一个文件的时候,内核会给进程一个描述符。读和写文件都需要文件描述符;
文件描述符一定是正数。
注意一个进程打开都会启动3个文件:标准输入,标准输出,标准出错处理
| 文件描述符 | 宏 |
标准输入 | 0 | STDIN_FILENO |
标准输出 | 1 | STDOUT_FILENO |
标准出错 | 2 | STDERR_FILENO |
man命令:2系统调用 2库函数
三。creat函数:通过man 2creat
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int open(const char *pathname, int flags);// pathname文件名
int open(const char *pathname, int flags, mode_t mode);
int creat(const char *pathname, mode_t mode);
找到flags相关的文件,发现这是告诉以什么方式打开文件
The parameter flags must include one of the following access modes: O_RDONLY, O_WRONLY, or O_RDWR. These request
opening the file read-only, write-only, or read/write, respectively.
其中:O_RDONLY 只读打开 O_WRONLY 只写打开 O_RDWR 读、写打开,为文件打开方式
The following symbolic constants are provided for mode:
S_IRWXU
00700 user (file owner) has read, write and execute permission //读 写 执行 许可
S_IRUSR
00400 user has read permission
S_IWUSR
00200 user has write permission
S_IXUSR
00100 user has execute permission
S_IRWXG
00070 group has read, write and execute permission
S_IRGRP
00040 group has read permission
以上为创建文件的权限
三。返回值:RETURN VALUE
open() and creat() return the new file descriptor, or -1 if an error occurred (in which case, errno is set appro-
priately).
可以知道open和creat返回一个文件描述符,如果出错返回-1
示例程序:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <errno.h>
int main()
{
int ret;
ret = creat("hello.c", S_IRWXU | S_IRWXG | S_IWGRP);
if (-1 == ret) //返回值判断
{
perror("creat");//
exit(1);
}
return 0;
}
四。perror函数
#include <stdio.h>
void perror(const char *s);//将上一个函数的的错误原因文字描述的字符串打印到平台上
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是以什么方式打开,mode是文件具有什么权限
示例程序:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
int main()
{
int fd, ret; //file description 文件描述符
char buf[64] = "helloworldhelloworld";
#if 0
fd = open("hello.c", O_RDWR); //以读写方式打开文件hello.c
if (-1 == fd)
{
perror("open");//打印出open的错误
exit(1);
}
close(fd);//关闭文件流
#endif
fd = open("hello.txt", O_RDWR | O_CREAT, S_IRWXU); //创建文件xx.c,并且读写方式打开拥有可读可写可执行权限
if (-1 == fd)
{
perror("open");
exit(1);
}
ret = write(fd, buf, strlen(buf)); //buf写入到hello.txt,strlen(len)写入的长度
if (-1 == ret)
{
perror("write");
exit(1);
}
//ret = lseek(fd, -20, SEEK_END); //相对某个位置移动某个长度
ret = lseek(fd, 0, SEEK_SET); //相对某个位置移动某个长度
if (-1 == ret)
{
perror("lseek");
exit(1);
}
memset(buf, 0, sizeof(buf));
ret = read(fd, buf, sizeof(buf));
if (ret == -1)
{
perror("read");
exit(1);
}
printf("read from txt : %s\n", buf);
close(fd);
return 0;
}
五。write函数
函数原型 :
#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);
write(文件描述符,写的内容,内容的大小);
函数含义:是buf所指的内容写到fd所指的文件中,每次写count的字节数。,文件读写位置也会改变。
返回值:为实际的写的字节数。
六。lseek函数:
int lseek(int fd, offset_t offset, int whence);
文件读写指针相对于whence移动“ offset ”个字节
返回文件指针相对于头的位置。
whence可使用下述值:
SEEK_SET:相对文件开头
SEEK_CUR:相对文件读写指针的当前位置
SEEK_END:相对文件末尾
offest 可以取负值表示向前移动。
七。read函数:
定义函数:ssize_t read(int fd, void * buf, size_t count);//读取的内容放入到缓冲区buf中,并读取count的字节数。如果count为0,read会返回0.
使用的头文件:#include <unistd.h>
返回值:错误返回-1 ,成功返回实际读取的字节数,如果读取位置已经在文件内容末尾则返回0.。
所以在使用read时候要先用lseek函数 , 将文件指针(fd)移动到文件开头。
lseek(fd, 0, SEEK_SET);
八。memset函数
头文件:#include <string.h>
函数原型: void *memset(void *s, int c, size_t n);
void*s可以是指针或者数组,int c是赋给void*s的值 ,size_tn是置位的长度。
一般用于给一个结构体或者数组赋值。当给int c为0或'\0'时。
#include <stdio.h>
#include<string.h>
struct student
{
char name[20];
int age;
int sex;
};
int main()
{
struct student s1={"hh",4,5};
struct student s2={"re",6,7};
printf("%s ,%d ,%d\n",s1.name,s1.age,s1.sex);
printf("%s,%d,%d\n",s2.name,s2.age,s2.sex);
memset(&s1,0,sizeof(struct student));
memset(&s2,0,sizeof(struct student));
printf("%s ,%d ,%d\n",s1.name,s1.age,s1.sex);
printf("%s,%d,%d\n",s2.name,s2.age,s2.sex);
return 0;
}
九:一些程序示例:
1.拷贝文件内容
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
int main(int argc, char *argv[])//argc 是传递参数的个数,argv是指针数组指向每个传递的参数。
{
int fd_from, fd_to, ret;
char buf[32] = {0};
if (argc != 3) //入参判断
{
printf("Input Error!\n");
exit(1);
}
fd_from = open(argv[1], O_RDONLY); //只读方式打开第一个文件,需要注意的是./文件名或者./a.out也算一个参数放在argv[0]里面
if (-1 == fd_from)
{
perror("open1");
exit(1);
}
fd_to = open(argv[2], O_WRONLY | O_CREAT | O_EXCL, S_IRWXU); //创建第二个文件,只写方式打开
if (-1 == fd_to)
{
perror("open2");
exit(1);
}
while ((ret = read(fd_from, buf, sizeof(buf) - 1)) > 0)//从fd_from读读sizeof(buf)-1个字节到缓冲区buf中,并返回读到的字节数
{
ret = write(fd_to, buf, ret);//调用buf缓冲区的内容写ret个字节到fd_to所指向的文件中
if (-1 == ret)
{
perror("write");
exit(1);
}
memset(buf, 0, sizeof(buf));
}
close(fd_from);//
close(fd_to);//关闭文件描述符
return 0;
}
2.保存结构体
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
struct Student
{
char name[20];
int age;
char sex;
};
int main()
{
struct Student s1 = {"aaaa", 20, 'm'};
struct Student s2 = {"bbbb", 30, 'f'};//对应结构体成员类型赋值,字符串加双引号
int fd = open("Student.txt", O_WRONLY | O_CREAT | O_EXCL, 0666);//0666对应可读可写可执行 O_EXCL文件已经存在会报错
if (-1 == fd)
{
perror("open");
exit(1);
}
int ret = write(fd, (void *)&s1, sizeof(s1));
if (-1 == ret)
{
perror("write");
exit(1);
}
ret = write(fd, (void *)&s2, sizeof(s1));//将s1 和s2的内容写到fd所指向文件去
if (-1 == ret)
{
perror("write");
exit(1);
}
struct Student s;
fd = open("Student.txt", O_RDONLY);
if (-1 == fd)
{
perror("open");
exit(1);
}
while (1)
{
ret = read(fd, (void *)&s, sizeof(s));//读取fd所指向的student.txt中的内容将放入s所对应的结构体中。读取结构体大小的字节
if (-1 == ret)
{
perror("read");
exit(1);
}
if (ret == 0)
{
break;
}
printf("%s %d %c\n", s.name, s.age, s.sex);
memset(&s, 0, sizeof(s));//清空结构体内容
}
close(fd);
return 0;
}
十:fread与read的区别---open和fopen的区别--fread函数和fwrite函数的区别
带f的是标准C库文件,后者是Linux系统下的库文件。
也可以fread是由read函数实现的,目的是减少read的调用。
十一:FILE类型的文件
FILE *fopen(const char *filename, const char *mode)
filename:打开的文件名(包含路径,缺省为当前路径) mode: 打开模式
mode打开模式有:
r, rb : 只读方式打开,文件必须已存在
w,wb : 只写方式打开,如果文件不存在则创建,
如果文 件已存在清空重写
a, ab: 只能在文件末尾追加数据,如果文件不存在则创建
r+,rb+,r+b: 读写方式打开,文件必须已存在
w+,w+b,wb+: 读写方式打开,如果文件不存在则 创建,如果文件已存在清空重写
a+,a+b,ab+: 读和追加方式打开,如果文件不存在则创建
十二。库函数的关闭
头文件:#include<stdio.h>
函数原型:int fclose(FILE * stream);
程序中写fclose(fd)就行
十三。文件长度用c库函数描述
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
FILE *fp;//创建一个文件流
if (argc != 2)
{
printf("Input Error!\n");//如果输入不是./文件名 测试文件名报错
exit(1);
}
fp = fopen(argv[1], "r");//只读方式打开文件必须存在
if (NULL == fp)
{
perror("fopen");
exit(1);
}
fseek(fp, 0, SEEK_END); //文件指针移动到文件末尾
int length = ftell(fp);//ftell是文件指针相对当前文件开头的距离,返回值就是文件的长度
printf("length is %d\n", length);
fclose(fp);
return 0;
}
十二:fread函数
size_t fread ( void *buffer, size_t size, size_t count, FILE *stream) ;
buffer
将读取的内容放入指针buffer所指向的内存中
size
每次读取的字节数
count
读取的次数
stream
文件流或者读取的文件
返回值为读取的次数
十三:fwrite函数
函数原型:size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream)
ptr -- 将元素写入ptr所指向的内存空间
size -- 每个元素的大小
nmemb -- 写入的元素的个数
stream -- 文件指针对应的输出流,要写入的文件。
返回值为元素的个数
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
FILE *fp;
int ret;
char buf[32] = {0};
fp = fopen("hello.c", "a+"); //读和追加的方式打开hello.c,不存在则创建
if (NULL == fp)
{
perror("fopen");
exit(1);
}
/*ret = fseek(fp, 0, SEEK_SET); //文件指针移动到文件开头
if (-1 == ret)
{
perror("fseek");
exit(1);
}*/
ret = fread(buf, 1, sizeof(buf), fp);//从fp读取sizeof(buf)个元素,每次写入1字节的大小的元素到buf中
if (-1 == ret)
{
perror("fread");
exit(1);
}
printf("read from txt : %s\n", buf);//输出缓冲区的值
memset(buf, 0, sizeof(buf));//清空缓冲区的值
scanf("%s", buf);//输入字符到buf
ret = fwrite(buf, 1, strlen(buf), fp);//将buf中的内容以一个单元1字节,写入strlen(buf)个元素到fp中
if (ret < 0)
{
perror("fwrite");
exit(1);
}
fclose(fp);
return 0;
}