8.2 低级IO read/write

​ 利用这个系统调用来构造高级一点的函数, 库函数之类的

​ 系统调用的函数原型集中放在一个头文件syscalls.h中( 事实上目前的系统里只有syscall.h,最后也会调用unistd.h)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//将输入复制到输出

//#include "syscalls.h"
#include <stdio.h>
#include <unistd.h>

int main()
{
char buf[BUFSIZ];
int n;

while((n = read(0, buf, BUFSIZ)) > 0 )
write(1, buf,n);
return 0;
}
1
2
3
4
io.c:3:10: fatal error: syscalls.h: No such file or directory
3 | #include "syscalls.h"
| ^~~~~~~~~~~~
compilation terminated.

https://blog.csdn.net/sesiria/article/details/52337114

​ PS: /usr/include/目录下存着c的各种头文件,可以从这里寻找

1
2
# cat /usr/include/syscall.h
#include <sys/syscall.h>

​ 确实是存在这个目录的,但是为什么没找到呢? syscall.h, 没有s

1
2
#include "syscall.h"
#include <stdio.h> //stdio.h里面有BUFSIZ, 或者自己定义一下

​ 实现getchar(无缓冲区)

1
2
3
4
5
6
7
#include "syscalls.h"

int getchar(void)
{
char c;
return (read(0,&c,1) == 1) ? (unsigned char) c: EOF;
}

​ 第二版 一次读入一组字符,但每次只输出一个字符(简单的带缓冲区的版本)

​ 但是这个函数调用完了,缓冲区什么的也都没了吧?? 不是的, static的生命周期,在整个程序的运行期间都存在

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
//#include "syscalls.h"
#include <stdio.h>
#include <unistd.h>

int getchar(void);

int main()
{
char s;
while((s = getchar())!=EOF){
write(1,&s,1);
}
}

int getchar(void)
{
static char buf[BUFSIZ];
static char *bufp = buf;
static int n = 0;

if (n == 0){//缓冲区为空
n = read(0,buf, sizeof buf);
bufp = buf;
}
return (--n>0) ? (unsigned char) *bufp++ : EOF;
}
1
2
3
4
5
6
#include <fcntl.h>

int fd;
int open(char *name,int flags,int perms);

fd = open(name, flags, perms)
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
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdarg.h>
#include <stdlib.h>

#define PERMS 0666

void error(char *,...);

int main(int argc, char *argv[])
{
int f1,f2,n;
char buf[BUFSIZ];

if (argc != 3)
error("Usage: cp from to");
if ((f1 = open(argv[1],O_RDONLY,0)) == -1)
error("cp: can't open %s",argv[1]);
if ((f2 = creat(argv[2],PERMS)) == -1)
error("cp: can't create %s,mode %03o",argv[2],PERMS);
while((n = read(f1,buf,BUFSIZ)) > 0)
if(write(f2,buf,n) !=n)
error("cp: write error on file %s",argv[2]);
return 0;
}


void error(char *fmt,...)
{
va_list args;

va_start(args,fmt);
fprintf(stderr,"error: ");
vfprintf(stderr,fmt,args);
fprintf(stderr,"\n");
va_end(args);
exit(1);
}

8.5 fopen与getc函数的实现

给的iobuf应该是dos的

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
#include <fcntl.h>
#include "syscalls.h"
#define PERMS 0666

FILE *fopen(char *name, char *mode)
{
int fd;
FILE *fp;


if (*mode != 'r' && *mode !='w' && *mode!='a')
return NULL;

for (fp= _iob; fp < _iob + OPEN_MAX; fp++)
if ((fp->flag & (_READ | _WRITE)) == 0)
break;
if (fp > _iob+OPEN_MAX)
return NULL;

if (*mode == 'w')
fd = creat(name,PERMS);
else if (*mode == 'a'){
if ((fd = open(name,O_WRONLY,0)) == -1)
fd = creat(name,PERMS);
lseek(fd,0L,2);
}else
fd = open(name,O_RDONLY,0);

if (fd == -1)
return NULL;

fp->fd = fd;
fd->cnt =0;
fp->base = NULL;
fp->flag = (*mode == 'r') ? _READ : _WRITE;
return fp;
}

int _fillbuf(FILE *fp)
{
int bufsize;

if ((fp->flag&(_READ|_EOF|_ERR)) != _READ)
return EOF:
bufsize = (fp->flag & _UNBUF) ? 1 : BUFSIZE;
if (fp->base == NULL)
if ((fp->base = (char *) malloc(bufsize)) == NULL)
return EOF;
fp->ptr = fp->base;
fp->cnt = read(fp->fd, fp->ptr,bufsize);
if (--fp->cnt < 0){
if (fp->cnt == -1)
fp->flag |= _EOF;
else
fp->flag !=_ERR:
fp->cnt = 0;
return EOF:
}
return (unsigned char) *fp->ptr++;
}

