0%

如何进入MySQL控制台

1
win + R -> 输入cmd -> 输入 -uroot -p -> 输入密码

输入密码即可

create

建数据库:create database dbname;

例如:create database student;

建表: create table tablename(列定义|表约束)

1
2
3
4
5
6
7
CREATE TABLE  StudentInfo
(
Sno char(9) PRIMARY KEY,
Sname char(20) UNIQUE,
Sage SAMLLINT,
Sdept char(20)
);

ALTER TABLE

1
2
alter table table_name
[修改事项 [,修改事项]...]

常用的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
ADD [COLUMN] 列名 数据类型 [列约束]
[FIRST | AFTER col_name]
| ADD {INDEX|KEY} [索引名] [类型] (列1,...)
| ADD [CONSTRAINT [约束名]] 主码约束
| ADD [CONSTRAINT [约束名]] UNIQUE约束
| ADD [CONSTRAINT [约束名]] 外码约束
| ADD [CONSTRAINT [约束名]] CHECK约束
| DROP {CHECK|CONSTRAINT} 约束名
| ALTER [COLUMN] 列名 {SET DEFAULT {常量 | (表达式)} | DROP DEFAULT}
| CHANGE [COLUMN] 列名 新列名 数据类型 [列约束]
[FIRST | AFTER col_name]
| DROP [COLUMN] 列名
| DROP {INDEX|KEY} 索引名
| DROP PRIMARY KEY
| DROP FOREIGN KEY fk_symbol
| MODIFY [COLUMN] 列名 数据类型 [列约束]
[FIRST | AFTER col_name]
| RENAME COLUMN 列名 TO 新列名
| RENAME {INDEX|KEY} 索引名 TO 新索引名
| RENAME [TO|AS] 新表名

总结:

  • ADD用来添加列和约束 (主码、外码、CHECK、UNIQUE)
  • DROP用来删除列、约束
  • MODIFY用来修改列的定义
  • RENAME用来修改列、索引、和表的名称
  • CHANGE用来修改列的名称、还可以修改列的定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
server {
listen 8080;

location /foo {
echo "foo = [$foo]";
}

location /bar {
set $foo 32;
echo "foo = [$foo]";
}
}
$ curl 'http://localhost:8080/foo'
foo = []

$ curl 'http://localhost:8080/bar'
foo = [32]

$ curl 'http://localhost:8080/foo'
foo = []

Nginx 变量一旦创建,其变量名的可见范围就是整个 Nginx 配置,甚至可以跨越不同虚拟主机的 server 配置块,但是赋值操作在bar中实现,foo中的foo为空

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
server {
listen 80;
server_name www.example.com;

location /api/ {
proxy_pass http://backend_api;
}

location /static/ {
root /var/www/html;
}

location /admin {
return 403;
}
}
# server的作用是匹配域名和端口号,location的作用是匹配路径
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
map $args $foo {
default 0;
debug 1;
}
# map为映射,args为自变量,foo为因变量,第一列为args的值,第二列卫foo对应的值,default类似于switch case中的default,当其他条件都不满足时,foo为0
# 注意: map不能在server中,与server是并列关系
server {
listen 8080;

location /test {
set $orig_foo $foo;
set $args debug;

echo "original foo: $orig_foo";
echo "foo: $foo";
}
}
# $ curl 'http://localhost:8080/test'
# original foo: 0
# foo: 0
# 原因:$foo 变量在第一次读取时,根据映射规则计算出的值被缓存住了
1
2
3
4
5
6
7
8
9
# nginx有11个阶段 postread -> server_rewrite -> find_config -> rewrite -> post_rewrite -> preaccess -> access -> postaccess -> precontent -> content -> log
location /test {
set $a 32;
echo $a;
set $a 50;
echo $a;
}
# 输出结果为 50 50
# set输入rewrite;echo属于content,所有set都在echo之前执行

1
2
3
4
5
#include <stdio.h>
int main() {
printf("hello world\n");
return 0;
}

