文件操作系统调用

写时拷贝

fork()之后,父子进程共享父进程的数据空间,并且内核将这些空间设置为只读的,当父子进程任意一个进程尝试修改数据时,会将修改数据所在的页拷贝一个副本,父子进程对于这个页上的数据就是各自都有一份。

文件

文件描述符:进程所打开的文件–>fork()方法调用之前,父进程打开的文件。

Linux上如何使用代码打开一个文件

C语言– 库函数:fopen() fread() fwrite() fclose() fseek()

man 1 command 查看命令帮助手册

man 2 查看系统调用手册

man 3 库函数 查看库函数的使用手册

1
2
3
4
5
FILE *fopen(const char *pathname, const char *mode);
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
size_t fwrite(const void *ptr, size_t size, size_t nmemb,FILE *stream);
int fclose(FILE *stream);
int fseek(FILE *stream, long offset, int whence);

系统调用:open read write close lseek

1
2
3
4
5
6
int open(const char *pathname, int flags, mode_t mode);
//flags:操作方式 O_RDONLY O_WRONLY O_RDWR O_APPEND(追加) O_TRUNC(清空),使用按位|的形式,设置多种操作方式
ssize_t read(int fd, void *buf, size_t count);
ssize_t write(int fd, const void *buf, size_t count);
int close(int fd);
off_t lseek(int fd, off_t offset, int whence);

编程实现将用户在界面输入的数据存储在a.txt中,再将a.txt中的所有内容整体显示到终端上

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<unistd.h>
#include<fcntl.h>
#include<string.h>
int main()
{
int fd=open("./a.txt",O_RDWR|O_CREAT,0664);
assert(fd!=-1);
while(1)
{
printf("input:");
char buff[128]={0};
fgets(buff,127,stdin); //stdin标准输入,会把最后的回车符也放到buff
if(strncmp(buff,"end",3)==0)
{
break;
}
int n=write(fd,buff,strlen(buff));
if(n<=0)
{
perror("write error:");
exit(0);
}
}
printf("************a.txt************\n");
lseek(fd,0,SEEK_SET);
while(1)
{
char buff[128]={0};
int n=read(fd,buff,127);
if(n==0)//读取结束
{
printf("END\n");
break;
}
else if(n<0)
{
perror("read error:");
exit(0);
}
else
{
printf("%s",buff);
}
}
close(fd);
}

使用fork()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<unistd.h>
#include<fcntl.h>
#include<string.h>
#include<errno.h>
int main()
{
int fd=open("./a.txt",O_RDONLY);
assert(-1!=fd);

pid_t pid=fork();
assert(pid!=-1);

if(pid==0)
{
char c=0;
while(1)
{
int n=read(fd,&c,1);
if(n<=0)
{
printf("child over\n");
break;
}
printf("child:%c\n",c);
sleep(1);
}
}
else
{
char c=0;
while(1)
{
int n=read(fd,&c,1);
if(n<=0)
{
printf("father over\n");
break;
}
printf("father:%c\n",c);
sleep(1);
}
}
exit(0);
}

我自己的虚拟机配的是一核的,如果是2核以上有可能父子进程会同时读出一个字母,也就是并行。

系统调用和库函数的区别

系统调用的实现是在内核中,属于内核空间,库函数的实现是在函数库中,属于用户空间。

利用文件操作的系统调用实现普通函数的cp命令

1
cp 源文件的路径+文件名 目的路径
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<assert.h>
#include<fcntl.h>
#include<sys/stat.h>
/*
argv[0]="./mycp"
argv[1]="./main.c"
argv[2]="..."
*/
int main(int argc,char *argv[])
{
if(argc<3)
{
printf("mycp error,parameter is not enough");
exit(0);
}
int fr=open(argv[1],O_RDONLY);
if(fr==-1)
{
perror("open error");
exit(0);
}
struct stat st;
int n=stat(argv[2],&st);
char path[128]={0};
strcpy(path,argv[2]);
if(n!=-1&&S_ISDIR(st.st_mode))
{
char *p=argv[1]+strlen(argv[1]);
while(p!=argv[1] && *p!='/')
{
p--;
}
strcat(path,"/");
strcat(path,p);
}
int fw=open(path,O_WRONLY|O_CREAT|O_TRUNC,0664);
if(fw==-1)
{
perror("open2 error");
exit(0);
}
while(1)
{
char buff[128]={0};
int num=read(fr,buff,127);
if(num<=0)
{
break;
}
int res=write(fw,buff,num);
if(res<=0)
{
break;
}
}
close(fr);
close(fw);
}