8.6

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
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
#define NAME_MAX 14

typedef struct {
long ino;
char name[NAME_MAX+1];
} Dirent;

typedef struct{
int fd;
Dirent d;
} DIR1;

DIR1 *opendir1(char *dirname);
Dirent *readdir1(DIR1 *dfd);
void closedir1(DIR1 *dfd);



#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
//#include "dirent.h"
#include <stdlib.h>
#include <unistd.h>


void fsize(char *);
//int stat(char *,struct stat * );
void dirwalk(char *, void (*fcn)(char *));

int main(int argc, char **argv)
{
if (argc == 1)
fsize(".");
else
while(--argc > 0)
fsize(*++argv);
return 0;
}




void fsize(char *name)
{
struct stat stbuf;
if (stat(name,&stbuf) == -1)
{
fprintf(stderr,"fsize: can't access %s\n",name);
}
if ((stbuf.st_mode & S_IFMT) == S_IFDIR)
{
printf("is a dir\n");
dirwalk(name, fsize);
}
printf("%8ld %s\n",stbuf.st_size,name);
}

#define MAX_PATH 1024

void dirwalk(char *dir,void (*fcn)(char *))
{
char name[MAX_PATH];
Dirent *dp;
DIR1 *dfd;
if ((dfd = opendir1(dir)) == NULL)
{
fprintf(stderr,"dirwalk: can't open %s\n",dir);
return;
}
printf("here\n");
while ((dp = readdir1(dfd)) != NULL){
printf("dp->name:%s",dp->name);
if (strcmp(dp->name,".") ==0 || strcmp(dp->name,"..")==0)
continue; //跳过自身和父目录
if (strlen(dir)+strlen(dp->name)+2 > sizeof(name))
fprintf(stderr,"dirwalk: name %s/%s too long\n",dir,dp->name);
else{
sprintf(name,"%s/%s",dir,dp->name);
(*fcn)(name);
}
}
closedir1(dfd);
}


DIR1 *opendir1(char *dirname)
{
int fd;

struct stat stbuf;
DIR1 *dp;
if ((fd = open(dirname,0,O_RDONLY,0)) == -1
|| fstat(fd,&stbuf) == -1
|| (dp = (DIR1 *) malloc(sizeof(DIR1))) == NULL)
return NULL;

dp->fd = fd;
return dp;
}

#include <sys/dir.h>

#ifndef DIRSIZ
#define DIRSIZ 14
#endif


struct direct1
{
ino_t d_ino;
char d_name[DIRSIZ];
};

Dirent *readdir1(DIR1 *dp)
{
struct direct1 dirbuf;
static Dirent d;

while(read(dp->fd,(char *)&dirbuf,sizeof(dirbuf)) == sizeof(dirbuf)){
if(dirbuf.d_ino == 0)
continue;
d.ino = dirbuf.d_ino;
strncpy(d.name, dirbuf.d_name,DIRSIZ);
d.name[DIRSIZ] = '\0';
return &d;
}
return NULL;
}

void closedir1(DIR1 *dp)
{
if(dp){
close(dp->fd);
free(dp);
}
}
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
63
64
#define NAME_MAX 14

#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/dir.h>
#define MAX_PATH 1024

void dirwalk(char *dir,void (*fcn)(char *))
{
char name[MAX_PATH];
struct dirent *dp;
DIR *dfd;

if ((dfd = opendir(dir)) == NULL)
{
fprintf(stderr,"dirwalk: can't open %s\n",dir);
return;
}
//printf("here\n");
while ((dp = readdir(dfd)) != NULL){
//printf("dp->name:%s",dp->d_name);
if (strcmp(dp->d_name,".") ==0 || strcmp(dp->d_name,"..")==0)
continue; //跳过自身和父目录
if (strlen(dir)+strlen(dp->d_name)+2 > sizeof(name))
fprintf(stderr,"dirwalk: name %s/%s too long\n",dir,dp->d_name);
else{
sprintf(name,"%s/%s",dir,dp->d_name);
(*fcn)(name);
}
}
closedir(dfd);
}

void fsize(char *name)
{
struct stat stbuf;
if (stat(name,&stbuf) == -1)
{
fprintf(stderr,"fsize: can't access %s\n",name);
}
if ((stbuf.st_mode & S_IFMT) == S_IFDIR)
{
//printf("is a dir\n");
dirwalk(name, fsize);
}
printf("%8ld %s\n",stbuf.st_size,name);
}



int main(int argc, char **argv)
{
if (argc == 1)
fsize(".");
else
while(--argc > 0)
fsize(*++argv);
return 0;
}