预处理阶段:预处理器根据#开头的命令,读取头文件,并插入到程序中,得到.i文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 1 "hello.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 31 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 32 "<command-line>" 2
# 1 "hello.c"






include <stdio.h>

int main()
{
printf("hello world\n");
return 0;

}

编译阶段:编译器将hello.i翻译成文本文件hello.s

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
	.arch armv8-a
.file "hello.c"
.text
.section .rodata
.align 3
.LC0:
.string "hello world"
.text
.align 2
.global main
.type main, %function
main:
.LFB0:
.cfi_startproc
stp x29, x30, [sp, -16]!
.cfi_def_cfa_offset 16
.cfi_offset 29, -16
.cfi_offset 30, -8
mov x29, sp
adrp x0, .LC0
add x0, x0, :lo12:.LC0
bl puts
mov w0, 0
ldp x29, x30, [sp], 16
.cfi_restore 30
.cfi_restore 29
.cfi_def_cfa_offset 0
ret
.cfi_endproc
.LFE0:
.size main, .-main
.ident "GCC: (Ubuntu 9.4.0-1ubuntu1~20.04.2) 9.4.0"
.section .note.GNU-stack,"",@progbits

汇编阶段:汇编器将hello.s翻译成机器语言的指令,将结果保存在.o文件中

链接阶段:hello.c文件中调用了printf()函数,printf函数存在于printf.o的程序中,链接器负责处理合并,得到可执行文件

Lab1

1 Sleep

首先判断sleep是否有参数,将argv[1]转换为int类型,使用系统调用sleep实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
1 #include "kernel/types.h"
2 #include "kernel/stat.h"
3 #include "user/user.h"
4
5 int main(int argc, char* argv[]) {
6 if (argc != 2) {
7 fprintf(2, "usage: sleep 10");
8 exit(1);
9 }
10 int time = atoi(argv[1]);
11 sleep(time);
12
13 exit(0);
14
15 }

2 pingpong

建立两个管道,fd1, fd2,通过readwrite实现父进程和子进程之间的通信

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
 1 #include "kernel/types.h"
2 #include "kernel/stat.h"
3 #include "user/user.h"
4
5 int main(int argc, char* argv[]) {
6 int fd1[2];
7 int fd2[2];
8 int ret1 = pipe(fd1);
9 int ret2 = pipe(fd2);
10 char buffer[] = {'a'};
11 if (ret1 == -1) {
12 fprintf(2, "pipe error");
13 exit(1);
14 }
15 if (ret2 == -1) {
16 fprintf(2, "pipe error");
17 exit(1);
18 }
19 int pid = fork();
20 if (pid == -1) {
21 fprintf(2, "fork error");
22 exit(1);
23 } else if (pid == 0) {
24 //child
25 read(fd1[0], buffer, 1);
26 printf("%d: received ping\n", getpid);
27 write(fd2[1], buffer, 1);
28 } else {
29 //parent
30 write(fd1[1], buffer, 1);
31 read(fd2[0], buffer, 1);
32 printf("%d: received pong\n", getpid);
33
34 }
35 exit(0);
36 }

3 primes

在main函数中的父进程中输入2~35,在main函数的子进程中递归调用filter函数

在filter函数中,首先读的一定是质数,直接输出,通过这个prime判断其他数是否读入到下一个进程中

每个父进程都要写wait,否则会超时

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
 1 #include "kernel/types.h"