​ 其实那些opendir函数等都不需要自己实现(不过从练习的角度讲,可以自己实现) , 自己实现的话,记得要根据操作系统的情况, 把对应的结构体都修改了,否则就会出问题

https://github.com/Heatwave/The-C-Programming-Language-2nd-Edition/tree/master/chapter-8-the-unix-system-interface

https://www.learntosolveit.com/cprogramming/chapter8/ex_8.5_fsize

niubi: https://stackoverflow.com/questions/7381000/what-is-wrong-with-this-example-from-kr

8.7 存储分配程序实例

​ 内存抽象成块的组织形式,以链表的形式进行组织,每个块都含有一个长度、指向下一个块的指针以及一个指向自身数据存储位置的指针.

1
2
3
4
5
6
7
8
9
10
11
typedef long Align; //按照long类型的边界对齐

union header{ //内存块的头部信息
struct {
union header *ptr; // 空闲块链表中的下一块
unsigned size; // 当前块的大小
} s;
Align x; //强制块的对齐 (效果就是8字节对齐,计算机中对齐通常取决于包含的最大基本数据类型的大小
};

typedef union header Header;

malloc时, ,找到的块太大的话,把尾部返回给用户,这样,初始块的头部只需要修改size就可以了

Snipaste_2023-11-12_11-59-06

free时,寻找的首先是这样的情况, bp > p && bp < p->s.ptr

9f55d85647f9035a945ab734b4d376be

如果不符合,看是否符合这种情况,即被释放的块在链表的开头或者末尾

image-20231112121942990

最终代码

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
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
#include <stddef.h>
#include <string.h>

typedef long Align; //按照long类型的边界对齐

union header{ //内存块的头部信息
struct {
union header *ptr; // 空闲块链表中的下一块
unsigned size; // 当前块的大小
} s;
Align x; //强制块的对齐 (效果就是8字节对齐,计算机中对齐通常取决于包含的最大基本数据类型的大小
};

typedef union header Header;



static Header base; //从空链表开始?
static Header *freep = NULL; //空闲链表的初始指针

void *malloc(unsigned nbytes)
{
Header *p,*prevp;
Header *morecore(unsigned);
unsigned nunits;

nunits = (nbytes+sizeof(Header)-1)/sizeof(Header)+1;// +1是那个Align x?
if ((prevp=freep) == NULL){ //没有空闲链表
base.s.ptr = freep = prevp = &base;
base.s.size= 0;
}
for(p = prevp->s.ptr; ;prevp=p,p = p->s.ptr){
if(p->s.size > nunits){//足够大
if (p->s.size == nunits) //正好
prevp->s.ptr = p->s.ptr; //从链表中卸下p
else{ //大了,分配剩下的
p->s.size = p->s.size - nunits; //剩下的大小
p += p->s.size; //p移动到了分配的那里
p ->s.size = nunits;//p的大小改成了分配的大小,(那之前的呢?)
// 逻辑是这样,找到的块太大的话,把尾部返回给用户,
//这样,初始块的头部只需要修改size就可以了
}
freep = prevp;
return (void *)(p+1);//返回数据部分的指针
}
if (p == freep) //闭环的空闲链表
if ((p = morecore(nunits)) == NULL)
return NULL; //没有剩余的存储空间了
}
}


void free(void *ap)
{
Header *bp, *p;

bp = (Header *)ap - 1; //指向块头
for (p = freep; !(bp > p && bp < p->s.ptr); p = p->s.ptr)
if (p >= p->s.ptr && (bp > p || bp < p->s.ptr))
break; //被释放的块在链表的开头或者末尾

if( bp + bp->s.size == p->s.ptr){ //与前面相邻块合并
bp->s.size += p->s.ptr->s.size;
bp->s.ptr = p->s.ptr->s.ptr;
} else
bp->s.ptr = p->s.ptr; //串进链表里

if(p + p->s.size == bp){ //与后面的堆块合并
p->s.size += bp->s.size;
p->s.ptr = bp->s.ptr; //这一句是不是没必要呢?? 有必要的,如果上一次和前面的合并了
//这里也可能出现再次合并, 这里需要修改指针的
} else
p->s.ptr = bp;
freep = p;
}


#define NALLOC 1024

Header *morecore(unsigned nu)
{
char *cp, *sbrk(int);
Header *up;

if (nu < NALLOC) nu = NALLOC;

cp = sbrk(nu * sizeof(Header));
if (cp == (char *) -1)
return NULL;
up = (Header *) cp;
up->s.size = nu;
free((void * )(up+1));
return freep;
}


void main()
{
char *str1;
char *str2;
str1 = malloc(0x20);
strcpy(str1, "hello");
str2 = malloc(0x30);
strcpy(str2, "tang");
}