2 #include "kernel/stat.h"
3 #include "user/user.h"
4
5 #define INT_LENGTH 4
6
7 void filter(int fd[2]) {
8 int prime;
9 read(fd[0], &prime, INT_LENGTH);
10 printf("prime %d\n", prime);
11 int num;
12 int flag;
13 flag = read(fd[0], &num, INT_LENGTH);
14 if (flag == 0) {
15 exit(0);
16 }
17 int new_fd[2];
18 int ret = pipe(new_fd);
19 if (ret == -1) {
20 fprintf(2, "pipe error");
21 exit(1);
22 }
23
24 int pid = fork();
25 if (pid == -1) {
26 fprintf(2, "fork error");
27 } else if (pid == 0) {
28 close(new_fd[1]);
29 filter(new_fd);
30 } else {
31 close(new_fd[0]);
32 if (num % prime != 0) {
33 write(new_fd[1], &num, INT_LENGTH);
34 }
35 while (read(fd[0], &num, INT_LENGTH) > 0) {
36 if (num % prime != 0) {
37 write(new_fd[1], &num, INT_LENGTH);
38 }
39 }
40 close(new_fd[1]);
41 close(fd[0]);
42 }
43 wait(0);
44
45
46 }
47
48 int main(int argc, char* argv[]) {
49 int fd[2];
50 int ret = pipe(fd);
51 if (ret == -1) {
52 fprintf(2, "pipe error");
53 }
54 int pid = fork();
55 if (pid == -1) {
56 fprintf(2, "fork error");
57 } else if (pid == 0) {
58 close(fd[1]);
59 filter(fd);
60 } else {
61 close(fd[0]);
62 for (int i = 2; i <= 35; ++i) {
63 write(fd[1], &i, INT_LENGTH);
64 }
65 close(fd[1]);
66 wait(0);
67 }
68 exit(0);
69 }

4 find

find用法:find current_path target

fmtname作用:根据当前路径返回最后一个/后的内容

通过st.type判断是文件还是目录

文件,与target进行比对,相同输出当前路径

目录,递归使用find,构造路径

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
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
#include "kernel/fs.h"

#define MAX_PATH 512

void find(char *curr_path, char *target);

char* fmtname(char *path)
{
static char buf[DIRSIZ+1];
char *p;

// Find first character after last slash.
for(p=path+strlen(path); p >= path && *p != '/'; p--)
;
p++;

// Return blank-padded name.
if(strlen(p) >= DIRSIZ)
return p;
memmove(buf, p, strlen(p));
buf[strlen(p)] = 0;
return buf;
}

void find(char *curr_path, char *target) {
char buf[MAX_PATH], *p;
int fd;
struct dirent de;
struct stat st;

if ((fd = open(curr_path, 0)) < 0) {
fprintf(2, "find: cannot open %s\n", curr_path);
return;
}

if (fstat(fd, &st) < 0) {
fprintf(2, "find: cannot stat %s\n", curr_path);
close(fd);
return;
}

switch (st.type) {
case T_FILE: {
// Check if the current file matches the target filename
char *f_name = fmtname(curr_path);
if (strcmp(f_name, target) == 0)
printf("%s\n", curr_path);
close(fd);
break;
}

case T_DIR:
// Traverse each entry in the directory
memset(buf, 0, sizeof(buf));
int curr_path_len = strlen(curr_path);
memcpy(buf, curr_path, curr_path_len);
buf[curr_path_len] = '/';
p = buf + curr_path_len + 1;
while (read(fd, &de, sizeof(de)) == sizeof(de)) {
if (de.inum == 0 || strcmp(de.name, ".") == 0 ||
strcmp(de.name, "..") == 0)
continue;
memmove(p, de.name, DIRSIZ);
p[DIRSIZ] = 0;
find(buf, target); // Recurse into subdirectories
}
close(fd);
break;
}
}

int main(int argc, char *argv[]) {
if (argc != 3) {
fprintf(2, "usage: find [directory] [target filename]\n");
exit(1);
}
find(argv[1], argv[2]);
exit(0);
}

5 xargs

xargs的用法:echo a | xargs echo b,输入 b a,将前一个命令的输出作为后一个命令的参数

注意:下面代码中argv[0]是xargs

使用fork和exec使得新的进程得到执行

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 "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
#include "kernel/param.h"

void xargs(char* argv[], char* args[]) {
int fd[2];
int ret = pipe(fd);
if (ret == -1) {
fprintf(2, "pipe error");
exit(1);
}

int pid = fork();
if(pid == -1) {
fprintf(2, "fork error");
} else if (pid == 0) {
//child
close(fd[0]);
close(fd[1]);
exec(argv[1], args);
exit(1);
} else {
wait(0);
}
}

int main(int argc, char* argv[]) {
if (argc < 3) {
fprintf(2, "usage error");
}

char buf[512];
char* args[MAXARG];
args[0] = argv[1];
// printf("argv[0]:%s\n", argv[0]);

while (1) {
gets(buf, 512);
if (buf[0] == '\0') {
break;
}
// printf("buf:%s\n", buf);

buf[strlen(buf) - 1] = '\0';

for (int i = 2; i < argc; i++) {
args[i - 1] = argv[i];
}

args[argc - 1] = buf;
args[argc] = 0;
/*
for (int i = 0; i < argc; i++) {
printf("args:%s\n", args[i]);
}
*/

xargs(argv, args);
}
exit(0);
}

exit

执行exit系统调用后,进程会从运行状态转为僵尸状态(zombie),操作系统会销毁当前进程,释放资源

exit是c标准库的函数,_exit是系统调用,直接终止进程,不进行清理工作

僵尸进程: 进程已经终止,但是父进程还未回收其状态信息

exec

将当前进程的地址空间替换为新的程序,但保留当前进程的pid

fork配合使用,可以在子进程中运行一个新的程序

fork

fork的作用是在当前进程下,创建一个新的进程,通常把这两个进程称为父进程和子进程,子进程的内存是复制父进程的,它们有独立的内存空间,fork在父进程中返回的值为子进程的pid,在子进程中返回的值为0,这就是为什么pid == 0可以判断它是子进程

1
2
3
4
5
6
7
int pid;
pid = fork();
if (pid == 0) {
std::cout << "this is child process" << std::endl;
} else {
std::cout << "this is parent process" << std::endl;
}

通过fork实现父子进程之间的读和写

read(fd[0], buf, sizeof(buf));

write(fd[1], buf, sizeof(buf));

注意:默认情况下读用0,写用1,管道是单方向的,要实现两端的读和写需要两个管道,buf是指向缓冲区的指针

wait

用于让父进程等待子进程结束的系统调用

xargs

xargs的作用是读取标准输入,将其作为后面命令的参数

1
echo hello |xargs echo bye

上述命令的输出为bye hello

概述

host

1
与网络相连的计算机常成为主机,也叫端系统

互联网的组成

1
1、边缘部分:由所有连接在互联网上的主机组成,这部分是用户直接使用,用来进行通信(传送数据、音ping)

报错信息

kex_exchange_identification: read: Connection reset by peer Connection reset by ::1 port 3316

使用vscode链接docker后在终端输入以下命令

1
sudo service ssh restart

wsl中出现AddressSanitizer:DEADLYSIGNAL

1
echo 0 | sudo tee /proc/sys/kernel/randomize_va_space

第一章

计算机的五大基本部件

1
2
3
4
5
6
7
8
9
输入设备:将编好的程序和原始数据送到计算机中,使它们转换成计算机内部能识别和接受的信息方式
输出设备:将计算机 的处理结果以数字、字符、图形、图像、声音等形式送出计算机
存储器:存放程序和数据的部件,是一个记忆装置,是计算机能够实现“存储 程序控制”的基础
常见的三级存储系统:高速缓冲存储器、主存储器、辅助存储器
主存储器可由CPU直接访问,存取速度快,容量小
辅助存储器设置在主机外部,存储容量大,价格低,存取速度慢
高速缓冲存储器位于主存和CPU之间,存取速度比主存快,容量更小
运算器:对信息机型处理和运算的部件。又称算术逻辑运算部件(ALU)
控制器:按照预先确定的操作步骤,控制整个计算机的各部件有条不紊的自动工作