C语言和Linux基础技术要点:
linux  下文件路径: /home/anolgame/0712/day09/文件.mp3
windows 下文件路径:D:\0725基础班\01-C基础第01天(linux操作系统介绍和命令使用)\01笔记\文件

文件种类: nihao.doc nihao.ppt nihao.mp3 nihao.txt nihao.avi 基于windows

路径+文件名 就可以打开文件

绝对路径:(linux)绝对路径是从[/]开始的[home][主文件][目录]
(windows)绝对路径是从[盘符][文件夹][文件]

相对路径:相对路径是从当前文件夹(目录)开始的 [文件夹][文件]

rwx rwx r-x
r代表 read 读权限
w代表 write 写权限
x代表 execute 执行权限
-代表没有该权限

d rwx rwx r-x  2 anolgame anolgame 4096  7月 13 16:57 day01/
第一组 是用户(所有者)
第二组 是用户组
第三组 是其他用户

更改用户权限

【文件分类】d 目录文件 - 普通文件 c/b设备文件 s管道文件 l连接文件

命令行的帮助和提示

【命令】 --help
man 【命令】

智能补全,在【命令】 之后补全参数使用 如果没有重复 按tab 补全  如果有重复 需要按两下tab键  提示查看所需参数

ls【查看文件】
-a查看所有文件包括隐藏文件
-l类表形式显示所有文件,不包含隐藏文件
-lh显示大小(1024K = 1M 1024M 1=G)
-all 以列表形式显示所有文件 包含隐藏文件 快捷方式为【ll】

通配符 | 可以组合使用
【*】代表匹配任意、一个或多个字符 0-256
【ls】【*】查看所有目录 包括目录下面的文件
【?】代表匹配任意一个字符
【[123][1-3][a-c]】代表匹配中括号范围内任意一个字符
【\】转义字符,用来获取特殊字符原本的意思

【>】输出重定向是将一个命令的输出结果放在一个文件中,如果该文件存在,内容覆盖
【>>】附加重定向是将一个命令的结果添加在一个文件末尾,如果该文件不存在,则会创建这个文件

【more】分页显示内容
【|】将一个命令的输出结果当做另外一个命令的输入条件

【clear】清屏  快捷键为:ctrl+l

【.】代表当前目录
【..】代表上级目录
【cd】切换工作目录空间 【cd ..】回到上层目录【cd】回到根目录 【cd -】回到上一次的工作目录
cd命令切换目录的时候可以使用通配符  如果有通配符匹配多个文件 默认进入顺序为为1 比较靠前的目录
cd切换工作目录是可以使用绝对路径也可以使用相对路径

【mkdir】新建目录 -p建立多层目录结构

切换超级管理员(root):
如果没有超级管理员需要设置 sudo passwd 输入当前用户密码  输入超级管理员密码  确认密码  exit退出管理员用户
【su】【su root】切换到超级管理员用户

【ln 源文件  链接文件】  硬链接 两个文件大小相同,即使删除源文件 硬链接一样可以使用
【ln -s 源文件 链接文件】 软链接   生成的链接文件比较小 如果源文件被删除,链接文件失效

ln -s /home/anolgame/0725/day01/a.txt /home/anolgame/0725/a.txt.ln
如果源文件和链接文件在不同目录下面需要使用绝对路径

【cat 文件】在终端上查看文件 ^

【cp 源文件 目标文件】文件拷贝时可以写绝对路径  也可以写相对路径
rwx rwx rwx
r     w     x
0     0     0    0
0     0     1    1
0     1     0    2
0     1     1    3
1     0     0    4
1     0     1    5
1     1     0    6
1     1     1    7

777 = rwxrwxrwx
765 = rwxrw-r-x
234 = -w--wxr--
775 = rwxrwxr-x

归档:【tar cvf 归档文件名.tar 文件1 文件2 】生成文件为:归档文件名.tar

压缩:【gzip 归档文件名.tar】生成文件为:归档文件名.tar.gz

解压缩:【gzip -d 归档文件名.tar.gz】生成文件为:归档文件名.tar

解压归档文件:【tar xvf 归档文件名.tar】生成文件为:文件1 文件2

一步压缩:
【tar zcvf 归档文件名.tar.gz 文件1 文件2】 生成文件为:归档文件名.tar.gz
一步解压缩:
【tar zxvf 归档文件名.tar.gz】生成文件为:文件1 文件2

【tar zxvf 归档文件名.tar.gz -C 路径+目录】-C将文件解压到指定的文件夹

bzip压缩:
【tar jcvf 归档文件名.tar.gz 文件1 文件2】生成文件为:归档文件名.tar.gz2
bzip解压缩:
【tar jxvf 归档文件名.tar.gz】生成文件为:文件1 文件2

zip压缩:
【zip 压缩名 *.*】 生成文件为:压缩名.zip *.*对当前整个文件夹进行压缩
unzip解压缩
【unzip -d 路径名+目录 压缩名.zip】

修改用户组:
用户名:anolgame  用户组:anolgame  切换用户组:aaa
【usermod -g 用户组 用户名】  在切换用户组是需要在root里面操作

在root用户里面可以修改任意一个普通用户的密码

- rw- rw- r--  1 anolgame anolgame   302  7月 28 10:39 a.txt  a = ugo
-文件类型
【chmod ugo+rwx 文件名】为所有用户添加所有权限

rwx rwx rwx
r     w     x      二进制
0     0     0    0
0     0     1    1
0     1     0    2
0     1     1    3
1     0     0    4
1     0     1    5
1     1     0    6
1     1     1    7

7 7 7 = rwxrwxrwx
7 6 5 = rwxrw-r-x
2 3 4 = -w--wxr--
7 7 5 = rwxrwxr-x

下面两个操作需要在root里面完成
修改文件所有者:
【chown 用户名 文件名】
修改文件所属组:
【chgrp 用户组 文件名】

ps查看及进程PID 【kill -9 PID】 -9强制终止进程 PID  进程编号

将一个程序放入后台执行:ctrl+z &
获取当前后台运行的程序:jobs
将后台程序取回到前台 fg + 编号(jobs 查看的编号)
IP地址  ip v 4
0.0.0.0(网络保留保留)

255.255.255.255(子网掩码网段)

192.168.1.1(局域网网段)
192.168.

ip v 6 (向下兼容)
0.0.0.0.0.0
255.255.255.255.255.255  最大值256^6 - 差值

打开VI编辑器:
vim打开  打开之后写入文件需要保存一个文件名 :w 文件名
vi 文件名 直接进入vi 编辑器  如果文件名存在,就进入已经存在的文件内 如果该文件不存在 则新建一个文件

vi里面有两种模式:命令行模式  编辑器模式  可以通过ESC键进行切换
【a】在光标右侧进行插入
【i】在光标当前位置插入
【o】在光标下一行开启新的一行
【O】在光标上一行开启新的一行
【I】在光标所在位置的行首插入
【A】在光标所在位置的行尾插入
退出:
【Shift + ZZ】【:wq】【:x】退出
【:q】文件没有改变,可以退出
【:q!】退出不保存
删除:
【[n]x】删除n个字符,从光标所在位置算起
【[n]X】删除n个字符,从光标所在位置之前算起
【D】删除光标到行位的内容,从光标所在位置算起
【[n]dd】删除从光标行算起的n行内容(剪切)
【[n]yy】复制从光标行算起的n行内容(复制)
【p】粘贴
【dG】删除光标所在的行到文件结尾
【J】合并两行内容 将光标所在下一行移动到当前行行尾,加空格用来区分
【.】执行上一次命令
【u】撤销
定位:
【Ctrl+f】下一页
【Ctrl+b】上一页
【gg】定位行首
【G】【:$】定位在文件末尾行首
【[m]gg || [m]G】定位在m行
查找:
【/查找内容】 查找 【n】查找下一个【N】查找上一个
【?】查找上一次查找内容 {两个n|N顺序颠倒}
【^内容】查找文件行首  内容
【内容$】查找文件行尾  内容
【.】查找文件任意一个字符
替换:
【r】替换光标所在位置字符
【:r 文件名】将一个文件插入在光标下一行
【:g/内容/s//替换内容/g】全文本替换
【:起始行,结束行s/内容/替换内容/g】指定行替换

修改程序 直接执行 不需要加入./
1、cd 回到主目录
2、vi .profile 中  {redhat .bash_profile}
3、显示行号(:set nu)
4、定位在文件末尾(G)
5、按i进行插入(注意大小写 空格 英文字符)
ulimit -c unlimited
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:.
export PATH=$PATH:.
6、退出保存文件(shift+zz)
7、切换到超级用户中(root)重启虚拟机(init 6)

只针对你的当前用户

ip 192.168.23.115 user anolgame password  空格(“ ”)

登陆远程linux:
1、打开xshell,选择文件-》新建 打开对话框
2、输入用户名和主机号(linux ip地址)
3、登陆远程ip地址 输入用户名和密码 登陆成功
4、打开远程文件传输对话框
5、上传文件 lcd(在windows里面打开对话框,选择路径)lcd d:\code
6、选择远程服务器路径(cd ../../)
6、上传文件 put 文件名.扩展名
7、下载文件 get 文件名.扩展名

布置远程服务器:
上传文件http.tar.gz
解压缩改文件【tar zxvf http.tar.gz】
cd http 切换到root 用户
编译make
启动服务器 ./myhttp start
停止服务器 ./myhttp stop

printf("hello world\n");
int a = 10;

预编译 编译 链接库

高级语言 ->汇编语言 ->机器语言

#include <stdio.h>  //导入头文件 (/usr/include 下面)stdio.h std c标准库 io输入输出库   标准输入输出库 该库中包含了printf()
#define MAX 100
int main(void)//返回值类型 int(整型)  main(c语言程序的主入口  一个c语言程序有且只有一个main()函数 函数参数类型(void  空类型)如果不写默认类型也是空类型 )
{//函数体 代码体  程序体  (大括号要一一对应)
printf("MAX = %d\n",MAX);//% 占位符 d有符号的整型类型
printf("hello world\n");//打印 在标准输出设备上输出一个字符串  双引号引起来的成为字符串常量 \n 换行  \转义字符
return 0;//如果return 在主函数中出现 代表程序结束 return 0
}
~
gcc -o a a.c
通过编译器gcc 将a.c c语言文件编译成可执行程序a

程序编译过程:

1、预处理:去掉程序中所有注释  头文件展开<stdio.h>  常量的值直接写在程序代码中 (c语言代码)这个过程不会讲语法错误检测出来
#defien MAX 100 //定义了一个常量 常量的值为100
【gcc -E a.c -o a.i】
2、编译:检查语法错误,如果没有错误,将c程序语言转化成汇编语言
【gcc -S a.i -o a.s】
3、汇编:将汇编语言转化成二进制文件
【gcc -c a.s -o a.o】
4、链接:链接系统的库文件
【gcc a.o -o a】
ldd 可执行程序  用来查看程序链接的库文件 在linux下面所有库文件都是 静态代码库文件(.so)
在windows下通过工具depends.exe查看文件添加的库 .dll动态链接库

如果在一个程序中执行system("ls")就相当于在终端上执行了ls

整行复制: 光标定位在本行 ctrl+c ctrl+v
整行剪切: 光标定位在本行ctrl+x(删除整行)
调整代码顺序:alt+↑↓
智能提示:ctrl+j
智能补全:alt+→
全屏:alt+shift+enter

float a = 3.14f;  double b = 3.14f;
在计算机操作中 乘号用 【*】  除号用【/】
a = a * a;  //将a乘以a的结果赋值给a

在变量基础上加上const 进行修饰  那么这个就是一个常量 常量的值在程序运行中不能发生改变(不安全)

定义常量另外一种方式是:【# defien 常量名称 值】 根据值的不同 常量修饰类型也不同
int abc;  1
int _abc; 1
int 2abc; 0
int a*bc; 0
int a_b_c;1
int a-b-c;0
int _a_1; 1
int ABC;

变量可以通过变量名和内存地址找到这个变量  常量也同样   const int a =10;

1B(Byte) = 8b(bit)
1k = 1024B
1M = 1024K
1G = 1024M
1T = 1024G

%d 有符号的十进制数
%o 有符号的八进制数
%x|%X有符号的十六进制数 输出的10-15(a-f)字符大小写
char 在内存的大小
char = 1B = 8bit  1111 1111
a = ‘a’;ascii 97
原码: 0110 0001
反码: 0110 0001
补码: 0110 0001 //转化为十六进制 61

int a = 10
int = 4B = 32bit 1111 1111 1111 1111 1111 1111 1111 1111
10
原码:0000 0000 0000 0000 0000 0000 0000 1010  //十六进制 a 在内存中 0a 00 00 00 根据大小端对齐有关
反码:0000 0000 0000 0000 0000 0000 0000 1010
补码:0000 0000 0000 0000 0000 0000 0000 1010
-10
原码:1000 0000 0000 0000 0000 0000 0000 1010//负数的原码最高位是符号位1 其余位数的值
反码:1111 1111 1111 1111 1111 1111 1111 0101//符号位不变其余位取反
补码:1111 1111 1111 1111 1111 1111 1111 0110//反码+1

-97
原码:1110 0001
反码:1001 1110
补码:1001 1111

-0 = -128
char a -128  127

-97
补码:1001 1111
反码:1001 1110
原码:1110 0001
char:1111 1111
0111 1111 =127
1111 1111 = -127 255
1000 0000 = -0在内存中存的值是-128 = -0 char类型取值范围【-128 127】【-2^7 2^7-1】
0000 0000 = 0

-0
原码:1000 0000
反码:1111 1111
补码:1 0000 0000 溢出 1000 0000  1111 1111 1000 0000
-128:
源码:1000 0000
反码:1111 1111
补码:1000 0000

char类型取值范围【-128 127】【-2^7 2^7-1】
unsigned char 类型取值范围是【0-255】【0 - 2^8-1】
unsigned int 类型取值范围是【0 - 2^32-1】【0-4294967295】
unsigend double 8B =64bit

整型(int)长度:4B
字符型(char)长度:1B
短整型(short)长度:2B
长整型(long)长度:4B(根据操作系统不同 long类型长度也会改变 8B)
单精度浮点型(float)长度:4B
双精度浮点型(double)长度:8B
长长整型(long long)长度:8B

char < short <int = float = long<= double = long long

sizeof(变量名|数据类型)求出数据类型在内存空间占有的大小 B
返回值类型是size_t

int a =10000;
char b;
b = a;

char a  = 127;
int b;
b = a;

hgjJhGHJjhJgGHjgJHGgGHg【A-Z】【65-90】+32 -32
hgjjhgh....

%10.5f    12.56

char a = 'a';字符
char a[] = "a";字符串 'a''\0'

int 10 / 3 = 3
10%3 =1   15%5=0   20%6=2
a++; a += 1; a = a + 1(先进行表达式运算  然后a+1)
++a;(先a+1 在进行表达式运算)
a--;a -= 1;a = a - 1; (先进行表达式运算  然后a-1)
--a;(先a-1 在进行表达式运算)

a=20;
a%=3  a = 20%3 = 2
a=2

【!】非运算符 非假为真  非真为假
【&&】与运算符 同真为真 其余为假
【||】或运算符 同假为假 其余为真

float a = 3.14f;
int b = a;
if(条件)  表达式||值 满足条件值为真  不满足条件值为假
{
程序语句
}
int a = 0;
int b = 20;
if(++a && b || 0)
{
printf("成立\n");
}
if(a++ && 0 || !0)//如果条件成立执行 if{}
{
printf("成立\n");
}
else//如果if条件不成立执行下面语句
{
printf("不成立\n");
}
三目运算符:

【(表达式)?条件1(值1):条件2(值2)】
int a = 10;
int b = 20;
c = (a > b) ? a : b;
真    条件1  条件2

switch条件判断语句:
switch(year)//条件判断语句 表达式||值
{
case 1993://条件
printf("您的属相是鸡\n");//程序语句
break;//跳出程序
case 1994:
printf("您的属相是狗\n");
break;
case 1995:
printf("您的属相是猪\n");
break;
default:
printf("对不起 不知到您的属相\n");
break;
}
switch(条件)
{
case 条件1:
程序语句
break;
case 条件2:
程序语句
break
default:
程序语句
break;

}

代码day05/a5.c
请您输入一个年份:
scanf("%d",&year)
if(被4整除但不被100整除,或被400整除。)year%4==0&& year%100 != 0 ||year%400==0
{
闰年
}
else
{
平年
}

代码day05/a6.c
A 100
B 90
C 80
D 70
E 60
F 不及格
switch(score / 10)
{
case 10:
printf("您的考试级别是:A级");
break;
default:
printf("您的考试级别是:A级");
break;
}

while循环语句:
int a = 10;
while(a)//条件
{
printf("%d\n",a);// 10 9 ...1
a--;
}
while(条件)
{
程序语句
}
do while循环语句:
do
{
printf("a = %d\n",a);//无论条件是否成立都会执行一次代码 //10 9...
}while(a--);

猜数字:day05 /a8.c
int a = 78;
int count = 0;
while(count)
{
scanf("%d",&num);
if(a == num)
{
printf("",count);
break;
}
if(a>num)

if(a<num)

count++;
printf("nin")
}
#include <time.h>

time_t timer = time(NULL);
srand(timer);

int a = rand() % 100;

for循环:
int i;
for(i = 0; i < 10; i++)
{
printf("i = %d\n",i);//012..89
}
【gcc -o a9 a9.c】使用的库是c89的库 c89 不支持for(int i=0;i<10;i++)  c99

敲7:
每逢含有数字7、或者7的倍数不打印

嵌套循环:
外层循环循环一次 内层循环循环一周
int i,j;
for(i = 0;i < 10; i++)//0-9
{
for(j = 0;j < 10; j++)//0-9
{
printf("i = %d\tj = %d\n", i, j);
}
}

九九乘法表:
1x1=1
1x2=2 2x2=4
1x3=3 2x3=6 3x3=9

10
*
**        0016.c
***
****
*****


*
***
*****
*******    0017.c
*********

*
***
*****
*******
*********    0018.c
*******
*****
***
*

A
ABC
ABCDE
ABCDEFG
ABCDEFGHI
ABCDEFG         0019.c
ABCDE
ABC
A

A         arrary[0]
ABA        arrary[0][1][0]
ABCBA        arrary[0][1][2][1][0]
ABCDCBA    arrary[0][1][2][3][2][1][0]
ABCDEDCBA    arrary[0][1][2][3][4][3][2][1][0]        0023.c
ABCDCBA    arrary[0][1][2][3][2][1][0]
ABCBA        arrary[0][1][2][1][0]
ABA        arrary[0][1][0]
A        arrary[0]

1)    arrary[0]     A
2)    arrary[0]arrary[1]arrary[0]   ABA
3)    arrary[0]arrary[1]arrary[2]arrary[1]arrary[0]
4)    arrary[0]arrary[1]arrary[2]arrary[3]arrary[2]arrary[1]arrary[0]

6)     arrary[0][1][2][3] [2][1][0]    3
7)    arrary[0][1][2] [1][0]          2
8)     arrary[0][1] [0]        1
9)     arrary[0]
m=i

6 7 8
2 1 0

8-i

*        1*2-1=1    5-1=4    空格个数:abs(5-i)
***        2*2-1=3    5-2=3
*****        3*2-1=5 5-3=2
*******    4*2-1=7    5-4=1
*********    5*2-1=9    5-5=0
*******     9-2 * abs(5-i)//135797531
*****
***
*
示例代码test/test01.c
for(int i=1;i<6;i++)
{
//控制空格合数
for(int j = 5;j>=i;j--)
{
printf(" ");
}
//控制*个数
for(int k=1;k<=i*2-1;k++)
{
printf("*");
}
printf("\n");
}

数组
数组定义:
字母、数字、下划线(开头不能是数组)
数组初始化:
int a[10] = {1,2,3,4,5,6,7,8,9,10};
int a[10] = {1,2,3};
int a[10] = {0};数组所有元素的值全部初始化为0
int a[] = {1,2,3,4,5,6}//数组长度时6个元素
数组使用:
for(int i=0;i<10;i++)
printf("a[%d] = %d",i,a[i]);//a[i]  i代表数组的下标 a[i]代表数组的元素
数组元素大小
数组大小:siezof(a)
数组元素大小:sizeof(a[0])
数组元素个数:sizeof(a)/sizeof(a[0])

数组元素地址:
首地址是a 或者&a[0]如果是int类型  下一个数组元素的首地址是 当前地址+4

12,45,66,3,19,99,31,191,90,100
100               12
45               90
191                   66
数组最大下标:sizeof(a)/sizeof(a[0])-1
0   9
1   8
2   7
3   6
4   5

输入5名学生的三门功课(语文数学外语)成绩:示例代码day06/a9.c
然后根据总成绩进行排序

1,2,3
2,3,4

a的大小:96  2*3*4*4 = 96
a[0]的大小:48 3*4*4 = 48
a[0][0]的大小:16 4*4 = 16
a[0][0][0]的大小:4

块:96/48 sizeof(a)/sizeof(a[0])
行:48/16 sizeof(a[0])/sizeof(a[0][0])
列:16/4 sizeof(a[0][0])/sizeof(a[0][0][0])

fgets(char a[],siezof(a),stdin)  从标准输入设备上获取一串字符串 如果字符串没有到达数组的最大长度会将'\n'作为字符的一部分输出;如果字符串到达数组的最大长度或者超过最

大长度,将末尾的字符用'\0'表示
puts从标准输出设备上输出一个字符串 默认添加'\n'
fputs从标准输出设备上输出一个字符串 没有'\n'遇到'\0'结束
strtok切割字符串
www\0itcast\0cn会改变原来字符串样式,将切割的字符用‘\0’来代替
strcmp  在linux下返回的结果 如果相同是0  如果不相同返回的结果是大于0 或者小于0   在windows下如果相同是0  如果不相同返回的结果是两个字符的差值 hello hallo -4
调用不同的函数实现四则元算 示例代码test/test07.c
在linux下面多个文件进行联合编译 /0725/day07/hanshu/a4.c a5.c a.h

1、内存地址:
每一个内存都分配一个唯一编号 【无符号十六进制整形】 在32位下占 4个字节 在64位下占8个字节

2、指针:
是指向一个变量的地址 int a=10; int *p=&a pirntf("%p",p);

3、指针赋值:
int a=10; int *p=&a pirntf("%p",p);可以通过*p对a进行赋值操作 *p=100;

4、指针大小:
sizeof(int *) 在32位下占 4个字节 在64位下占8个字节

5、野指针 空指针:
int *p=10;(这个指针没有具体 指向一个变量 但是这样不会出错 野指针会在给指针赋值的操作上会出错 )
空指针 int *p =NULL; 空指针在赋值时候也会出错

6、const:
const 修饰 int * 可以修改地址 但是不能修改值   int * const 修饰 p可以修改值 但是不能修改地址
const int * const p = &a;既不能修改地址,又不能修改值

7、指针和数组:
int a[]={1,2,3}; int *p a; *p等同于a[0] p++ *p 等同于 a[1]
p[0]=1;p[1] = 2; i[p];p++ p--
两个指针相减 p++   int n= p-a; 1这个是int类型数组 每个int类型大小占 4个字节 strchr
指针数组:指针数组每一个元素都是一个指针int a=10;int b=20; int *p[] = {&a,&b};
int a[]={1,2,3};int b[]={2,3,4};int *p[]={a,b};

8、二级指针:
int a=10; int *p=&a; int **p = &p 二级指针是指向指针地址的指针;

9、指针和函数:
指针作为函数参数 void demo(char * a,char *b)  通过地址传递 改变实参变量的值
指针作为函数返回值类型 char * demo(char * a, char *b) strstr strcat

10、指针和字符串:
char *p="hello world";printf("%s",p);
在一个字符串中查找另外一个字符串出现的次数 while do while
字符串对堵模型 从两头查找空格 进行测算
字符串逆置 将一个字符串反转输出

11、main函数的形式参数样式  ls -all  ls -l
int main(int argc,char * argv)
{
}

变量作用域:
一个局部变量的作用域是整个{}

全局变量:
全局变量的作用域是整个文件 但是在各个文件中不能出现重名的全局变量 全局变量定义在函数之外

静态变量:
静态变量会在程序启动时进行一次赋值,静态变量 会一直存在内存中 直到程序结束才会被销毁 静态变量的作用域是本文件之内

全局函数:
默认情况在C语言中,所有函数都是全局函数(系统函数、自定义函数) 全局函数的作用域是所有文件 也就是说不能出现相同函数名
这个也就解释了当初未写头文件 函数也可以调用
静态函数:
静态函数是在全局函数上加上static 修饰 作用域范围是本文件中 可以出现重名

内存分区:(内存四区)
代码区:
text 存放的是可执行的二进制文件(函数 指令)

静态区(全局区):
data 数据区:存放的是已经初始化的全局变量 已经初始化的静态变量
bss 未初始化数据区 :存放的是未初始化的全局变量 未初始化的静态变量

栈区:
stack  存放局部变量 函数返回值 和函数形式参数 栈区  在liunx32位下是8M

堆区:
可执行程序 用来开辟的内存空间 存储大型数据 (音乐的音频流 视频视频流 图片 文本文件)

auto int a=10; 栈中
static int a=10; 静态区 数据区(data)
static int a; 静态区 未初始化数据区(bss)

栈区:遵从先进后出 后进先出的原则 一般存放的数据都是比较小的数据 存放大数据可能会出现内存溢出

堆区:程序在执行过程中动态开辟的内存  堆内存空间在内存中一定是连续的内存地址   数组 所有内存地址都是一个连续的一维数组

我们开辟的堆空间 可以存储任何类型的数据 可以把堆空间当做一个数组

申请一个堆空间 a[5][3]  申请五个堆空间 每个是一个一位数组 a[3]

数组里面分别存语数外成绩

调用函数 根据总成绩 从小到大排序

函数 堆空间  循环 排序 赋值  指针 数组
定义指针
int * p[5] = NULL;
申请堆空间
for(int i=0;i<5;i++)
{
p[i] = malloc(sizeof(int ) *3);
}
for(int i=0;i<5;i++)
{
for(int j=0;j<3;j++)
scnaf(。。。。。。。);
}
排序
{
for(int i=0;i<5;i++)
{
for(int j=0;j<5-i;j++)
{
堆空间数据跟换

打印堆空间的值

循环释放堆空间
{
p[0]-p[4]
}

开辟堆内存
输入五名学生三门功课成绩
创建函数 排序
打印学生数据
释放堆空间

结构体:
注意:
变量名可以和结构体类型名重名 不会出错 但是不能和结构体变量名重名 这样会出错

结构体声明:
struct 结构体类型名
{
结构体成员列表
};

struct 结构体类型名 结构体变量名

struct 结构体类型名
{
结构体成员列表
}结构体变量名1,结构体变量名2;

struct   无 【结构体类型名】
{
结构体成员列表
}结构体变量名1,结构体变量名2;

结构体定义:
struct 结构体类型名 结构体变量名  = {结构体成员列表初始化};
struct 结构体类型名 结构体变量名  = {.成员 = 值,.成员 = 值};
struct 结构体类型名 结构体变量名;
变量名.成员 = 值

注意如果是数组类型 不能直接赋值 需要采用其他方式

结构体里面成员大小:
如果有相同类型 需要将相同类型放在一起 如果有不同类型 需要按照大小进行排序

指针和结构体:
结构体指针:
结构体指针指向一个结构体变量的地址
struct person p1 = { "张三",'M',"13888888888",18 };
struct person *p = &p1;
想要找到结构体成员需要通过指针指向一个成员【p->name】

结构体指针和指针的区别
结构体指针是通过指向【p->】找到成员的内存首地址
指针:
p++ p[i] p+i i[p]

结构体指针数组
s[i][0].name
s[i]->name
(*s[i]).name

struct student
{
//学生姓名
char name[50]
//学生成绩地址
int * p;
};
struct student stu[5];
for(int i=0;i<5;i++)
{
scanf("%s",stu[i].name);
stu[i].p = malloc(sizeof(int) * 3);
for(int j=0;j<3;j++)
{e
stu[i].p[j] = rand()%100+1;
}
}
sort(stu);

sort(struct student ** stu)

状态机:

const 修饰的一定是一个常量【不安全】

const 【int】 a; const修饰的是int 定义了一个常量 这个常量的值不能在运算中改变

const 【int *】【p】 const修饰的是int * 能改变地址 不能改变值
【int *】const 【p】 const修饰修饰是p p能改变值 不能改变地址
const【int *】 const 【p】 const修饰int* 也修饰 p 改常量什么也不能改变

const 【struct stu *】 p const修饰的是struct stu *  能改变结构体地址但是不能改变结构体成员的值

【struct stu *】const  【p】const修饰的是p能改变值 不能改变地址
const【struct stu *】 const 【p】

状态机:
enum zhuangtaiji
{
走 跑 跳 攻击 跳舞 死亡
}
enum zhuangtaiji z
switch(z == 走)
{
z= 攻击
}
case 攻击:

文件:

文件分为磁盘文件和设备文件

磁盘文件分为 文本文件和二进制文件

文本文件:记事本.txt c语言源码文件.c java语言源码.java 编码格式是ACSII

二进制文件:图片 可执行程序 电影 音乐 编码格式二进制
操作文件:
打开文件
FILE*
操作文件
写 读
关闭文件
手动写一个cat

int main(int argc ,char * argv[])
{
//判断买了是否正确
打开文件
判断文件是否打开
读取字符 输出在界面中
判断是否到文件结尾
关闭文件
}
想加入到系统命令中
gcc -o mycat a1.c
which cat
将编辑的可执行程序放入 /bin
sudo mv mycat /bin

加密解密:
可以添加密钥(生成一个文件)
通过文件中的随机数 动态的生成加密文件
在通过文件中的随机数进行解密

int main(int argc,char *argv[])
{
if(argc < 2)
p("缺少参数");
return -1;
FILE * fp = fopen("文件名","方式");//argv[1]
if(!fp)
return -2;
while(!feof(fp))//feof = 1
p("%c",fgetc(fp))

fclose(fp)
}
int main(int argc,char *argv[])
{
if(argc < 2)
p("缺少参数");
FILE * fp = fopen("文件名","方式");
if(!fp)
return -2;
char buf[1024];
while(1)
{
//退出
if(!(strncmp(buf,"exit",4)))
break;
fgets(buf,szieof(buf),stdin);
fgets("\n",fp)
}
}
fprintf(fp,"%d",100);
int a;
fscanf(fp,"%d",&a);

0 张三 001
0 李四 003
0 王五 002
1 小淘气  004

//块形式写文件  这种形式是以二进制读写的 通过文本文件看不到具体的值
返回值 = fwrite(块地址,大小,个数,文件)

返回值 = fread(块地址,大小,个数,文件)

注意:大小和个数这两个数据可以颠倒位置 不会影响结果 但是会影响返回

如果是结构体数组 在fwrite中大小 指向的是结构体数组 那么个数就是1 指向的是结构体 那么大小就是数组元素个数

精通  掌握 熟练  熟悉 了解
5    4    3     2   1

int a = (a++,b);

C++一些基本操作:
输出
函数
析构函数
分文件编程

QT界面程序UI搭建

集成开发环境搭建

//设置文件名 设置为全局变量 方便编译操作
QString fileName;
1、打开文件操作
//获取文件名 需要导入头文件#include <QFileDialog>
QString fileName= QFileDialog::getOpenFileName();
//cout <<"file name = "<<fileName;
//更改编码格式 Qt 默认的中文编码格式是utf-8 需要转成GBK  需要头文件 #include <QTextCodec>
QTextCodec * codec = QTextCodec::codecForName("GBK");
char *file = codec->fromUnicode(fileName).data();
//打开文件
FILE * fp = fopen(file,"rb");
if(!fp)
return;
char buf[1024];
QString txt;//设置读取文件类型
while(!feof(fp))
{
fgets(buf,sizeof(buf),fp);
txt += codec->toUnicode(buf);//将读取的文件转化成utf-8
}
//显示在界面中
ui->textEdit->setText(txt);
//cout << txt;
//关闭文件
fclose(fp);

2、保存文件
//需要注意保存文件方式
QString fileName= QFileDialog::getSaveFileName();
QTextCodec * codec = QTextCodec::codecForName("GBK");
char *file = codec->fromUnicode(fileName).data();
//将文本编辑器中的字符 转化为字符串
QString txt = ui->textEdit->toPlainText();
const char * buf = txt.toStdString().data();

FILE * fp = fopen(file,"wb");
if(!fp)
return;
//char buf[1024];
fputs(buf,fp);
fclose(fp);

3、新建文件
ui->textEdit->clear();
fileName.clear();
4、退出程序
exit(1);
5、撤销
ui->textEdit->undo();
6、拷贝
ui->textEdit->copy();
7、粘贴
ui->textEdit->paste();
8、程序编译
/*
int flag = system("gcc -o D:\\Desktop\\a D:\\Desktop\\a.c");
if(flag != 0)
{
system("cmd /k gcc -o D:\\Desktop\\a D:\\Desktop\\a.c");
}
system("cmd /k D:\\Desktop\\a");
*/
//获取文件名
QString dst = fileName;
//去掉文件名中的.c
dst = dst.replace(".c","");
//system里面有中文 需要转码成GBK
QTextCodec * codec = QTextCodec::codecForName("GBK");

QString cmd;
cmd = QString("gcc -o %1 %2").arg(dst).arg(fileName);
char * tmp = codec->fromUnicode(cmd).data();
int flag = system(tmp);

if(flag != 0)
{
cmd = QString("cmd /k gcc -o %1 %2").arg(dst).arg(fileName);
tmp = codec->fromUnicode(cmd).data();
system(tmp);
}
cmd = QString("cmd /k %1").arg(dst);
tmp = codec->fromUnicode(cmd).data();
system(tmp);

2)    C语言提高技术要点:
1. #ifndef CLIENT_SOCKET_H
#define CLIENT_SOCKET_H //防止头文件重复包含
#endif
2. 编写函数,首先对传进来参数进行合法校验。
3. 定义变量、对象都要初始化,防止不必要错误产生
4. *handle解引用,编译器根据指针的地址去找这个地址内存空间。对变量取地址&val 指针级别升级,对变量取*解引用,指针级别降一级
5. 工具->代码片段管理器->vc++ ->添加mycode文件夹,确定退出
6. 数据类型可以告诉编译器分配多少内存,如果你定义了数据类型,编译器不能确定分配多少内存,编译器就会报错
7 void*表示无类型的指针,可以不需要强制转换指向任何类型的数据。
8. sizeof不是函数,是一个操作符
sizeof 获得一个变量或者一个类型分配多大的内存
sizeof得到的结果是一个unsigned int类型,如果用此结果进行计算时候要注意

9. 我们所编写的程序在编译完成之后会分成两个段-代码段和数据段

10 代码分区好处:
1)方便对程序中代码和数据进行分别的管理,比如说代码是只读的,那么我们就将代码放到只读内存区域(代码区)
比如数据是可读可写的,那么我们就放到数据区
2)可以更有效 更合理利用宝贵内存空间
11. 编译器对源代码编译完之后,将我们程序分成两个区,一个数据区 一个代码区,当我们将程序load到内存中的时候,会多出来两个区,多出来的区叫: 堆区和栈区

1. 理解空指针、野指针概念
2. 了解函数调用流程
3. 理解并掌握指针的步长
4. 掌握指针间接赋值三大条件
5. 掌握字符串拷贝函数编写
6. 掌握查找字符串子串出现次数(while模型 do-while模型)

1. 一维数组名/二维数组名的本质的理解
2. 二维数组做参数的有那三种形式
3. 结构体定义和赋值操作
4. 结构体中深拷贝和浅拷贝问题
5. 结构体嵌套二级指针案例的理解

1. 链表的初始化_插入_删除_销毁算法思路
2. 函数指针定义的三种方式
3. 回调函数的理解

1. 常用的预处理指令的理解和使用
2. 动态库和静态库概念的理解
3. 面向接口编程的理解


3)    C++基础技术要点:
1. 单例模式的实现思路
2. this指针的用法,及修饰成员函数
3. 常量对象的理解
4. 友元函数的理解及使用
【C++部分均为代码实现 就不贴源码了】

4)    数据结构
1.    冒泡排序:
for(int i=0;i<n-1;i++)
{
for(int j=0;j<n-1-i;j++)

swap(a[j],a[j+1]);
};
};

2.    选择排序:
冒泡排序是比较一次更换一次顺序,选择排序是比较一轮以后更换一次顺序。效率略高于冒泡排序。
void SelectSort(int* arr, int len)
{
if (NULL == arr)
{
return;
}
if (len == 0)
{
return;
}
/*
从第一个元素开始和每一个元素比较,遇到比第一个元素大的元素,则更新max。然后把max值放在第一位了以后开始下一轮循环
也就是外层循环依是需要排序的数的个数,所以i<len。内层循环:因为每一个被确定了最大的数字都放在首位,如:外层循环结束第
一轮以后,有一个max值在arr[0]的位置是确定的了,所以下一轮只需要从i=1,j=i+1的位置开始。SO~ j的初始化值是j=i+1;
*/
for (int i = 0; i < len-1; i++)
{
int max = i;
for (int j = i + 1; j < len; j++)
{
if (arr[j]>arr[max])
{
max = j;
}
}
if (max != i)
{
int temp = arr[max];
arr[max] = arr[i];
arr[i] = temp;
}
}
}

3.    插入排序:
void InsertSort(int* arr, int len)
{
if (NULL == arr)
{
return;
}
if (len == 0)
{
return;
}
for (int i = 1; i < len; i++)/* 可以把i看做控制无序数列的下标
i从1开始是因为数列被分为了有序和无序两部分。又因为一般数列基本为无序数列。所以把数列
中第一个元素(即i=0时的元素)单独列为有序数列,剩下的元素分为无序数列。插入排序即为
把无序元素插入到有序数列中。所以i=1时的元素为无序数列中的第一个元素。i=0时的元素为
有序数列中的最后一个元素。因此要从i=1时的元素开始插入到i=0时的元素前后构造有序数列。
*/
{
if (arr[i] < arr[i - 1])//如果无序数列中的第一个元素小于有序数列中的最后一个元素
{
int temp = arr[i];//因为后面要移动元素,用temp保存无序数列中的第一个元素,以便后面移动

int j = i - 1;/*可以把j看做控制有序数列的下标 所以有序数列元素的最后一个位置(i)减1就等于无序数列的第一个
元素的位置  即i-1代表的是无序数列中的第一个元素
*/
for (; j >= 0 && temp < arr[j]; j--)/*
条件:无序数列中第一个元素(temp)与有序数列中元素(i-1)倒序(即从后往
前)挨个比较
但是比较范围不能超过下标0,而且要一直满足temp小于有序数列中元素才能
前移。
*/
{
arr[j + 1] = arr[j];/*【j+1】是因为结束比较时,指针总是指向前面一个不满足条件的位置才能退出循环呀。
所以要想把值放在正确的位置,就得+1
举个栗子:{4,2}采用插入排序,4看做有序数列,2看做无序数列。
1)2(i=1)与4(i=0)进行比较,2(i=1)<4(i=0)条件成立。
2)temp=arr[1],用temp存储无序数列中第一个元素2。
3)将4(i=0)向后移动到2(i=1)的位置。【此时指针指向4(i=0),应该放在2(i=1)的位置】
所以需要+1
4)2(temp)继续与有序数列倒数第二个元素(i=-1)进行比较【这个时候指针指向的是i=-1的
位置】时发现下标越界,不满足条件,跳出循环。指针依旧指着i=-1的越界位置。2(temp)
应该存放在i=0的位置上,所以存储2(temp)时依旧要j+1。
*/
}
arr[j + 1] = temp;
}
}
return;
}
4.    希尔排序:
void ShellSort(int* arr, int len)
{
if (NULL == arr)
{
return ;
}
//定义增量【增量相当于要分多少个组】
int increasement = len;

//使用do...while..是因为等于1时还可以计算一次
do
{
//行内经验的关于增量的算法 记住就可以
increasement = increasement / 3 + 1;

//分组的for循环,i是确定有哪些行的
for (int i = 0; i < increasement; i++)
{
/*
如此分组的原因是:i是控制行的变量,而分组后的每一行(即每一组)的首元素下标都是连续且范围在0到increasement-1
而increasement是组数(即行数),所以这样循环。而分组方法就是数学方法了...为啥我也不知道。
*/

//确定列的循环,j是确定有哪些列的
for (int j = i + increasement; j < len; j += increasement)
{
/*
该个循环是用来确定列元素的,即确定每一行都有几个元素的。
j=i+increasement 是每一行的第二个元素
这样就确定了分组以后的数列状态 可以进行插入排序了。
*/

//插入排序开始
if (arr[j] < arr[j - increasement])
{
int temp = arr[j];
int k = j - increasement;

//因为i是每一行的行首元素,所以数组不越界即是k>=i!!!
//注意!!!!!!!!!!!!!!!是 k >= i!!!!是大于等于!!!
for (; k>=i && temp < arr[k]; k-=increasement)
{
arr[k + increasement] = arr[k];
}
arr[k + increasement] = temp;
}

}
}
} while (increasement > 1);
return;
}
5)    STL、QT、MFC
1.    STL:
标准模板库
六大组件
容器、算法、迭代器、仿函数(函数对象)、适配器(配接器)、空间配置器(内存配置器)
引用头文件
vector<int>::iterator it = v.begin() it != v.end () it++
取值 *it
string容器
常用API
at ,[]
利用find substr 获取用户名
vector
构造
赋值
大小
size元素个数
resize重新指定元素个数,超出的会默认填充0,
empty判断是否为空
巧用swap收缩空间
reserve预留空间
插入和删除操作
deque
构造
赋值
大小
双端插入删除
push_back
push_front
pop_back
pop_front
插入 insert
删除 erase

2.    QT:
1、QMainWindow中的各个栏目
2、资源文件加载
3、创建对话框-模态,非模态
4、QMessageBox使用
5、界面布局操作
6、控件操作
7、自定义控件

3.    MFC:
Qt案例复习
底层Windows实现窗口
SDK
API句柄
windows消息机制
创建Windows窗口程序
1、设计
2、注册
3、创建
4、显示更新
5、通过循环取消息
6、处理窗口过程函数
窗口处理
WM_CLOSE
DestroyWindow
WM_Destroy
postQuitMessage(0)
getMessage获取退出进程消息
鼠标左键事件
键盘事件
绘图
创建第一个MFC程序
应用程序类  继承CWinApp
窗口类  继承 CFrameWnd
MFC程序入口initInstance
头文件  afxwin.h
CFrameWnd::create 查API方法
//m_pMainWnd = frame; 保存指向应用程序的主窗口的指针
消息映射机制
.h
DECLARE_MESSAGE_MAP
声明将在一个类中使用消息映射,把消息映射到函数(必须用在类声明中)
BEGIN_MESSAGE_MAP
开始消息映射的定义(必须用在类实现中)
END_MESSAGE_MAP
结束消息映射的定义(必须用在类实现中)
具体的消息 写到分界宏中间
写入函数原型
函数实现
window字符集
TEXT  TCHAR 内部做了自适应字符串
多字节转宽字节  L
wcslen 对宽字节取长度
char * 和 CString 互相转换
CString( char*)
CStringA  tmp
tmp.getBuffer() 转成 char*
利用向导创建MFC
create  WM_Create  OnCreate之间关系
OnPaint 和OnDraw 之间关系
Frame  和 View 之间关系

6)    Linux系统编程
1. shell操作相关的快捷键
1>. tab
2>. 遍历历史记录, 显示历史记录: history
①. ctrl+p
②. ctrl+n
3>. 光标移动
①. ctrl+b
②. ctrl+f
③. ctrl+a
④. ctrl+e
4>. 字符删除
①. ctrl+d
②. ctrl+h
③. ctrl+u
④. ctrl+k
2. linux文件系统目录结构
1>. 根目录如何表示?
2>. 根目录下常用目录
①. /dev
②. /etc
③. /bin
④. /lib
⑤. /mnt
⑥. /media
⑦. /usr
⑧. /home
3. 相对路径和绝对路径  -- /home/itcast/programer/kevin
当前用户所在的目录: /home/itcast, 使用两种方式表示kevin目录
1>. 绝对路径
== 从根据目录开始表示的目录
cd /home/itcast/programer/kevin
2>. 相对路径
== 从当前的目录开始表示
cd programer/kevin
cd ./programer/kevin
当前目录: .
当前的目录的上一级目录: ..
4. 文件和目录相关操作
1>. tree -- 树形式查看目录
命令 -- 相当于软件, 需要额外安装
使用:
tree  -- 列出当前目录下的内容
tree 目录名 -- ...指定目录...
2>. ls --
ls
ls 文件名/目录名
参数:
-a: 显示隐藏文件
linux隐藏文件表示: 名字前加.
-R: 递归显示目录中的内容
-l: 列出文件的详细信息

3>. cd -- 目录切换
如何进入到家目录:
cd /home/itheima
cd ~
cd
在临近的两个目录间切换:
cd -
4>. pwd -- 显示当前工作目录
5>. mkdir -- 创建目录
mkdir 目录名 -- 单个目录
mkdir /dir1/dir2 -p  == 创建多个目录(嵌套)
6>. touch -- 创建或更新文件
touch 文件名
文件不存在 -- 创建文件
文件存在 -- 更新文件的时间(文件的修改日期)
7>. rmdir -- 删除空目录
使用频率不高
8>. rm -- 删除文件或目录
删除之后无法恢复
rm 目录 -r (空和非空)
rm 文件名
参数:
-i: 删除的时候有提示
-f: 强制删除

9>. cp -- 拷贝文件或目录
10>. mv -- 移动或修改文件[目录]名
11>. 查看文件内容(了解知识点)
①. cat -- 查看文件内容比较少的文件
cat 文件名
文件内容输出到终端
②. more -- 可以显示文件的所有内容, 只能向下浏览
more 文件名
回车 -- 向下显示一行
空格 -- 翻页
退出: q
③. less -- 可以上下浏览
less 文件名
回车 -- 向下显示一行 ctrl+n
空格 -- 翻页(向下) -- pagedown
向上滚动一行 -- ctrl+p
向上翻页: pageup
退出: q
④. head
head 文件名
默认显示文件的前10行
指定显示的行数: head -行数 文件名
⑤. tail
head 文件名
默认显示文件的后10行
指定显示的行数: tail -行数 文件名
12>. 软硬链接 -- ln
①. 软链接(符号链接) -- 快捷方式(windows)
ln -s 文件名(要给他创建快捷方式) 创建出来的链接的名字
文件名需要是一个绝对路径
②. 硬链接
ln 文件名(给哪一个文件创建硬链接) 硬链接的名字

5. 用户权限, 用户和用户组
1>. chmod -- 修改文件或目录权限 *****
①. 文字设定法
chmod who [+|-|=] mode 文件名
who: u -- user 文件所有者, g--group, 同组用户, o-other其他人, a --all, 所有
+: 添加权限
-: 移除权限
=: 直接设置, 覆盖原来的权限(可以省略)
mode: 权限
r: 读read
w: 写write
x: 执行
-: 没有权限
题目: rwxrwxrwx -- file, 文件所有者和其他人 减去读写权限
②. 数字设定法
chmod [+|-|=]mode 文件名 √
mode: -- 使用八进制的方式表示权限 0777
r -- 4
w -- 2
x -- 1
- -- 0
---xrwx--x 1 itheima itheima 0 10月 18 15:51 file
将文件所有者和同组用户的权限设置为-wx, 其他人执行权限
chmod 0331 file

2>. chown -- 修改文件所有者或所属组, 需要使用root权限
修改文件所有者: chown 文件所有者 文件名
......所有者和所属组: chown 文件所有者:文件所属组 文件名
3>. chgrp -- 修改文件所属组 -- 需要root权限
修改文件所属组: chgrp 所属组 文件名
6. 文件查找和检索 *****
1>. find -- 根据文件属性查找
1. 根据文件名
find 查找的路径 -name "文件名"
2. 根据文件的大小
find 查找的路径 -size 大小
大于100k: find ~ -szie +100k(k必须小写)
查找一下大于100k小于10M的文件
find ~ -size +100k -size -10M(M必须大写)
3. 根据文件的类型
find 查找的路径 -type
7中文件类型:
普通文件: -(普通文件使用f表示)
目录: d
链接文件:l
套接字:s
管道:p
字符设备:c
块设备:b
2>. grep -- 根据文件内容查找
grep "查找的字符串" 查找的路径 -r(路径中有目录)
总结: find 和 grep 的区别
find: 查找的路径在前边, 查找的内容在后边
grep: 超照的路径在后边, 查找的内容在前边

7. 压缩包管理 *****
linux下常见的压缩格式:
.gz   --  gzip
.bz2  --  bzip2
1>. tar -- 只有打包功能, 并不能压缩
参数:
z -- 使用gzip的方式压缩和解压缩 -- .gz
j -- 使用bzip2的方式......     --  .bz2

c -- 创建压缩文件
x -- 释放压缩文件

v -- 输出压缩和解压缩信息
f -- 指定压缩包的名字

压缩: tar 参数  生成的压缩包的名字  原材料(要压缩的文件)
解压缩: tar 参数 压缩包的名字 -- 解压到当前目录
tar 参数 压缩包的名字 -C 解压目录
2>. rar -- window常用的压缩格式
rar -- 需要安装这个软件才能使用
压缩: rar a(压缩) -r(如果压缩的是目录) 生成的压缩包的名字  原材料
解压缩: rar x 压缩包名 -- 解压到当前目录
rar x 压缩包名 释放的目录
3>. zip -- 7z
压缩: zip -r(压缩的是目录) 生成的压缩包的名字 原材料
解压缩: unzip 压缩包的名字 -- 解压到当前目录
unzip 压缩包的名字 -d 解压目录 -- 解压到指定目录

总结: 规律:
压缩: tar/rar/zip 参数 生成的压缩包的名字 原材料
解压缩: tar/rar/unzip 参数(tar/rar) 压缩包名 [参数(-C(tar)/-d(uzip))  解压缩目录]
8. U盘的挂载和卸载 -- 需要使用管理员权限执行
1>. 挂载 -- mount
2>. 卸载 -- umount
9. 软件的安装和卸载
1>. 在线安装 -- apt-get
2>. 软件包安装(.deb) – dpkg

环境变量:
1>. 用户级别: 用户家目录 ~  .bashrc
2>. 系统级别: /etc/profile

U盘的挂载和卸载 -- 需要使用管理员权限执行
1>. 挂载 -- mount
sudo mount 挂载的设备名 挂载的目录
问题: 如何获取设备名
sudo fdisk -l
挂载的时候指定编码格式:
sudo mount 设备名 挂载目录 -o iocharset=utf8
U盘只能挂载到/mnt目录吗?
可以, 如果目录中有内容, 会临时覆盖, 当卸载之后, 数据就恢复了
挂载到其他目录的时候建议使用空目录
2>. 卸载 -- umount
sudo umount 挂载目录
注意事项: 不能再挂载路径里边卸载
软件的安装和卸载
1>. 在线安装 -- apt-get
1. 安装: sudo apt-get install 软件名(tree/vim)
卸载: sudo apt-get remove  软件名
更新安装列表: sudo apt-get update
删除缓存: /var/cache/apt/archives
sudo apt-get clean
2>. 软件包安装(.deb) -- dpkg
安装: dpkg -i xx.deb
卸载: dpkg -r 软件名

1. vim的三种工作模式
2. vim命令模式下的相关操作
保存退出: ZZ
1>. 光标的移动
上下左右:
光标移动到行首: 0
........尾: $
......文件头部: gg
......文件尾部: G
行跳转: 行号G
2>. 删除命令 -- 删除操作实际上是一个剪切操作
删除字符: 删除光标后边的: x
.....前边:     X
删除一个字: 单词: dw
删除整个单词, 需要将光标房子单词的前边, 否则只能删除一部分
删除光标前的串: d0
......后边的..: d$(D)
删除整行: dd
删除多行: ndd (n==行数)
3>. 撤销和反撤销
撤销: u
反撤销: ctrl+r
4>. 复制和粘贴
粘贴: p -- 粘贴光标所在行的下一行
P -- xxxxxx 上一行
复制: yy
复制多行: nyy (n=行号)
5>. 可视模式
v -- 切换到课时模式
选择: hklj
删除选中内容: d
复制.....:    y
6>. 替换操作
r: 只能替换一个字符, 光标盖住的字符
R: 从光标盖住的字符开始从后替换
8>. 查找命令
/  - 从当前位置向下, n切换
? - xxxx上, n切换

在要查的单词身上按 # , 切换n
9>. 查看man文档
章节号 K(shift+k)
3. 命令模式切换到文本编辑模式
a -- 从光标的后边插入, A 行尾
i -- ....前边插入, I 行首
o -- 在当前行的下边创建新行, O当前行的上边创建新行
s -- 删除一个字符, 光标覆盖的字符, 光标后边的字符, S, 删除行
4. vim末行模式下的相关操作
从命令 --> 末行: 输入一个:
在末行模式下执行完一条命令, 自动回到命令模式
1>. 保存退出
1. 保存: w
2. 退出: q, 退出时做了修改, 没保存, 会给提示
3. 退出不保存: q!
4. 保存退出: wq == x
2>. 替换
替换光标所在行:
:s/tom/mike/g -- g将当前行的所有tom替换为mike
替换一个范围内的行:(10-20行)
:10,20s/tom/mike/g
替换所有行:
:%s/tom/mike/g
3>. 分屏操作
水平分屏: :sp  [文件名]
垂直分屏: :vsp [文件名]

打开的时候文件分屏显示:
水平: vi/vim -on (n代表屏幕的个数) file1 file2
垂直: vi/vim -On (n代表屏幕的个数) file1 file2
在末行模式下执行命令:
切换到末行模式
:!shell命令

5. gcc的工作流程 -- 4步
6. gcc常用参数
sudo apt-get install gcc g++
1>. -v/--version
2>. -I: 编译时候指定头文件
3>. -c: 生成.o文件
4>. -o: 指定生成的文件的名字
5>. -g: gdb调试需要加的参数
6>. -D: 编译时候指定宏
7>. -Wall: 添加警告信息
8>. -On: 优化的参数: n == 0, 1, 2, 3
int a = 10;
int b = a;
int c  = b;
int d = c;
printf("c=%d\n", d);

int d = 10;
printf("c=%d\n", d);

7. 静态库的制作和使用*.lib
命名规则: libxxx.a

库是什么? 源代码 -- 源文件(.c .cpp)
库制作出来了, 如何给用户使用?
1. 制作出来的库
2. 头文件
制作步骤:
1. 生成.o文件
gcc -c *.c
2. 将.o文件打包为.a文件 -- 使用ar工具
ar rcs 库的名字(libCalc.a) *.o
使用静态库:
1. 提供一个测试文件main.c
2. 使用库和头文件+main.c生成可执行程序:
gcc main.c -Iinclude -L./lib -lCalc -o aabb
-L: 指定库的路径
-l: 指定库的名字(掐头去尾 ) libabc.a  ->   abc

8. 动态库的制作和使用
命名规则: libxxx.so
制作步骤:
1. 生成.o文件 -> .c .cpp
gcc -fpic(-fPIC) -c *.c
2. 将.o文件打包为.a文件 -- 使用gcc 需要加参数 -shared
gcc -shared -o libxxx.so *.o
使用库和头文件+main.c生成可执行程序:
gcc main.c -Iinclude -L./lib -lCalc -o aabb
-L: 指定库的路径
-l: 指定库的名字(掐头去尾 ) libabc.so  ->   abc

解决动态库找不到的问题:
1. export LD_LIBRARY_PATH=库的目录:$LD_LIBRARY_PATH -- 临时的设置
2. 永久生效: 把上面的代码写入 ~/.bashrc   /etc/profile
3. 更新 /etc/ld.so.cache文件列表
1. 打开/etc/ld.so.conf -- 在里边写入动态库的绝对路径
2. 执行一个命令: sudo ldconfig -v  -- 更新/etc/ld.so.cache文件列表

1. Makefile
1>. makefile的命名
1. makefile
2. Makefile
2>. makefile中的规则
三部分:
目标(app):依赖(main.c a.c b.c)
命令(gcc main.c a.c b.c -o app)
gcc main.c a.c b.c -o app

执行 make 命令, 就会按照makefile中编写的规则编译整个项目
在makefile文件中可以有一个或多个规则

第一版本:
1 app:main.c sub.c mul.c add.c
2     gcc main.c sub.c mul.c add.c -o app
缺点: 修改一个程序, 重新make其他文件也会被重新编译

改进:
第二个版本:
makefile工作时候的原理:
1. 如果规则中的依赖不存在, 向下查找下边的规则, 使用找到的规则生成不存在的依赖.
2. 通过时间判断是否更新:
第一次: add.c -> add.o
第二次: 修改add.c 如果目标的时间比依赖的时间早, 需要更新

直接make生成的是一个条规则中的目标(终极目标)
生成其他规则中的目标:
make 其他规则中的目标的名字
第二个版本程序:
1 app:main.o sub.o mul.o add.o
2     gcc main.o sub.o mul.o add.o -o app
3
4
5 # 编写规则, 来生成依赖中不存在的文件
6 main.o:main.c
7     gcc -c main.c
8
9 sub.o:sub.c
10     gcc -c sub.c
11
12 mul.o:mul.c
13     gcc -c mul.c
14
15 add.o:add.c
16     gcc -c add.c
17
18 hello:
19     echo "hello makefile"
缺点: 代码冗余

第三版本:
变量: 自动变量: 自定变量只能在规则的命令中使用
$<: 依赖中的第一个
$^: 所有的依赖
$@: 规则中的目标
%.o:%.c
main.o:main.c
gcc -c main.c
gcc -c main.c -o main.o
大写: makefile中自带的变量:
CC == cc == gcc
CPPFLAGS = -I./

代码:
1 # 自定义变量
2 obj = main.o sub.o mul.o add.o
3 app:$(obj)
4     gcc $(obj) -o app
5 # 编写规则, 来生成依赖中不存在的文件
6 # 模式规则
7 %.o:%.c
8     gcc -c $< -o $@
缺点: .o还是需要手动指定, 适应能力不行

版本4:
使用函数: --  在makefile里边, 所有的函数都是有返回值的
1. wildcard -- 搜索指定目录下,指定类型的文件
2. patsubst -- 匹配替换函数
代码:
2 # 使用makefile中的函数
3 src = $(wildcard ./*.c)
4 obj = $(patsubst %.c, %.o, $(src))
5 app:$(obj)
6     gcc $(obj) -o app
7 # 编写规则, 来生成依赖中不存在的文件
8 # 模式规则
9 %.o:%.c
10     gcc -c $< -o $@
缺点: 没有清除项目的功能

版本5:
声明伪目标: 不对文件做是否更新的判断
.PHONY:目标名

3>. makefile中的变量
4>. makefile中的函数
5>. 伪目标的声明
2. gdb调试命令
1>. 启动gdb
gdb 可执行程序的名字
2>. 查看代码
l -- 查看当前文件 -- 默认main函数
2. 查看其它文件: l 文件名:行号, 显示指定行号的上下文
l 函数名 -- 当前文件
l 文件名:函数名 -- 指定文件
3. 查看默认显示的行数: show listsize
4. 设置: set listsize 行数
3>. 断点操作
1. 当前文件设置断点: b(break) 行号
2. 指定文件: b 文件名:行号
b 函数名 -- 当前文件的这个函数位置打断点
b filename:funcName -- 指定文件的指定函数
3. info(i) b(break)
4. 删除断点:
d 删除所有断点
d m-n  删除范围  (m, n断点的编号)
d m n k  删除指定断点
5. 使断点无效: disable(dis) 断点编号
6. 是断点再次生效: enable(ena) 断点编号
7. 设置条件断点: b 行号 if 变量==value
4>. 调试相关命令
1. run(r) -- 执行gdb在断点处停止
2. start  -- ....., 只执行一行就停止了
3. c(continue)继续运行, 停在下一个断点的位置
4. 单步调试:
n(next) -- 往下执行一行代码, 不会进入到函数内部
s(step) -- 往下执行一行代码, 会进入到函数内部
从函数内部出来: finish
5. 打印变量的值:
p 变量名
ptype 变量名 -- 打印变量的类型
自动追踪变量的值:
display 变量名
取消自动追踪:
undisplay 编号
info display -- 获取编号
6. 设置变量的值: set var 变量名=value
7. 退出gdb : quit(q)

5>. 查看变量和类型
6>. 退出gdb
3. 文件IO函数
1>. open/close
1. 创建文件时候, 文件的权限问题:
0002 -> 000010
mode: 777 -> 111111111
&111111101
111111101
775

mode & ~umask
目录权限: 775 -> 目录必须有执行权限  x
普通文件: 664 -> 不给执行权限 775 - 111 = 664

2>. read
3>. write
4>. lseek
4. 阻塞和非阻塞
1>. 对文件读写默认是阻塞还是非阻塞?
2>. 阻塞和非阻塞是文件的属性还是read函数的属性?

1. 是什么?
2. 能干什么?
3. 怎么干?
gcc 参数:
-o, -c, -I, -D, -Wall, -On, -g

静态库: .a    libxx.a
是什么? 二进制文件, 将生成的.o文件做了打包
能干什么? 将.c文件做了加密, 最终打包到可执行程序中
怎么使用: 库+头文件
1. 编写测试程序, 调用库中的函数 -- 看库对应头文件
2. 生成可执行程序: main.c   lib[目录] -- libaaa.a
gcc main.c -I./ -L./lib -laaa -o app
制作库:
1. 生成.o: gcc -c *.c
2. 打包: ar rcs 静态库名 *.o

共享库(动态)
格式: libxxx.so
能干什么? 将.c文件做了加密, 不会打包到可执行程序中,
程序执行时, 会检查这个动态库是否存在, 没有加载
什么时候加载? -- 调用动态库中的函数的时候, 加载到内存
多个程序, 可以共享内存中的同一个动态库
怎么使用: 库+头文件
1. 编写测试程序, 调用库中的函数 -- 看库对应头文件
2. 生成可执行程序: main.c   lib[目录] -- libaaa.so
gcc main.c -I./ -L./lib -laaa -o app
使用的时候, app启动不了? 解决方案:
1. /etc/ld.so.cache文件列表需要更新
1>. 修改 /etc/ld.so.conf  -- 加上动态库的绝对路径
2>. 更新: sudo ldconfig -v
2. 临时设置:
终端: export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:库的路径
3. 永久设置:
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:库的绝对路径, 添加到配置文件
1. 用户级别: ~/.bashrc
2. 系统级别: /etc/profile
生成动态库:
1. 生成.o: gcc -fpic(-fPIC) -c *.c
2. 打包: gcc -shared -o libxxx.so *.o

./a.out aa bb

1. 文件IO函数
1>. open/close
函数原型:
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
参数:
pathname -- 文件名
flags:
必选项: 互斥
O_RDONLY O_WRONLY O_RDWR
可选项:
O_CREAT -- 创建文件, open函数需要指定3个参数
O_EXCL -- 检查文件是否存在, 需要与O_CREAT一起使用
O_APPEND -- 追加文件
O_NONBLOCK -- 设置非阻塞
O_TRUNC -- 文件截断
mode: 八进制整型值, 必须指定O_CREAT属性
2>. read
函数原型:
ssize_t read(int fd, void *buf, size_t count);
返回值:
>0: 读出的字节数
=0: 读文件完毕
-1: 读失败
参数:
fd : 文件描述符, open的返回值
buf: 缓冲区, 存储数据
count: 缓冲区大小
3>. write
函数原型:
ssize_t write(int fd, const void *buf, size_t count);
返回值:
>0: 写入的字节数
=0: 没有数据写入
-1: 写失败
参数:
fd: open的返回值
buf: 往文件中写入的数据
count: 写入的字节数
4>. lseek
函数原型:
off_t lseek(int fd, off_t offset, int whence);
函数参数:
fd: 文件描述符
offset: 偏移量
whence: SEEK_SET SEEK_CUR SEEK_END

函数使用:
1). 获取文件长度
int len = lseek(fd, 0, SEEK_END);
2). 获取文件指针的位置
int pos = lseek(fd, 0, SEEK_CUR);
3). 文件指针移动到文件头
lseek(fd, 0, SEEK_SET);
4). 文件拓展 ****** 文件原来100K, --> 1000k
1. lseek(fd, 1000, SEEK_END);
2. 需要对文件进行一次写操作, 否则不会拓展
write(fd, "1", 1);
2. 阻塞和非阻塞
1>. 对文件读写默认是阻塞还是非阻塞?
阻塞
2>. 阻塞和非阻塞是文件的属性还是read函数的属性?
文件的属性
3. 文件操作相关函数
如果操作失败: -1
成功 >0 ==0
1>. 获取文件属性
int stat(const char *path, struct stat *buf);
int lstat(const char *path, struct stat *buf);
区别:
stat -> 追踪函数
lstat -> 不追踪函数
2>. 测试指定文件是否具有某种属性 -- 当前用户, 使用哪个用户调用这个函数, 这个用户就是当前用户
int access(const char *pathname, int mode);
参数:
pathname: 文件名
mode: 4中权限
R_OK -- 读
W_OK -- 写
X_OK -- 执行
F_OK -- 文件是否存在
返回值:
0 - 有某种权限, 或者文件存在
-1 - 没有, 或文件不存在
3>. 修改文件权限
int chmod(const char *filename, int mode);
参数:
filename: 文件名
mode: 文件权限, 八进制数
./a.out filename mod
4>. 修改文件所有者和所属组
int chown(const char *path, uid_t owner, gid_t group);
函数参数:
path -- 文件路径
owner -- 整形值, 用户ID /etc/passwd
group -- ....., 组ID        /etc/group
5>. 修改文件大小
int truncate(const char *path, off_t length);
参数:
path -- 文件名
length -- 文件的最终大小
1. 比原来小, 删掉后边的部分
2. 比原来大, 向后拓展

6>. 创建一个硬链接 ln  文件名  硬件名
int link(const char *oldpath, const char *newpath);
7>. 创建一个软连接
int symlink(const char *oldpath, const char *newpath);
8>. 读软连接对应的文件名,不是读内容(该函数只能读软链接文件)
ssize_t readlink(const char *path, char *buf, size_t bufsize);
函数参数:
path: 文件名
buf: 读软链接中存储的路径, 放到buf中
bufsize: buf的容量
9>. 删除一个文件
int unlink(const char *pathname);
特点: 当文件处于打开状态, 对文件进行unlink, 文件不会被马上删除, 当fd被关闭的时候,文件被删除
10>. 文件重命名
int rename(const char *oldpath, const char *newpath);
4. 目录操作相关函数
1>. 修改当前进程(应用程序)的路径 cd
int chdir(const char *path);
参数: 切换的路径
2>. 获取当前进程的工作目录 pwd
char *getcwd(char *buf, size_t size);
返回值:
成功: 当前的工作目录
失败: NULL
参数:
buf: 缓冲区, 存储当前的工作目录
size: 缓冲区大小
3>. 创建目录 mkdir
int mkdir(const char *pathname, mode_t mode);
参数:
pathname: 创建的目录名
mode: 目录权限, 八进制的数, 实际权限: mode & ~umask
4>. 删除一个空目录
int rmdir(const char *pathname);
参数: 空目录的名字
5>. 打开一个空目录
DIR *opendir(const char *name);
参数: 目录名
返回值: 指向目录的指针
6>. 读目录
struct dirent *readdir(DIR *dirp);
参数: opendir的返回值
返回值:
7>. 关闭目录
int closedir(DIR *dirp);
5. 复制文件描述符
1>. dup
2>. dup2
6. 改变已经打开的文件的属性: fcntl
1>. 复制一个已有的文件描述符
int fd1 = fcntl(fd, F_DUPFD, 0);    // 返回值为新的文件描述符
2>. 获取/设置文件状态标志
1. 获取文件状态标识
int flag = fcntl(fd, F_GETFL, 0);
2. 设置文件状态标识
flag |= O_APPEND;
fcntl(fd, F_SETFL, flag);
可以更改的几个标识: O_APPEND、O_NONBLOCK (常用)

1. makefile
使用: make -- 生成的第一条规则中的目标
一个规则:
目标:依赖
命令
两个函数: - 所有函数都有返回值
1>. src = $(wildcard /home/itcast/ *.c)
2>. obj = $(patsubst %.c, %.o, $(src))
三个自动变量: -- 只能在命令中使用
$<: 依赖中的第一个
$^: 所有依赖
$@: 目标
target = app
$(target):main.c add.c
gcc $^ -o $@
2. gdb
1>. 是什么: 代码调试工具
2>. 能干什么? 调试代码, -g
3>. 怎么用: 熟练掌握命令
0. 启动gdb: gdb 程序名
1. 查看代码: l 行号
2. 设置断点: b  行号
条件断点: b 行号 if 变量==值
3. 查看断点信息: i(info) b
4. 删除断点: del/d/delete 断点的编号   (单个[n]  多个[m n l]  范围[m-n])
4>. 让gdb跑起来:
1. run(r) -- 停在断点的位置
2. start -- 只执行一行
3. c(continue) - 继续跑, 在下一个断点位置停止
5>. 代码调试:
1. n(next) -- 单步调试, 不会进入函数内部
2. s(step) -- ........, 会进入函数内部
从函数体内部出来: finish
3. p(print) 变量名 - 打印变量的值
4. ptype 变量名  -- 打印变量的类型
5. display 变量名 - 追踪打印变量的值
取消追踪 -- undisplay 编号
获取编号: info display
6. 设置变量的值: set var 变量名=指定的值
7. unitl - 跳出循环
6>. 退出gdb: q(quit)

1. 复制文件描述符 -- unistd.h
1>. dup
返回值: 新的文件描述符, 空闲的最小的文件描述符
参数:   要被复制的文件描述符
2>. dup2(old, new)
参数:
old: 要被复制的fd
new: 新的文件文件, old复制到new中
1. new 被占用: 先关闭原来的文件, 然后在指向old
2. 没被占用: 直接指向old
2. 改变已经打开的文件的属性: fcntl -- unistd.h  and fcntl.h
1>. 复制一个已有的文件描述符
int fd1 = fcntl(fd, F_DUPFD, 0);    // 返回值为新的文件描述符
参数: fd: 要复制的文件描述符
第二个参数: F_DUPFD, 复制文件描述符
第三个参数: 0
返回值: 新的文件描述符
2>. 获取/设置文件状态标志 flags
1. 获取文件状态标识
int flag = fcntl(fd, F_GETFL, 0);
2. 设置文件状态标识
flag |= O_APPEND;
fcntl(fd, F_SETFL, flag);
可以更改的几个标识: O_APPEND、O_NONBLOCK (常用)
3. 进程的五种状态(了解内容)
4. 进程控制
1>. fork
2>. getpid/getppid
5. 进程相关的命令
1>. ps
1> ps aux
2> ps ajx
2>. kill -- 发送一个信号给指定的进程
杀死进程: kill -9(SIGKILL) 进程ID
查看信号: kill -l
6. 进程共享
1>. 初始状态: 父子进程0-3G的地址空间, pcb(出去pid), 都是一样的
2>. 运行起来之后:
思考: 进程间的数据传递可以通过使用全局变量实现吗?
不可以, 父子进程共享机制: 读共享, 写复制
7. 多进程gdb调试
需要在fork之前去设置
1. 追踪父进程(默认): set follow-fork-mode parent
2. 追踪子进程: set follow-fork-mode child
8. exec函数族
核心思想: 换核不换壳 -- 暗度陈仓
让子进程执行与父进程完全不一样的操作
返回值:
执行成功: 不返回
....失败: -1
1>. 执行指定目录下的程序
int execl(const char *path, const char *arg, ...);
int execv(const char *path, char *const argv[]);
参数:
path -- 要运行的程序的绝对路径
execl -- arg, arg   "arg1", "arg2", NULL
execv -- char* arg[] = {"arg1", "arg2", NULL};

2>. 执行PATH环境变量能够搜索到的程序
int execlp(const char *file, const char *arg, ...);
int execvp(const char *file, char *const argv[]);
参数:
file -- 文件名
后边的参数: 文件运行需要的参数
3>. 执行指定路径, 指定环境变量下的程序
int execle(const char *path, const char *arg, ..., char *const envp[]);
int execve(const char *path, char *const argv[], char *const envp[]);

9. 回收子进程
1>. 孤儿进程
父亲先死, 儿子还活着.
儿子成为孤儿, 进孤儿院. -- 进程孤儿院 init (内核为用户创建的第一个进程)
init进程负责回收孤儿的pcb
2>. 僵尸进程
子进程先死, 父亲还活着.
但是父亲没有给儿子收尸(pcb), 儿子进程叫僵尸进程.

kill -9 僵尸进PID   ---  干不掉
鞭尸
3>. 进程回收
1). wait函数
1. 阻塞等待子进程退出
2. 回收子进程资源
3. 查看子进程退出状态
返回值:
1. >0: 子进程ID
2. ==-1: 没有子进程
2). waitpid函数

1. 回收子进程
1>. wait函数
1. 阻塞等待子进程退出
2. 回收子进程资源
3. 查看子进程退出状态
返回值:
1. >0: 子进程ID
2. ==-1: 没有子进程
函数原型: int wait(int *status)
1. 正常退出
w if exited(status) > 0
得到退出的值: w exit stauts(status)
2. 通过信号退出
w if signaled(status)
得到通过哪一个信号退出: w term sig(status)
2>. waitpid函数 -- wait的升级版
函数原型: pid_t waitpid(pid_t pid, int *status, int options);
1. 阻塞或非阻塞等待子进程退出
options == 0: 阻塞
options = w no hang 非阻塞
2. 回收子进程资源
可以回收指定子进程资源: pid
pid == 0: 代表回收当前进程组的子进程
pid == -1: 回收所有的子进程
pid < -pid:
pid > 0: 子进程的pid
返回值:
>0: 回收的子进程pid
==0: 子进程正在运行 == 设置了wnohang
--1: 没有子进程

2. pipe -- 无名(匿名)管道
特点:
1. 内核缓存区, 伪文件
2. 环形队列
3. 分为两端, 一端读, 一端写
4. 进程结束, 缓冲区被回收
5. 只适用有血缘关系的进程间通信
6. 保证数据的单向流动
适用场景:
1>. 父子进程间通信
ps aux | grep "bash"
2>. 兄弟进程间通信
子1: ps aux
子2: grep "bash"
父亲: 回收子进程
创建匿名管道:
pipe(fd[2]);
fd[0] -- read
fd[1] - -write
管道的读写行为:
3. 查看管道缓冲区的大小
1>. 使用命令: ulimit -a
2>. 使用函数: fpathconf(int fd, name)
name == _PC_PIPE_BUF

3. fifo -- 有名管道
1>. 适用场景:
2>. 创建有名管道的两种方式:
命令: mkfifo 管道名
函数: int mkfifo(const char *pathname, mode_t mode);
pathname: 管道名
mode: 八进制数, 文件权限
3>. 使用方法:
1. 使用open将管道打开
2. 进程a -- 写操作
3. 进程b -- 读操作
4. mmap/munmap 内存映射
1>. 创建内存映射 -- mmap
作用: 将磁盘文件的数据映射到内存, 用户通过修改内存就能修改磁盘文件
函数原型:
void *mmap(
void *adrr,          // 映射区首地址,传NULL
size_t length,     // 映射区的大小 == 映射的文件长度
int prot,             // 映射区权限
PROT_READ -- read
PROT_WRITE -- write
int flags,             // 标志位参数
MAP_SHARE -- 内存映射区和磁盘数据会同步
MAP_PRIVATE -- 内存映射区和磁盘数据不会同步
int fd,                 // 文件描述符
fd对应需要映射数据到内存的文件
off_t offset        // 映射文件的偏移量
);
<1>. 将文件映射到内存, 通过内存读写文件
<2>. 使用mmap进行有血缘关系的进程间通信
<3>. 使用mmap进行没有血缘关系的进程间通信
2>. 关闭内存映射 -- munmap
函数原型: int munmap(void *addr, size_t length);
addr -- mmap的返回值
length -- mmap的第二个参数
3>. 思考问题:
1). 如果对mmap的返回值(ptr)做++操作(ptr++), mumap是否能够成功?
2). 如果open时O_RDONLY, mmap时prot参数指定PROT_READ | PROT_WRITE会怎样?
3). 如果文件偏移量为1000会怎样?
4). 如果不检测mmap的返回值会怎样?
5). mmap什么情况下会调用失败?
6). 可以open的时候O_CREAT一个新文件来创建映射区吗?
7). mmap后关闭文件描述符,对mmap映射有没有影响?
8). 对ptr越界操作会怎样?
1. mmap/munmap 内存映射
1>. 创建内存映射 -- mmap
作用: 将磁盘文件的数据映射到内存, 用户通过修改内存就能修改磁盘文件
函数原型:
void *mmap(
void *adrr,          // 映射区首地址,传NULL
size_t length,     // 映射区的大小 == 映射的文件长度
int prot,             // 映射区权限
PROT_READ -- read
PROT_WRITE -- write
int flags,             // 标志位参数
MAP_SHARED -- 内存映射区和磁盘数据会同步
MAP_PRIVATE -- 内存映射区和磁盘数据不会同步
int fd,                 // 文件描述符
fd对应需要映射数据到内存的文件
off_t offset        // 映射文件的偏移量
);
<1>. 将文件映射到内存, 通过内存读写文件
<2>. 使用mmap进行有血缘关系的进程间通信
<3>. 使用mmap进行没有血缘关系的进程间通信
2>. 关闭内存映射 -- munmap
函数原型: int munmap(void *addr, size_t length);
addr -- mmap的返回值
length -- mmap的第二个参数
3>. 思考问题:
1). 如果对mmap的返回值(ptr)做++操作(ptr++), mumap是否能够成功?
2). 如果open时O_RDONLY, mmap时prot参数指定PROT_READ | PROT_WRITE会怎样?
3). 如果文件偏移量为1000会怎样?
4). 如果不检测mmap的返回值会怎样?
5). mmap什么情况下会调用失败?
6). 可以open的时候O_CREAT一个新文件来创建映射区吗?
7). mmap后关闭文件描述符,对mmap映射有没有影响?
8). 对ptr越界操作会怎样?
2. Linux中的信号初步认知
1>. 特点:
1. 简单
2. 携带的信息量少
3. 发生在特定的场景
2>. 信号的状态
1. 创建信号,5种:
1. 系统调用
2. 命令
3. 键盘
4. 软条件:定时器
5. 硬件: 段错误
2. 未决
信号已经到达了某个进程, 但是还没有被处理, 一般都是由阻塞引起的
3. 递达
信号处理.
3>. 处理方式
1. 执行默认操作   2. 忽略     3. 捕捉, 执行自定义操作
4>. 信号的四要素
编号 名字    事件      动作
1   SIGINT  ctrl+c    终止
5>. 通过man文档查看信号
man 7 signal
默认处理动作:
1. 终止   2. 暂停   3. 继续  4. 忽略   5. 终止, 并且产生core
The signals SIGKILL(9) and SIGSTOP(19) cannot be caught, blocked, or ignored.
6>. 概念: 阻塞信号集, 未决信号集
pcb: 阻塞信号集(信号屏蔽字), 未决信号集
3. 信号相关函数
1>. kill -- 发送信号给指定进程
函数原型: int kill(pid_t pid, int sig);
参数: pid
>0: 指定进程的pid
=0:
2>. raise -- 自己给自己发信号  == kill(getpid(), sig);
函数原型:  int raise(int sig);
3>. abort -- 给自己发送异常终止信号 - SIGABRT
函数原型: void abort(void);
没有参数没有返回值, 永远不会调用失败

4>. 闹钟(定时器)
1). alarm -- 设置定时器(每个进程只有一个定时器)
1. 函数原型:unsigned int alarm(unsigned int seconds);
当时单位: 秒
当时间到达之后, 函数发出一个信号:SIGALRM
获取程序的执行时间: time  ./可执行程序
总时间 = 用户 + 内核 + 消耗
io操作效率很低, 可以优化
重定向输出: >
2. 取消定时器: alarm(0);
2). setitimer -- 定时器, 并实现周期性定时
函数原型: int setitimer(int which,
const struct itimerval *new_value,
struct itimerval *old_value);

4. 信号集操作相关函数
概念:
未决信号集:程序还没有处理的信号, 存在pcb, 内核操作
默认全部为0, 没有被处理的1
没有被处理的原因: 被阻塞了
阻塞信号集:
在信号处理之前, 会读pcb中的阻塞信号集, 如果对应的标志位1, 代表阻塞
如果想阻塞信号, 修改阻塞信号集中的数据
1. 自定义一个信号集
sigset_t myset;
2. 初始化自定义信号集, 默认为0
sigemptyset(&myset);
3. 如果想阻塞某个信号: 将这个信号添加自定义信号集
sigaddset(&myset, SIGINT);
sigaddset(&myset, SIGQUIT);
4. 自定义信号集, 添加到阻塞信号集
sigprocmask(SIG_BLOCK, &myset, NULL);
5. 查看未决信号集
1>. 自定义信号集
int sigemptyset(sigset_t *set);                   将set集合置空
int sigfillset(sigset_t *set);                      将所有信号加入set集合
int sigaddset(sigset_t *set,int signo);     将signo信号加入到set集合
int sigdelset(sigset_t *set,int signo);   从set集合中移除signo信号
int sigismember(const sigset_t *set,int signo); 判断信号是否存在
参数: set - -信号集
signo -- 代表信号的编号
2>. sigpromaks -- 屏蔽and接触信号屏蔽, 将自定义信号集设置给阻塞信号集
函数原型: int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
how: 设置阻塞 解除阻塞  覆盖
set: 自定义信号集
oldset: 设置之前内核中的阻塞信号集
3>. sigpenging -- 读取当前进程的未决信号集
函数原型: int sigpending(sigset_t *set);
参数: set -- 内核将未决信号集写入set
5. 信号捕捉
1>. siganl函数
2>. sigaction函数
函数原型: int sigaction(int signum,
const struct sigaction *act,
struct sigaction *oldact
);

3>. 内核实现信号捕捉的过程
6. SIGCHLD信号
1>. 产生的条件
2>. 使用该信号回收子进程
3>. 使用时的注意事项
7. 信号传参

1. 信号捕捉:
1>. sigaction函数
函数捕捉的是sigint, 那么在sigint处理函数执行期间, sigint信号也是被临时阻塞的
函数原型: int sigaction(int signum,
const struct sigaction *act,
struct sigaction *oldact
);
参数: signum - 捕捉的信号
struct sigaction {
void     (*sa_handler)(int);        // 函数指针 -- 指向信号处理函数 -- 常用
sa_flags = 0;
void     (*sa_sigaction)(int, siginfo_t *, void *); // 函数指针 -- 指向信号处理函数
sa_flags = SA_SIGINFO;
sigset_t   sa_mask;        // 自定义信号集
在信号的处理过程中, 设置屏蔽某些信号
当信号处理函数调用完成, 屏蔽解除
我们要做什么?
1. 初始化sa_mask -- sigemptyset(&sa_mask);
2. 添加要阻塞的信号: -- sigaddset(&sa_mask, 要阻塞的信号)
int        sa_flags;                        // 指定使用哪一个信号处理函数
void     (*sa_restorer)(void);    // 不需要关系, 被废弃
};
2>. 内核实现信号捕捉的过程
3. SIGCHLD信号
1>. 产生的条件
1>. 当子进程死的时候
2>. 子进程暂停/恢复运行(父进程忽略)
2>. 使用该信号回收子进程
代码处理
3>. 使用时的注意事项
1>. 子进程会继承父进程的: 阻塞信号集, 信号处理动作(siganl/sigaction)
2>. 不继承: 未决信号集
3>. 在为父进程的信号处理动作注册成功之前, 孩子已经死了.
处理: 在注册成功之前, 先阻塞SIGCHLD, 当注册成功, 解除阻塞.
1>. 子进程创建出来之后,没有做任何事情, 被干死.
4. 信号传参
sigqueue -- 信号发送的时候可以传参
kill(pid, sig);
int sigqueue(pid_t pid, int sig, const union sigval value);
union sigval
{
int   sival_int;        // 两个进程直接使用, 栈空间
void  *sival_ptr;        // 进程自己给自己发数据
};

思考:
kill(pid, sig); 如何捕捉?
siganl
sigaction: sigaction结构体使用
1. 设置: sa_flags = 0;
2. void (*sa_handler)(int);
sigqueue(pid_t pid, int sig, const union sigval value);
参数:
pid: 接收信号的进程pid
sig: 给指定进程发送的信号
value: 例: 两个不同的进程: value.sival_int = 100;
捕捉该信号:
需要拿到传递的参数: 设置sigaction结构体
1. sa_flags = SA_SIGINFO;
2. 使用: (*sa_sigaction)(int, siginfo_t *, void *);
取出传递的参数: siginfo_t结构体中的变量:
int  si_int;
5. 守护进程  --   精灵
1. 没有终端的进程
2. 用户注销再次登录, 守护进程不会关闭
3. 系统的服务都是守护进程
4. 周期性的执行某些操作
1>. 终端:
tty1 -- tty6 -- 文字终端 ctrl+alt+(f1-f6)
图形界面终端: tty7 ctrl+alt+f7
虚拟终端: pts/xx
2>. 通过文件描述符得到对应的文件名:
函数: char *ttyname(int fd);
3>. 进程组
1). 进程组组长的选则
进程组中的第一个进程
2). 进程组ID的设定
如何查看进程组ID: ps ajx    --- 在第三列 PGID
进程组的ID == 组长的ID
3). 获取当前进程的进程组ID
pid_t getpgrp(void);
4). 获取指定进程的进程组ID
pid_t getpgid(pid_t pid);
pid: 指定进程的PID
pid == 0: 代表当前进程
5). 设置指定进程的进程组ID -- 加入一个现有的进程组或创建一新进程组
int setpgid(pid_t pid, pid_t pgid);
4>. 会话 -- 多个进程组
1). 创建一个会话注意事项:
1>. 不能是进程组长
2>. 创建会话的进程成为新进程组的组长
创建一新的会话 -> 新的进程组 -> 组长: 创建会话的进程, 还是会长
3>. 有些linux版本需要root权限执行此操作(ubuntu不需要)
4>. 创建出的新会话会丢弃原有的控制终端
5>. 一般步骤:先fork, 父亲死, 儿子执行创建会话操作(setsid)
2). 获取进程所属的会话ID
pid_t getsid(pid_t pid);
3). 创建一个会话
pid_t setsid(void);
5>. 创建守护进程模型
1). 创建子进程,父进程退出
所有工作在子进程中进行形式上脱离了控制终端
2).    在子进程中创建新会话
setsid()函数
使子进程完全独立出来,脱离控制
3).    改变当前目录为根目录 -- 程序的运行目录
chdir()函数
防止占用可卸载的文件系统, 也可以换成其它路径
不改也可以.
4).    重设文件权限掩码
umask()函数
防止继承的文件创建屏蔽字拒绝某些权限
增加守护进程灵活性
可以不改.

5).    关闭文件描述符
不关也可以.
继承的打开文件不会用到,浪费系统资源,无法卸载
6).    开始执行守护进程核心工作
守护进程要做的事情.
7).    守护进程退出处理程序模型
关闭操作系统的时候退出

6. 了解线程相关的概念
1>. 什么是线程
2>. 线程的特点
3>. 线程共享/非共享资源
4>. 线程优缺点
7. 线程控制相关函数
相同点: 函数调用成功返回0, 失败返回 错误号
1>. 创建线程 -- pthread_create
1>. 函数原型:
int pthread_create(
pthread_t *thread,                              // 线程ID
=无符号长整形
const pthread_attr_t *attr,             // 线程属性, NULL
void *(*start_routine) (void *),     // 线程处理函数
void *arg                                                    // 线程处理函数参数
);
2>. 使用第四个参数arg给回调函数传参时的注意事项
3>. 验证线程之间共享全局变量
2>. 单个线程退出 -- pthread_exit
1>. 函数原型:    void pthread_exit(void *retval);
2>. 退出方式 -- 效果?
1). exit
2). return
3). pthread_exit:
3>. 阻塞等待线程退出, 获取线程退出状态 -- pthread_join
1>. 函数原型:     int pthread_join(pthread_t thread, void **retval);
4>. 线程分离 -- pthread_detach
1>. 函数原型:     int pthread_detach(pthread_t thread);
2>. 调用该函数之后还需要pthread_join么?
5>. 杀死(取消)线程 -- pthread_cancel
1>. 函数原型:  int pthread_cancel(pthread_t thread);
2>. 使用注意事项:
6>. 比较两个线程ID是否相等(预留函数) -- pthread_equal
1>. 函数原型: int pthread_equal(pthread_t t1, pthread_t t2);
8. 线程属性(了解内容)

原子操作:
1. cup在处理指令的时候不会中断
printf("hello");
// 原子操作不会再该位置事情cup
printf("world");
2. 非原子操作:
printf("hello");
// 失去cup
printf("world");

1. 了解线程相关的概念
1>. 什么是线程
轻量级的进程 -- Linux
2>. 线程的特点
1. 共用pcb, 栈
2. 不占用资源, 是最小的执行单元
3. 进程是最小的资源分配单元, 进程可以退化为线程
4. 对应内核来说, 线程就是进程
3>. 线程共享/非共享资源
共享: 全局变量, 代堆 -- 使用其进行线程间通信
不共享: 栈,
4>. 线程优缺点

2. 线程控制相关函数
相同点: 函数调用成功返回0, 失败返回 错误号
1>. 创建线程 -- pthread_create
1>. 函数原型:
int pthread_create(
pthread_t *thread,                              // 线程ID
获取线程ID, 传出参数, unsigned long int -- %lu
const pthread_attr_t *attr,             // 线程属性, NULL
默认值NULL
void *(*start_routine) (void *),     // 线程处理函数
回调函数, 线程的处理操作
void *arg                                                    // 线程处理函数参数
回调函数的参数
);
2>. 使用第四个参数arg给回调函数传参时的注意事项
3>. 验证线程之间共享全局变量
2>. 单个线程退出 -- pthread_exit
1>. 函数原型:    void pthread_exit(void *retval);
参数: retval: 可以携带退出信息, 一般传NULL
2>. 退出方式 -- 效果?
1). exit: 退出进程, 所有线程都会杀死
2). return: 返回到调用者的位置
3). pthread_exit: 退出指定的线程, 其他线程不受影响
3>. 阻塞等待线程退出, 获取线程退出状态 -- pthread_join -- wait/waitpid(-1, status, 0)
回收的是子线程的pcb
1>. 函数原型:     int pthread_join(pthread_t thread, void **retval);
参数: thread: 线程id
retval: 线程退出时候的状态信息 void* ptr   &ptr
使用的注意事项:
1>. ptr 需要指向有效内存: 堆内存, 全局变量的地址
线程退出的时候, 传递的对内存或者全局变量的内存
return 地址:
pthread_exit(地址)
4>. 线程分离 -- pthread_detach
1>. 函数原型:     int pthread_detach(pthread_t thread);
参数: thread: 子线程ID
2>. 调用该函数之后还需要pthread_join么?
不需要
5>. 杀死(取消)线程 -- pthread_cancel
1>. 函数原型:  int pthread_cancel(pthread_t thread);
2>. 使用注意事项:
需要一取消点.
1. 有系统调用
2. pthread_testcancle();    // ==
6>. 比较两个线程ID是否相等(预留函数) -- pthread_equal
1>. 函数原型: int pthread_equal(pthread_t t1, pthread_t t2);

PS: 线程同步函数返回值:成功0, 失败返回错误号.(不包含信号量)
什么叫线程同步: 协同步调, 按照先后顺序去执行
1. 互斥量(互斥锁)
1>. 互斥锁类型: pthread_mutex_t mutex; -- 创建一把互斥锁
2>. 互斥锁的特点:
所有的线程都是排队访问共享资源
3>. 使用互斥锁缺点?
降低了程序的并发性
互斥锁的使用步骤:
1. 创建一把互斥锁 -- 理解为int类型的数
pthread_mutex_t mutex;
2. 初始化互斥锁 -- 初始化成功, 是没有锁住的状态, 1
两种方式:
1. 静态初始化: pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; // 不常用
2. 动态初始化: pthread_mutex_init(&mutex, NULL);    // 常用的方式
3. 访问共享资源之前, 对共享资源加锁  --  被锁住:  0  --
pthread_mutex_lock(&mutex);
4. 访问完毕, 解锁   ---   ++   1
pthread_mutex_unlock(&mutex);
5. 销毁互斥锁
pthread_mutex_destroy(&mutex);
加锁注意事项:
1. 加锁的区域 -- 临界区 lock和unlock中间的代码
lock
......
xxxxx
unlock
2. 临界区越小越好

4>. 互斥锁相关函数:
1). pthread_mutex_init(        // 初始化互斥锁
pthread_mutex_t *restrict mutex,
const pthread_mutexattr_t *restrict attr
);
2). pthread_mutex_destroy(pthread_mutex_t *mutex);    // 销毁互斥锁
3). pthread_mutex_lock(pthread_mutex_t *mutex);            // 加锁
4). pthread_mutex_trylock(pthread_mutex_t *mutex);    // 尝试加锁, 失败返回, 不阻塞
5). pthread_mutex_unlock(pthread_mutex_t *mutex);        //    解锁
5>. 练习: 两个线程操作全局变量, 交替数数.
2. 死锁 -- 造成死锁的原因?
1>. no zuo no die -- 对自己加锁两次
2>. 吃着碗里瞧看着锅里 --
线程A用于A锁, 线程B用于B锁, 线程A请求B锁, 线程B请求A锁
3. 读写锁 -- 只有一把锁
1>. 读写锁类型: pthread_rwlock_t rwlock;
2>. 读写锁状态: -- 2种
1. 写锁: 锁写操作
2. 读锁: 锁读操作
3. 没加锁
3>. 读写锁的特性: 读共享, 写独占, 写的优先级高
1. 加读锁: 线程A加锁成功, 又来了三个线程, 做读操作, 可以加锁成功   -- 读锁共享
2. 加写锁: 线程A加锁成功, 又来了三个线程, 做读操作, 三个线程阻塞   -- 写锁独占
3. 加读锁: 线程A加锁成功, 又来了2个线程, 读写同时来了, A是否锁之后, 写加锁成功, 读阻塞.
-- 写的优先级高
4>. 读写锁场景练习:
1. 线程A请求得到了写锁, 线程B请求读锁
线程B阻塞, 等待A释放写锁
2. 线程A持有读锁, 线程B请求写锁
写阻塞, 等待A释放读锁
3. 线程A拥有读锁, 线程B请求读锁
B加读锁成功
4. 线程A持有读锁, 线程B请求写锁, 线程C请求读锁
A释放读锁, B加锁成功, C阻塞
5. 线程A持有写锁, 线程B请求读锁, 线程C请求写锁
A释放写锁, C加锁成功, B阻塞
5>. 读写锁适用场景: 多操作次数比写操作多的情况
6>. 主要函数:
1). pthread_rwlock_init(                                                                // 初始化读写锁
pthread_rwlock_t *restrict rwlock,
const pthread_rwlockattr_t *restrict attr
);
2). pthread_rwlock_destroy(pthread_rwlock_t *rwlock);        // 销毁读写锁
3). pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);      // 加读锁
4). pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);    // 尝试加读锁
5). pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);        // 加写锁
6). pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);    // 尝试加写锁
7). pthread_rwlock_unlock(pthread_rwlock_t *rwlock);        // 解锁
4. 条件变量 -- 不是锁, 但是可以造成线程阻塞
1>. 主要包括两个动作:
1). 一个线程等待"条件变量的条件成立"而挂起;
2). 另一个线程使"条件成立"(给出条件成立信号)。
为了防止竞争,条件变量的使用总是和一个互斥锁结合在一起。
2>. 条件变量类型:
3>. 主要函数:
1). pthread_cond_init(                                                        // 初始化一个条件变量
pthread_cond_t *restrict cond,
const pthread_condattr_t *restrict attr
);
2). pthread_cond_destroy(pthread_cond_t *cond);        // 销毁一个条件变量
3). pthread_cond_wait(                                                        // 阻塞等待一个条件变量
pthread_cond_t *restrict cond,
pthread_mutex_t *restrict mutex
);
4). pthread_cond_timedwait(                                                // 限时等待一个条件变量
pthread_cond_t *restrict cond,
pthread_mutex_t *restrict mutex,
const struct timespec *restrict abstime
);
5). pthread_cond_signal(pthread_cond_t *cond);        // 唤醒至少一个阻塞在条件变量上的线程
6). pthread_cond_broadcast(pthread_cond_t *cond);    // 唤醒全部阻塞在条件变量上的线程
4>. 练习: 实现生产者, 消费者模型
5. 信号量 -- 头文件 <semaphore.h>
1>. 基本描述:
1. 进化版的互斥锁, 实现了多个进程对某一对象部分数据进程共享
2. 既能保证同步,数据不混乱,又能提高线程并发
2>. 信号量类型:
3>. 主要函数:
1>. sem_init(sem_t *sem, int pshared, unsigned int value);    // 初始化信号量
2>. sem_destroy(sem_t *sem);                                                                // 销毁信号量
3>. sem_wait(sem_t *sem);                                                                        

// 给信号了加锁 --
4>. sem_trywait(sem_t *sem);                                                                // 尝试对信号量

加锁 --
5>. sem_timedwait(sem_t *sem);                                                            // 限时尝试对信号量加锁

--
6>. sem_post(sem_t *sem);                                                                        

// 给信号量解锁 ++

7)    Linux网络编程
1. 协议概念
一组规则。要求使用协议的双方,必须严格遵守协议规则。
数据链路层: 以太网帧、 ARP数据报
网络层: IP、ICMP、IGMP
传输层: TCP、UDP
------------------------------------------
应用层:http、ftp、ssh。。
2. 网络应用程序的设计模式
c/s: client/server  客户端 服务器
b/s: browser/server  浏览器 服务器
CS模型:                    BS模型:
优点:
1. 缓存数据到本机                跟CS相反。
2. 协议选择灵活
缺点:
1. 用户的安全性不能得到保障
2. 消耗系统资源
3. 开发任务量较大
4. 移植性较差
3. 分层模型
4层: TCP/IP 4层模型
数据链路层、网络层、传输层、应用层。
7层: OSI
物、数、网、传、会、表、应
4. 协议格式
以太网帧格式
借助 mac 地址完成数据包的传递。
ARP数据报
跟据 IP地址获取 mac 地址。
IP段格式
192.168.23.119 --- 点分十进制。 --- string。
格式:4位 --- IPv4
8位,TTL: time to live  数据包生存时间。记录数据包在网络环境中经过的跳数。
4字节源IP地址
4字节目的IP地址
TCP数据包格式
16位源端口  --- 65535
16位目的端口
32位序号
32位确认序号
标志位--6个
窗口大小---16位 --- 6553
UDP数据包格式
16位源端口  --- 65535
16位目的端口
5. NAT映射
NAT映射表:完成公网IP和内网IP的一一对应关系。  --端口号
路由表 --- 寻路
6. 打洞机制
-----socket 编程
套接字概念、本质
在数据通信过程中一定 成对出现。
一个套接字由 两个缓存区组成(发送、接受)。
一个套接字由 一个文件描述符引用。
IP地址可以在网络环境中唯一的标示 一台目的主机。
端口号 可以在主机中唯一标示一个进程。
IP + 端口号 --> socket
C/S模型 TCP 通信  --- 大小写字母转换  server.c、 client.c
预备知识:
网络字节序
大端法。
192.168.23.119 --- string --- 数值类型 --- htonl、htons/ntohs
IP地址 转换函数
int inet_pton(int af, const char *src, void *dst);   string -- 网络
af: AF_INET   AF_INET6
src:点分十进制的IP地址
dst:转换为网络字节序的 IP地址(传出)。
const char *inet_ntop(int af, const void *src,        网络-- string
char *dst, socklen_t size);
af: AF_INET   AF_INET6
src: 转换为网络字节序的 IP地址(传出)。
dst: 用于储存点分十进制的Ip地址 结构
size: 大小
sockaddr 结构体 类型 scokaddr_in 类型。
struct sockaddr_in addr;
bind(fd, (struct sockaddr *)&addr, size);
网络套接字函数:
int socket(int domain, int type, int protocol);   创建一个套接字
domain:AF_INET / AF_INET6 / AF_UNIX
type:    SOCK_STREAM / SOCK_DGRAM
protocol: 0
struct sockaddr_in addr;
addr.sin_family=AF_INET;
addr.sin_port = htons(9527);
addr.sin_addr.s_addr = inet_pton("192.168.23.119");
.sin_addr.s_addr = htonl(INADDR_ANY);   表示本地任意一个有效的IP地址。

int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen); 给套接字绑定地址结构(IP+端口)
sockfd: socket函数返回值
addr: (struct sockaddr *)&addr
addrlen: sizeof(addr)
int listen(int sockfd, int backlog);   设置 同时 与服务器建立连接的客户端上限数。
sockfd:socket函数返回值
backlog: 上限数值
定义一个空的 sockaddr 地址结构。
struct sockaddr_in cli_addr;
socklen_t clie_len = sizeof(cli_addr);
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
sockfd: socket函数返回值
addr: 传出参数。(struct sockaddr *)&cli_addr  传出 与 服务器成功建立连接的那个客户端的 地址结构。
addrlen: 传入传出参数。 &clie_len;
返回:一个新的文件描述符。指向已经跟服务器建立好连接的那个套接字。

int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

sockfd: socket函数返回值
addr: 服务器端 地址结构(IP+端口号)
addrlen: 大小

客户的 socket 对应得IP+端口 可以依赖于“隐式绑定”。 192.168.23.110  8000 ----- abcd
流程:
server:       
1. socket()
2. bind()
3. listen();
4. accept();     阻塞
5. read()
6. 小--大写
7. write()
8. close();

client:    
1. socket();
2. connect();
3. write();
4. read();
5.close();

1. client.c
2. TCP通信时序
TCP:面向连接的可靠的数据包传递。 ———— 针对不稳定的网络层,进行完全弥补。  序号、确认序号。
UDP:无连接的不可靠的报文传递。  ———— 针对不稳定的网络层,进行完全不弥补。
3次握手
发起连接一端,发送 SYN 标志位,携带包号
被动建立连接端, ACK应答,并且发送SYN,与对端建立连接
主动发起连接端,再次ACK应答。
4次握手
支持半关闭。
首 FIN --- ACK --  半关闭完成 --  次 FIN -- ACK  -- 关闭
3. 滑动窗口 ---- 流量控制。
通知对端,本端接受数据包的上限值。  实时修改。
防止发送数据端与接受数据端,效率不一致导致的 数据错误。
4. MTU
最大传输单元 --- 协议层
5. mss
最大报文长度(一次能传递的数据包的大小)
6. 半关闭
read 函数的返回值:
1. > 0 实际读到的字节数
2. = 0 已经读到结尾(对端已经关闭)
3. -1 应进一步判断errno的值:
errno = EAGAIN or EWOULDBLOCK: 设置了非阻塞方式 读。 没有数据到达。
errno = EINTR 慢速系统调用被 中断。
errno = “其他情况” 异常。
7. 错误处理模块
设计思想
系统调用 都应该检查返回值。
首字母大写,函数名不变。
readn
readline
8. 多进程并发服务器
server.c
1. lfd = socket();
2. bind();  serv_addr
3. listen();
4. while (1) {
accept(); --> cfd
fork(); ---> 子进程
}
5. 父进程:
继续调用 accept();  位于循环中。
close(cfd);
回收子进程  waitpid
注册信号捕捉函数,捕捉 SIGCHLD 信号。
while(){} 循环回收 ---> 信号捕捉函数中
子进程:
close(lfd);
与客户端进行数据传递。 --  read、 转大写 、 write
read --- 0 对端关闭 --- close(cfd);
client.c  同 原来的 client.c
serve.c --- server.o
warp.c --- warp.o
gcc server.o warp.o -o server

1. TCP状态转换图
主动发起连接一方: CLOSE -- 发送 SYN -- SYN_SENT -- 接收到对端的ACK -- ESTABLISHED (数据通信)
主动关闭连接一方: ESTABLISHED -- 发送 FIN -- FIN_WAIT_1 -- 接收到对端的ACK -- FIN_WAIT_2 (半关闭)
-- 对端发送FIN ,且 本端回发ACK -- TIME_WAIT -- 2MSL 时长 -- CLOSE
被动发起连接一方: CLOSE -- LISTEN -- 接受对端的SYN, 并且 回发了ACK -- SYN_RCVD -- 接受ACK -- ESTABLISHED (数据通信)
被动关闭连接一方: ESTABLISHED -- 接收 FIN, 并且回发了ACK -- CLOSE_WAIT -- 发送FIN
-- LAST_ACK -- 接到最后一个ACK -- CLOSE
2. 2MSL
一定出现在 主动 关闭连接端。
TIME_WAIT 状态,一定出现在 主动 关闭连接端。
等待一个固定的时长----确保,对端介绍 最后一个ACK
端口复用
int opt = 1;
setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
函数 --- 查资料
APUE --- LInux系统编程。
UNP -- LInux网络编程。
TCP/IP详解 3卷
3. 心跳机制
心跳包 --- 检测客户端和服务器之间的 连接状态。
兵乓包 --- 检测客户端和服务器之间的 连接状态的同时,携带少量数据。
TCP的属性: 探测网络连接状态。 探测分节。
4. 半关闭
函数 ---了解。
shutdown(int fd, int how);
how:     SHUT_RD
SHUT_WR
SHUT_RDWR
5. select
fd_set set;
void FD_ZERO(fd_set *set);     ------- 清空一个文件描述符集合
set: fd_set 类型变量的地址。FD_ZERO(&set);
void FD_SET(int fd, fd_set *set); ---- 向集合中添加某个文件描述符
fd:待添加的文件描述符
set: 集合            FD_SET(lfd, &set);FD_SET(cfd1, &set);FD_SET(cfd2, &set);FD_SET(cfd3, &set);
void FD_CLR(int fd, fd_set *set); ----- 从集合中将某一个文件描述符 清除
fd:待清除的文件描述符
set: 集合
int  FD_ISSET(int fd, fd_set *set); ---- 判别 某个fd是否在 集合中
fd:待判定的文件描述符
set: 集合
返回值: 1:在 0;不在。
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
nfds: 所有监听的文件描述符中,最大的文件描述符+1
readfds: 监听读事件的文件描述符集合     传入传出参数
writefds: 监听写事件的文件描述符集合    传入传出参数
exceptfds: 异常             传入传出参数
timeout:超时等待时间
NULL: 永久等待
> 0; 等待时长
0 : 不等待。
ret = select(lfd+1, &set, NULL, NULL, NULL);  -- 3
for(i = 2; i < lfd+1; i++) {
if (FD_ISSET(lfd, &set)) {
// 有新客户端连接请求达到
cfdx = Accept();          阻塞??? 不会!
}
if (FD_ISSET(cfd1, &set)) {
// 有新客户端数据达到
read()
转大写
write()
}
}
select :        优点: 跨平台。
缺点:     监听的上限受 1024 限制。
实现原因导致效率低下。
易用性差。
select 实现的 多路IO转接 服务器。
1. poll函数
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
fds: struct pollfd 类型数组   (传入传出)
nfds:有效的数组元素个数。
timeout: 超时时长
-1 : 永久
0: 不阻塞
>0:  实际毫秒数。
返回:  数组中所有满足对应事件的文件描述符 总个数。
struct pollfd {
int fd;          待监听的文件描述符
short events:     该文件描述符 所对应的监听事件: POLLIN/POLLOUT/POLLERR
short revents:     标记 fd 对应事件是否满足。  0  --- 满足 --- events的值。
}
突破1024
cat /proc/sys/fs/file-max    受计算机硬件限制的 文件描述符上限。
sudo vi /etc/security/limits.conf
*    soft     nofile    3000
*    hard     nofile    80000
较 select 优点
突破文件描述符 1024 限制。
实现文件描述符对应事件的 分离。
2. epoll
int epoll_create(int size);  ----- 创建一个监听红黑树
size:给内核参考值。待监听的fd个数。
返回值: efd 监听红黑树的 根节点。
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);   ------- 修改监听红黑树
epfd:根节点
op:操作方式: EPOLL_CTL_ADD / EPOLL_CTL_MOD / EPOLL_CTL_DEL
fd: 待操作的fd
event: fd事件描述结构体
struct epoll_event {
events;
data  ---- union {
int fd
void * ptr;   // 函数指针
U32
U64;
}
}
struct test {
int fd;
void (*test_func) (int fd, void *p, int *ps )
} *ptr;
void test_func (int fd) {
accept
read write;
}
int epoll_wait(int epfd, struct epoll_event *events,int maxevents, int timeout); ———— 阻塞监听 树上的节点  select 、 poll
epfd: 根节点
events:传出参数。数组。用来存储满足监听事件的文件描述符
maxevents: 数组容量。
timeout: 超时时长
-1 : 永久
0: 不阻塞
>0:  实际毫秒数。
返回值: 满足条件的总个数。
优点 缺点。
缺点: 仅限于Linux使用
优点: 效率较select、poll高
易用性较强。
3. ET模式: 边沿触发模式。
尚未处理结束的数据,不会触发epoll返回事件。
event.events = EPOLLIN | EPOLLET;
LT模式: 水平触发模式。
尚未处理结束的数据,会造成epoll触发,返回事件。
默认既是水平触发。
【高并发数据访问中,应付较大数据量时,选用ET模式 + 非阻塞IO方式 读取 socket。】
4. epoll反应堆模型。(libevent库核心实现 epoll)
libevent库,C语言实现的,开源的网络编程库。 开源、易用性强,跨平台,精简。 select、poll、epoll、dev/poll kqueue.....
windows/Linux/Mac os/*BSD
可读、可写、异常
可写:    管道     fd[0] / fd[1] --- 管道缓存区满,不可写, 阻塞。——不可写
套接字  ---- 不可写。 滑动窗口---流量控制。   epoll_wait(); fd  EPOLLOUT
epoll普通模型:epoll_create() --- epfd 红黑树 --- 将connfd 添加到 epfd , epoll_ctl() --  EPOLLIN 事件 ---- epoll_wait 设置监听
----- 满足条件 ---- epoll_wait 返回 ---- 数组.data.fd == connfd --- read() --- 小->大 --- write()
epoll反应堆模型: epoll_create() --- epfd 红黑树 --- 将connfd 添加到 epfd , epoll_ctl() --  EPOLLIN 事件 ---- epoll_wait 设置监听
—— 满足条件 ---- epoll_wait 返回 ---- 数组.data.fd == connfd --- read() ---- epoll_ctl --- 将connfd从红黑树摘下
---- 修改connfd监听事件 EPOLLOUT --- 添加到epfd 树 调用epoll_wait监听 写事件
----- epoll_wait返回说明 connfd 可写 ---- write() ———  将connfd从红黑树摘下 ---- 修改connfd监听事件 EPOLLIN(回调函数)
—— 添加到epfd 树 调用epoll_wait监听 读事件
1. 多播 --- 组播
对应广播
组播地址:
239.0.0.0~239.255.255.255
239.0.0.7 —— 组播地址。
获取网卡的序号:
ip ad  —— 网卡名、网卡序号。
unsigned int if_nametoindex(const char *ifname);  —— 根据网卡名称获取网卡序号。
结构体 struct ip_mreqn
struct ip_mreqn {
struct in_addr  imr_multiaddr;  239.0.0.2          /* IP multicast address of group */
struct in_addr  imr_address;      0.0.0.0          /* local IP address of interface */
int     imr_ifindex;                    /* Interface index */
};
定义组播地址: “239.0.0.2”
struct ip_mreqn group;
inet_pton(AF_INET, “239.0.0.2”, &group.imr_multiaddr)
inet_pton(AF_INET, “0.0.0.0”, &group.imr_address)
group.imr_address = htonl(INADDR_ANY);
group.imr_ifindex = if_nametoindex("eth0");
group;初始化完成。
开放权限  setsockopt();
setsockopt(sockfd, IPPROTO_IP, IP_MULTICAST_IF, &group, sizeof(group));
server.c
1. lfd = socket();
2. bind(); ---- 可以隐式绑定。
3. struct ip_mreqn group;  初始化group  —— 目的 为了使用setsockopt 创建组播组。
inet_pton(AF_INET, “239.0.0.2”, &group.imr_multiaddr)
inet_pton(AF_INET, “0.0.0.0”, &group.imr_address)
group.imr_ifindex = if_nametoindex("eth0");
4. setsockopt();
setsockopt(sockfd, IPPROTO_IP, IP_MULTICAST_IF, &group, sizeof(group));   创建一个组播组。
5. 定义:clie_addr; 并初始化  —— 目的 为sendto函数 传参
sin_family=AF_INET;
inet_pton(AF_INET, "239.0.0.2", &sin_addr.s_addr);  --- 组播IP地址。
sin_port = htons(9000);
6. sendto(lfd, buf, strlen(buf), 0, (struct sockaddr *)&clie_addr, sizeof(clie_addr));
client.c
1. cfd = socket();
2. bind();    —— 不能依赖隐式绑定。 必须显式指定port --- 9000     addr ---- INADDR_ANY
3. struct ip_mreqn group;  初始化group  —— 目的 为了使用 setsockopt 加入组播组。
inet_pton(AF_INET, “239.0.0.2”, &group.imr_multiaddr)
inet_pton(AF_INET, “0.0.0.0”, &group.imr_address)
group.imr_ifindex = if_nametoindex("eth0");
4. setsockopt();
setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &group, sizeof(group));    加入组播组
5. recvfrom();
recvfrom(cfd, buf, sizeof(buf), 0, NULL, NULL);
2. 分屏软件实现的思想。—— 流程
1. 截屏幕,保存成图片,流畅--24帧
2. 降帧 8/12, 提高流畅传输。
3. 发送端,发送之前压缩。
4. 解压缩。还原图像。
5. 截取修改后的局部屏幕,进行压缩,传递。
6. 组播——sender、view 位于同一个组播组。
3. 本地套 -- domain
IPC: 管道pipe、fifo、mmap、信号、domain——稳定性好。
注意事项:
struct sockaddr_un {
sun_family;            2
sun_path;
} addr;
lfd = socket(AF_UNIX, SOCK_STREAM, 0);
sun_family = AF_UNIX;
strcpy(sun_path, "temp.socket");
addr地址结构初始化完成。
len = offsetof(struct sockaddr_un, sun_path) + strlen(sun_path);
unlink();        删除bind函数待创建的文件。确保bind创建成功。
bind(lfd, (struct sockaddr *)&addr, len);
4. 对比本地套 和 网络套。
网络套接字                        本地套接字
server:    lfd = socket(AF_INET, SOCK_STREAM, 0);            lfd = socket(AF_UNIX, SOCK_STREAM, 0);
bzero() ---- struct sockaddr_in serv_addr;        bzero() ---- struct sockaddr_un serv_addr, clie_addr;
serv_addr.sin_family = AF_INET;                serv_addr.sun_family = AF_UNIX;
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_addr.sin_port = htons(8888);            strcpy(serv_addr.sun_path, "套接字文件名")
len = offsetof(sockaddr_un, sun_path) + strlen();
unlink();
bind(lfd, (struct sockaddr *)&serv_addr, sizeof());    bind(lfd, (struct sockaddr *)&serv_addr, len);  创建新文件
Listen(lfd, 128);                    Listen(lfd, 128);
cfd = Accept(lfd, ()&clie_addr, &len);            cfd = Accept(lfd, ()&clie_addr, &len);
client:
lfd = socket(AF_INET, SOCK_STREAM, 0);            lfd = socket(AF_UNIX, SOCK_STREAM, 0);
" 隐式绑定 IP+port"                    bzero() ---- struct sockaddr_un clie_addr;
bzero() ---- struct sockaddr_in serv_addr;        clie_addr.sun_family = AF_UNIX;
serv_addr.sin_family = AF_INET;                strcpy(clie_addr.sun_path, "client套接字文件名")
len = offsetof(sockaddr_un, sun_path) + strlen();
unlink();
bind(lfd, (struct sockaddr *)&clie_addr, len);
inet_pton(AF_INT, "服务器IP", &sin_addr.s_addr)
bzero() ---- struct sockaddr_un serv_addr;
serv_addr.sin_port = htons("服务器端口");        clie_addr.sun_family = AF_UNIX;
strcpy(clie_addr.sun_path, "client套接字文件名")
len = offsetof(sockaddr_un, sun_path) + strlen();
connect(lfd, &serv_addr, sizeof());            connect(lfd, &serv_addr, len);
4. webbench

8)    数据库编程
1 oracle安装
    安装目录不能有中文和空格
2 体系结构和服务
    数据文件+实例(内存)
    OracleServiceORCL  c:\app\administrator\product\11.2.0\dbhome_1\bin\ORACLE.EXE ORCL
    C:\app\Administrator\product\11.2.0\dbhome_1\BIN\TNSLSNR  OracleOraDb11g_home1TNSListener
3 查询
    查询可以全部列,部分列,表达式,别名
    排序可以按列名,别名,表6达式,序号
    分组的要求: 在select中出现的非组函数的列必须在group by后出现 ,where 后面不能使用组函数,当where 和having 都可以 ,使用where
    综合练习:
        1.多个条件过滤时,如何写sql的条件更优化?
            and 的时候,易假的在右侧,or的时候相反
            select * from emp where deptno=10 or deptno = 20 and sal = 1300;
            select * from emp where deptno=20 or deptno = 10 and sal = 1300;
            select * from emp where (deptno=20 or deptno = 10) and sal = 1300;
        2.between and 语句的特点和要求? 闭区间,从小到大
        3.distinct的作用域 整行
        4.排序时 asc 和 desc代表的含义以及作用域? asc 是默认的排序模式,升序的 ,作用域:它之前的最近一个字段
        5.求10号部门的最低薪水和最高薪水.
        select min(sal),max(sal) from emp where deptno =10;
        6.求1980年12月17日入职的员工信息
        select * from emp where hiredate=to_date('1980-12-17','yyyy-mm-dd')
        select * from emp where to_char(hiredate,'yyyy-mm-dd')='1980-12-17';
        7.求当前日期所在的月份的最后一天和第一天,显示格式为:yyyymmdd
        select to_char(last_day(sysdate),'yyyymmdd') ,to_char(sysdate,'yyyymm')||'01' from dual;
        8.查询名字中有字母A,并且是 MANAGER 的员工
        select * from emp where ename like '%A%' and job ='MANAGER';
4 null的总结
    1. 表达式有null 结果为null 2.null = null 和 null != null ,永远不成立
    3. not in 的集合中不能有null  4. null代表无穷大,排序默认在最后 nulls last ,nvl(a,b) 如果a 为null,返回 b ,否则返回 a

    理论基础:笛卡尔集 行=tab1行数*tab2行数 列=tab1列数+tab2列数
    多表连接如何写连接,一般要给表起别名(需要知道,可以用 表名.列名)
    等值连接(N-1)
        --查询员工信息:员工号 姓名 月薪和部门名称
        select e.empno,e.ename,e.sal,d.dname
          from emp e,dept d
         where e.deptno = d.deptno;
    不等值连接
        --查询员工信息:员工号 姓名 月薪 和 薪水级别(salgrade表)
         select * from salgrade;
         GRADE      LOSAL      HISAL
         select e.empno,e.ename,e.sal,s.grade
           from emp e,salgrade s
          where e.sal >= s.losal
            and e.sal <= s.hisal;
         ==>修改成 between and
         select e.empno,e.ename,e.sal,s.grade
           from emp e,salgrade s
          where e.sal between  s.losal and  s.hisal;        
    外连接 左外连接 右外连接
        --按部门统计员工人数,显示如下信息: 部门号 部门名称 人数
        select d.deptno,d.dname,count(*)
          from dept d,emp e
         where d.deptno = e.deptno
        group by d.deptno,d.dname;
        --上述结果缺少40号部门
        ---左外连接
        select d.deptno,d.dname,count(*)
          from dept d,emp e
         where d.deptno = e.deptno(+)
        group by d.deptno,d.dname;
        ===>
        select d.deptno,d.dname,count(e.empno)
          from dept d,emp e
         where d.deptno = e.deptno(+)
        group by d.deptno,d.dname
        order by d.deptno;
        --右外连接
        select d.deptno,d.dname,count(*)
          from dept d,emp e
         where e.deptno(+) = d.deptno
        group by d.deptno,d.dname;
    自连接 要取的数据都在一个表中,记录不在同一行
        --查询员工信息:xxx的老板是 yyy  口诀: 员工表的老板 是 老板表的员工
        select e.ename||'''s boss is '||b.ename
          from emp e,emp b
         where e.mgr = b.empno;
        ===>上述结果没有大老板
        select e.ename||'''s boss is '||b.ename
          from emp e,emp b
         where e.mgr = b.empno(+);
         ===>
         select e.ename||'''s boss is '||nvl(b.ename,'himself')
          from emp e,emp b
         where e.mgr = b.empno(+);
    自连接的问题: 平方的增长数量
    查询 比 scott 工资高的员工信息     
    第一步:得到scott的工资
    select sal from emp where ename ='SCOTT'; ===> 3000
    第二步:工资大于3000的员工信息
    select * from emp where sal > 3000;    
    3 子查询 子查询的本质 sql嵌套sql
    --子查询入门 查询 工资比 scott工资高的员工信息
    第一步:得到scott的工资
    select sal from emp where ename ='SCOTT'; ===> 3000
    第二步:工资大于3000的员工信息
    select * from emp where sal > 3000;    
    ====>变成子查询
    select * from emp where sal > (select sal from emp where ename ='SCOTT');
    *****子查询10点注意事项
    1. 合理的书写风格 (如上例,当写一个较复杂的子查询的时候,要合理的添加换行、缩进 )
    2. 小括号( )
    3. 主查询和子查询可以是不同表,只要子查询返回的结果主查询可以使用即可
        --查询部门名称是“SALES”的员工信息
        --第一步 查询 SALES 对应的部门编号
        select deptno from dept where dname ='SALES';
        --第二步 通过部门编号 得到员工信息
        select * from emp where deptno =30;        
        ====>变成子查询
        select * from emp where deptno =(select deptno from dept where dname ='SALES');
    4. 可以在主查询的where、select、having、from后都可以放置子查询
        select ...
          from ...
         where ...
         group by ... err
         having ...
         order by ... err
        --select后 查询10号部门员工号,员工姓名,部门编号,部门名称
        select empno,ename,deptno,(select dname from dept where deptno=10)
          from emp
         where deptno = 10;
        --from后 查询员工的姓名、薪水和年薪:说明:该问题不用子查询也可以完成。但如果是一道填空题:select * from ___________________
        select * from (select ename,sal,sal*14 from emp); --from 后的是一个结果集
        --where后 查询与ward相同job并且薪水比他高的员工信息
        --先得到ward 的工种和薪水
        SQL> select job,sal from emp where ename ='WARD';
        JOB              SAL
        --------- ----------
        SALESMAN        1250    
        --得到那个人
        select * from emp where job ='SALESMAN' and sal > 1250;        
        ===>变成子查询
        select *
           from emp
          where job =(select job from emp where ename ='WARD')
            and sal > (select sal from emp where ename ='WARD');
        --having后  查询高于30号部门最低薪水的部门及其最低薪水
        --先得到30号部门的最低薪水
        select min(sal) from emp where deptno =30; ===>950
        --找到最低薪水高于950的部门
        select min(sal),deptno from emp group by deptno having min(sal) > 950;
        
        ===>变成子查询
        select min(sal),deptno from emp group by deptno having min(sal) > (select min(sal) from emp where deptno =30);
    5. 不可以在主查询的group by后面放置子查询 (SQL语句的语法规范)
    6. 强调:在from后面放置的子查询(***)        from后面放置是一个集合(表、查询结果)
    7. 单行子查询只能使用单行操作符;多行子查询只能使用多行操作符
        举例说明
        --多行操作符有 IN  ANY ALL
            --查询部门名称为SALES和ACCOUNTING的员工信息
            select deptno from dept where dname in ('SALES','ACCOUNTING');    
            select * from emp where deptno in (10,30); ===>子查询
        
            select * from emp where deptno in (select deptno from dept where dname in ('SALES','ACCOUNTING'));
            --查询薪水比30号部门任意一个员工高的员工信息 any 其中某一个高即可 ,比最低的那个高             
            select min(sal) from emp where deptno =30;            
            select * from emp where sal > (select min(sal) from emp where deptno =30) ;            
            ===>使用any
            select * from emp where sal >  ANY (select sal  from emp where deptno =30) ;            
            --查询薪水比30号所有人都高的员工  ALL
            select * from emp where sal >  ALL (select sal  from emp where deptno =30) ;             
    8. 子查询中的null值
        --查询不是老板的员工信息 没有小弟
        --找到是老板的
        select distinct mgr from emp ;
        select * from emp where empno in (select distinct mgr from emp);        
        --得到不是老板的
        select * from emp where empno not  in (select distinct mgr from emp);
        select * from emp where empno not  in (select distinct mgr from emp where mgr is not null );
    9. 一般先执行子查询(内查询),再执行主查询(外查询);但是相关子查询除外    
    10. 一般不在子查询中使用order by, 但在Top-N分析问题中,必须使用order by
4 集合运算
    引子:查询部门号是10和20的员工信息
    select * from emp where deptno = 10 or deptno = 20;====> 10号部门 和 20号部门的员工集合在一起
    --集合的三情况 并集(2个)、交集、差集
    并集 union 重复部分保留一份
    select * from emp where deptno in(10,20) union
    select * from emp where deptno in(20,30)
    并集 union  all 全保留
    select * from emp where deptno in(10,20) union all
    select * from emp where deptno in(20,30)    
    交集 intersect
    select * from emp where deptno in(10,20) intersect
    select * from emp where deptno in(30,20);    
    差集 minus 在前一个集合中去掉交集的部分
    select * from emp where deptno in(10,20) minus
    select * from emp where deptno in(30,20);    
    --集合运算需要注意的问题
        1.    参与运算的各个集合必须列数相同,且类型一致。
        2.    采用第一个集合的表头作为最终使用的表头。
        3.    可以使用括号()先执行后面的语句。
     select deptno,job,sum(sal) from emp group by deptno,job
     union
     select deptno,to_char(null),sum(sal) from emp group by deptno
     union
     select to_number(null),to_char(null),sum(sal) from emp;    
6 数据处理
    DML(data manipulation language) 数据处理语言  select insert update delete
    DDL(data definition language)  数据定义语言 create(table view synonym sequence ) drop
    DCL(data control language)     数据控制语言 grant
    --插入
        --语法:insert into tabname[()] values()|结果集(子查询)
        --可以插入全部列、部分列(隐式插入null)
        insert into dept values(51,'51name','51loc');
        insert into dept (deptno,dname) values(52,'52name');--隐式插入null
        insert into dept values(53,'53name',null);--显示插入 null
        --地址符
        SQL> insert into dept values(&dno,&dnm,&loc);
        输入 dno 的值:  54
        输入 dnm 的值:  '54name'
        输入 loc 的值:  '54loc'
        原值    1: insert into dept values(&dno,&dnm,&loc)
        新值    1: insert into dept values(54,'54name','54loc')    
        已创建 1 行。    
        SQL> /
        输入 dno 的值:  55
        输入 dnm 的值:  '55name'
        输入 loc 的值:  '55loc'
        原值    1: insert into dept values(&dno,&dnm,&loc)
        新值    1: insert into dept values(55,'55name','55loc')    
        SQL> insert into dept values(&dno,'&dnm','&loc')
          2  ;
        输入 dno 的值:  56
        输入 dnm 的值:  56name
        输入 loc 的值:  56loc
        原值    1: insert into dept values(&dno,'&dnm','&loc')
        新值    1: insert into dept values(56,'56name','56loc')
        SQL> select * from &ttt; -- 也可以在查询中替换
        输入 ttt 的值:  emp
        原值    1: select * from &ttt
        新值    1: select * from emp
        --批量插入 一次性插入10号部门的全部员工信息
        create table emp10 as select * from emp where 1=2;--建立一张与 emp 表结构相同的表 emp10
        insert into emp10 select * from emp where deptno = 10;--一次性插入10号部门的数据
            insert into dept select 57,'57name','57loc' from dual;--利用伪表生成集合 (配置一些参数有时使用)
    --更新
        --语法 update tabname set column1=val1,column2=val2,... where ....;--一般要加where条件
        update dept set loc='52loc' where deptno = 52;
        --更新的右值必须是单行结果 更新的语法同样适用子查询的各种规则
        update emp set deptno=(select deptno from dept ) where deptno=10;
        
        SQL> update emp set deptno=(select deptno from dept ) where deptno=10;
            update emp set deptno=(select deptno from dept ) where deptno=10                           *
            第 1 行出现错误:
            ORA-01427: 单行子查询返回多个行
    --删除
        --语法 delete from tablename where ...;
        delete from emp  where deptno=(select deptno from dept);--必须是单行返回值
        --结论:子查询的相关规则在DML语句中都适用
        --delete 与 truncate (截断 )
        set feedback off; --回显
         set timing off;
         @C:\sql.sql
         SQL> set feedback on;
        SQL> set timing on;
        SQL> delete from testdelete;    
        已删除5000行。
        已用时间:  00: 00: 00.03
        drop table testdelete purge;
        SQL> set feedback off;
        SQL> set timing off;
        SQL> @C:\sql.sql
        SQL> select count(*) from testdelete;
          COUNT(*)
        ----------
              5000
        SQL> set feedback on;
        SQL> set timing on;
        SQL> truncate table testdelete;
        表被截断。
        已用时间:  00: 00: 00.55
        --当前数据量级别, delete更快(oracle做了优化). 数据量较大的时候,通常使用 truncate.
            delete 和 truncate的区别
            delete 逐条删除表“内容”,truncate 先摧毁表再重建
            delete 是DML语句,truncate 是DDL语句,DML语句支持回退(rollback).
            由于delete是逐条操作数据,所以delete会产生碎片,truncate不会产生碎片.
            delete不会释放空间,truncate 会释放空间
            delete可以回滚rollback,    truncate不可以回滚rollback
    --事务(讲义) oracle 特性 : 要么都成功,要么都失败.
    事务4大特性(ACID) :原子性、一致性、隔离性、持久性。
    事务的结束标志:提交、回滚都是事务的结束标志。
    提交:    显示提交:commit
            隐式提交:1. 有DDL语句,如:create table除了创建表之外还会隐式提交Create之前所有
                        没有提交的DML语句。 2. 正常退出(exit / quit)
    回滚:    显示回滚:rollback
            隐式回滚:掉电、宕机、非正常退出。
    --保存点
    --隔离级别
    SQL99定义4中隔离级别:    1.     Read Uncommitted    读未提交数据。
                            2.    Read Commited        读已提交数据。        (Oracle默认)
                            3.    Repeatable Read        可重复读。            (MySQL默认)
    这4种MySQL都支持        4.    Serializable        序列化、串行化。 (查询也要等前一个事务结束)
    
    oracle 支持上述的4种的2 和 4.  自定义级别 read-only
redhat口令
    root 123456
    itcast a
    oracle oracle
192.168.137.111
 [itcast@localhost ~]$ su - oracle
--登陆管理员
[oracle@localhost ~]$ sqlplus / as sysdba
--启动实例
SQL> startup
--启动侦听
[oracle@localhost ~]$ lsnrctl start
LSNRCTL for Linux: Version 11.2.0.1.0 - Production on 18-NOV-2016 17:30:52
Copyright (c) 1991, 2009, Oracle.  All rights reserved.
Starting /home/oracle_11/app/oracle/product/11.2.0/db_1/bin/tnslsnr: please wait...
TNSLSNR for Linux: Version 11.2.0.1.0 - Production
System parameter file is /home/oracle_11/app/oracle/product/11.2.0/db_1/network/admin/listener.ora
Log messages written to /home/oracle_11/app/diag/tnslsnr/localhost/listener/alert/log.xml
Listening on: (DESCRIPTION=(ADDRESS=(PROTOCOL=ipc)(KEY=EXTPROC1521)))
Listening on: (DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=localhost)(PORT=1521)))
Connecting to (DESCRIPTION=(ADDRESS=(PROTOCOL=IPC)(KEY=EXTPROC1521)))
STATUS of the LISTENER
------------------------
Alias                     LISTENER
Version                   TNSLSNR for Linux: Version 11.2.0.1.0 - Production
Start Date                18-NOV-2016 17:30:54
Uptime                    0 days 0 hr. 0 min. 0 sec
Trace Level               off
Security                  ON: Local OS Authentication
SNMP                      OFF
Listener Parameter File   /home/oracle_11/app/oracle/product/11.2.0/db_1/network/admin/listener.ora
Listener Log File         /home/oracle_11/app/diag/tnslsnr/localhost/listener/alert/log.xml
Listening Endpoints Summary...
(DESCRIPTION=(ADDRESS=(PROTOCOL=ipc)(KEY=EXTPROC1521)))
(DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=localhost)(PORT=1521)))
The listener supports no services
The command completed successfully
--登陆redhat oracle 数据库
sqlplus scott/11@//192.168.137.111/orcl
--在root下关闭防火墙
[root@localhost oracle]# service iptables status
[root@localhost oracle]# service iptables stop
iptables:清除防火墙规则:                                 [确定]
iptables:将链设置为政策 ACCEPT:nat mangle filter         [确定]
iptables:正在卸载模块:                                   [确定]
--登陆mysql  
[itcast@localhost ~]$ mysql -uroot -p123
redhat口令
    root 123456
    itcast a
    oracle oracle
192.168.137.111
[itcast@localhost ~]$ su - oracle
--登陆管理员
[oracle@localhost ~]$ sqlplus / as sysdba
--启动实例
SQL> startup
--启动侦听
[oracle@localhost ~]$ lsnrctl start
LSNRCTL for Linux: Version 11.2.0.1.0 - Production on 18-NOV-2016 17:30:52
Copyright (c) 1991, 2009, Oracle.  All rights reserved.
Starting /home/oracle_11/app/oracle/product/11.2.0/db_1/bin/tnslsnr: please wait...
TNSLSNR for Linux: Version 11.2.0.1.0 - Production
System parameter file is /home/oracle_11/app/oracle/product/11.2.0/db_1/network/admin/listener.ora
Log messages written to /home/oracle_11/app/diag/tnslsnr/localhost/listener/alert/log.xml
Listening on: (DESCRIPTION=(ADDRESS=(PROTOCOL=ipc)(KEY=EXTPROC1521)))
Listening on: (DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=localhost)(PORT=1521)))
Connecting to (DESCRIPTION=(ADDRESS=(PROTOCOL=IPC)(KEY=EXTPROC1521)))
STATUS of the LISTENER
------------------------
Alias                     LISTENER
Version                   TNSLSNR for Linux: Version 11.2.0.1.0 - Production
Start Date                18-NOV-2016 17:30:54
Uptime                    0 days 0 hr. 0 min. 0 sec
Trace Level               off
Security                  ON: Local OS Authentication
SNMP                      OFF
Listener Parameter File   /home/oracle_11/app/oracle/product/11.2.0/db_1/network/admin/listener.ora
Listener Log File         /home/oracle_11/app/diag/tnslsnr/localhost/listener/alert/log.xml
Listening Endpoints Summary...
(DESCRIPTION=(ADDRESS=(PROTOCOL=ipc)(KEY=EXTPROC1521)))
(DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=localhost)(PORT=1521)))
The listener supports no services
The command completed successfully
--登陆redhat oracle 数据库
sqlplus scott/11@//192.168.137.111/orcl

--在root下关闭防火墙
[root@localhost oracle]# service iptables status
[root@localhost oracle]# service iptables stop
iptables:清除防火墙规则:                                 [确定]
iptables:将链设置为政策 ACCEPT:nat mangle filter         [确定]
iptables:正在卸载模块:                                   [确定]
--登陆mysql  
1 约束(主键字段特殊,check语法保留,但是无效)auto_increment --自增 timestamp 时间戳
        drop table myclass;
        create table myclass(
                                class_id int primary key auto_increment,
                                class_name varchar(20) not null,
                                create_date timestamp);
        
        insert into myclass(class_name) values('json');
        insert into myclass(class_name) values('jackson');
        insert into myclass(class_id,class_name) values(5,'jacksonson');
        insert into myclass(class_name) values('jacktom');
        create table mystudent(
                                student_id int primary key auto_increment,
                                student_name varchar(20) not null,
                                hiredate timestamp,
                                class_id int,
                                constraint stu_classid_FK foreign key(class_id) references myclass(class_id));
        insert into mystudent(student_name,class_id) values('xiaoming',1);
        mysql> insert into mystudent(student_name,class_id) values('xiaoming2',8);
        ERROR 1452 (23000): Cannot add or update a child row: a foreign key constraint fails (`scott`.`mystudent`, CONSTRAINT `stu_classid_FK` FOREIGN KEY

(`class_id`) REFERENCES `myclass` (`class_id`))
        mysql>
        mysql> insert into mystudent(student_name,class_id) values(null,1);
        ERROR 1048 (23000): Column 'student_name' cannot be null
        
        mysql>
        mysql> delete from myclass where class_id=1;
        ERROR 1451 (23000): Cannot delete or update a parent row: a foreign key constraint fails (`scott`.`mystudent`, CONSTRAINT `stu_classid_FK` FOREIGN KEY

(`class_id`) REFERENCES `myclass` (`class_id`))
        t_rate_group t_rate_group_member
        group_id     group_id ,member (手机号)        
        共享内存 5g 计费资料   
LANG="zh_CN.UTF-8" 菜单显示按照简体中文,数据存储按照utf8             
mysql api 编程 c 连接mysql  编写客户端
第一小步 : 连接上 mysql
locate 查看文件所在位置 比find快速,但是不及 find 实时性好.
mysql.h libmysqlclient.a
    mysql_init() 初始化
    mysql_real_connect() 连接
    mysql_close()  关闭连接
第二小步: 查询数据
    int mysql_query(MYSQL *mysql, const char *query)  查询(实际包含各种操作指令的功能)
    mysql_store_result  取回结果集
        mysql_fetch_row 取回行
    mysql_free_result  释放结果集
第三步;编写客户端
    create table biancheng(id int,name varchar(30));
    insert into biancheng values(1,'叶开');
    insert into biancheng values(2,'傅红雪');
打桩 函数
top-N 问题 mysql 解决
    --取前三名
    select * from emp order by sal desc limit 3;
        --取5-8名
        select * from emp order by sal desc limit 4,4;
为什么学mongodb呢?
    一,丰富我们的知识面,因为它是非关系型数据库(NOSQL = not only sql)
    二,mongodb是非关系型数据库中最像关系型的.
    三,排名
    四,跨平台
    五,特点:高性能 易部署 易使用
我们学mongodb学哪些内容?
    体系结构 数据库- 集合(表) mysql root用户 - 数据库 scott -- 表
    数据库的增删改查操作
    集合的增删改查操作
    数据的增删改查操作
    api编程连接mongodb 实现增删改查.
mongodb简介
    10gen 公司 2007年 推出 mongodb
    MongoDB是一个开源的,基于分布式的,面向文档存储的非关系型数据库  C++编写 起源于 humongous.
安装
    数据库的安装
        mongod 数据库运行程序
        mongo  客户端运行程序
        27017 默认端口 1521 oracle 3306 mysql
        netstat -an|grep 27017  --检测端口是否侦听
    驱动的安装(C++) api接口
        1. 安装 boost 库 准标准库
        2.安装PCRE 正则表达式的库
        3.安装scons mongodb 驱动的编译指令
        4.编译驱动程序
            SConstruct 文件必须有    
        /home/itcast/mongoapi/lib    
        g++ -o testmongo testmongo.cpp -lmongoclient -lboost_thread -lboost_filesystem -lboost_program_options -L/home/itcast/driver/boost/lib -

L/home/itcast/driver/mongo/lib -I/home/itcast/driver/mongo/include -I/home/itcast/driver/boost/include        
    概念
    文档 json格式的键值对  {id:1,name:'zhangsan'}  对应关系型数据的行
    集合 多个文档组成集合   对应关系型数据库  表
    关系型和mongodb的比对(库,表,行,列,索引,关联,主键)
    关系型数据库   mongodb     
    数据库         数据库
    表             集合
    行             文档
    索引           索引
    关联           内嵌文档(内嵌对象)
    主键           _id
    关系型数据库和非关系型数据库的区别
        1. 表与表之间的关系
        2. 行和列有固定的限制和要求     
    分布式数据库系统    
    常见的一些数据类型         
    库的操作
    登陆
    [itcast@localhost lib]$ mongo
        MongoDB shell version: 2.6.10
        connecting to: test  -- test 是默认连接到的数据库
    mongo localhost:27017/local  ===> mongo [ip][:port][/dbname]
    退出
    exit
    查看库
    show dbs
    建库(..)
    use yekai --如果没有yekai则创建数据库,如果有了,就是切换数据库
    切换库(选择库)
    use yekai
    显示当前库名
    >db
    > db.getName()
    yekai
    > show dbs
    admin     (empty)
    local     0.078GB
    tutorial  0.078GB
    yekai     (empty)
    显示库下的集合名称
    > show collections
    > show tables
    删库(..)
        分两步 1. 选择库
                use yekai
               2. db.dropDatabase()
    mongodb的客户端 相当于是一个简易shell ,有部分提示功能.
    
查看文档
    语法:db.COLLECTION_NAME.find(COND_JSON,SHOW_JOSN)
            查看巴萨队内全部球员 DBQuery.shellBatchSize 默认显示20条记录 DBQuery.shellBatchSize=30
            db.Barca.find()
            db.Barca.find({})
            查看梅西的详细信息 select * from Barca where name ='messi';
            db.Barca.find( { name:'messi' } )
            查看年龄为29,并且是踢中后卫(pos为CB)的球员 select * from Barca where age=29 and pos='CB';
            db.Barca.find( {  age:29,pos:'CB' } )
            查看年龄大于25,并且是中场的球员 pos 为 MF  
            gt greater than
            lt less  than
            lte less equal than
            gte greater equal than
            ne  not equal   
            db.Barca.find( { age:{$gt:25}, pos:'MF' } )
            上述查询只返回一条记录,findOne到一个就返回
            > db.Barca.findOne( { age:{$gt:25}, pos:'MF' } )
            {
                    "_id" : ObjectId("5833b02a8fb45659c015519f"),
                    "hiredate" : "2014-7-11",
                    "name" : "rakitic",
                    "number" : 4,
                    "age" : 28,
                    "pos" : "MF",
                    "height" : 182,
                    "weight" : 72
            }
            显示球员姓名,年龄,位置     显示部分列
            db.Barca.find( {},{name:1,age:1,pos:1,_id:0}  )    
            显示球员的不同年龄 distinct
            db.Barca.distinct('age')  --只能处理一列
            
修改文档
    修改文档 语法
    db.collection.update(
   <query>,
   <update>,
   {
     upsert: <boolean>,
     multi: <boolean>,
     writeConcern: <document>
   }
)
        将 vhalirvic 的位置修改为中场 pos= MF  update barca set pos='MF' where name='vhalirvic';
        db.Barca.update( {name:'vhalirvic'},{$set:{pos:'MF'}} )
        将 vhalirvic 的位置修改为null
        db.Barca.update( {name:'vhalirvic'},{$set:{pos:null}} )
        将 vhalirvic 的年龄改为25岁,位置改成SB(边后卫)--改2列
        db.Barca.update( {name:'vhalirvic'},{$set:{pos:'SB',age:25}} )
        将年龄为24的球员年龄全部修改为 25
        db.Barca.find( {age:24})
        db.Barca.update( {age:24},{$set:{age:25}})
        db.Barca.update( {age:24},{$set:{age:25}},{upsert:false,multi:true} )
        
        --下面这种更新方式是直接覆盖 save
        db.Barca.update( {name:'vhalirvic'},{pos:null} )

删除文档    
    语法:db.COLLECTION_NAME.remove(<query>,{justOne:<boolean>,writeConcern:<document>})
    删除球员位置为null的球员信息 --justOne 默认情况下是false
    db.Barca.find({pos:null})
    db.Barca.remove({pos:null})
    删除集合
    db.Barca.drop()
    集合是什么时候创建的?第一次insert的时候.

索引 提高查询效率
    索引作用,mongodb的索引 是 btree
    如何创建索引
    db.biancheng.ensureIndex({name:1})
    查看索引信息
    db.system.indexes.find()
    执行计划
    db.biancheng.find({name:'yekai'})  --用上索引
    db.biancheng.find({id:1}).explain() -- 没用上索引
            “cursor”:“BasicCursor”表示本次查询没有使用索引;“BtreeCursor  name_1 ”表示使用了name上的索引;
            “isMultikey”表示是否使用了多键索引;
            “n”:本次查询返回的文档数量;
            “nscannedObjects”:表示按照索引指针去磁盘上实际查找实际文档的次数;
            ”nscanned“:如果没有索引,这个数字就是查找过的索引条目数量;
            “scanAndOrder”:是否对结果集进行了排序;
            “indexOnly”:是否利用索引就能完成查询; db.Barca.find( {name:'neymar'},{_id:0,name:1} ).explain()
            “nYields”:如果在查询的过程中有写操作,查询就会暂停;这个字段代表在查询中因写操作而暂停的次数;
            “ millis”:本次查询花费的毫秒数,数字越小说明查询的效率越高;
            “indexBounds”:这个字段描述索引的使用情况,给出索引遍历的范围。
            "filterSet" : 是否使用和索引过滤;

聚合函数 aggregate max min avg sum
    显示集合的记录总数
    db.Barca.find().count()
    --求各个位置的最小年龄 pos minage  aggregate 相当于得到一个新的集合
    db.Barca.aggregate( { $group:{_id:"$pos",minage:{$min:"$age"}} } )
    --求各个位置的最大年龄
    db.Barca.aggregate( { $group:{_id:"$pos",maxage:{$max:"$age"}} } )
    --求各个位置的平均年龄
    db.Barca.aggregate( { $group:{_id:"$pos",avgage:{$avg:"$age"}} } )
    --求各个位置的年龄和
    db.Barca.aggregate( { $group:{_id:"$pos",sumage:{$sum:"$age"}} } )
    --统计各个位置的人数 select count(*),pos from Barca group by pos;
    db.Barca.aggregate( { $group:{_id:"$pos",count:{$sum:1}} } )
    --统计不同位置不同年龄的人数  group by pos,age
    db.Barca.aggregate( { $group:{_id:{pos:"$pos",age:"$age"},count:{$sum:1}} } )

9)    安全传输平台项目
简介
        安全传输平台 : 解决点和点之间的数据传送的安全    
项目的需求        
    和网点相关的需求
            两个点之间的安全
            点和点之间是1:N关系
            总部点 有可能是集群 ()
            点可以向上级联 向下级联        
    和密钥相关的需求        
            异地的密钥要一样(协商密钥)
            总部能控制分部的密钥协商
            密钥更新    
                    手工更新密钥
                    自动更新密钥
                    在总部一键更新所有的密钥            
    和网点相关的需求    
        总部需要控制各个分行(如何控制)
        需要编号
        总部需要管理分行的各个节点(增删改查)
        密钥恢复需求
    其他需求
            总体性能不能损耗太多 (10% --- 15%)    
            密钥更新时 业务的平滑过渡
            管理类
            审计类  
            日志类            
项目的阶段
            1 项目的可行性分析  老大要抛砖    
            2 项目启动         组建团队 项目经理 核心人员
            3 需求分析        项目启动会
            4 概要设计    要把重要的业务流设计出来 表  重要的业务流伪代码     核心业务                
            5 详细设计        具体业务流
            5-1编码
            6 单元测试        自测/局部的对接测
            7 集成测试 (压力测试性能测试)    所有模块
            8 交付
            9 上线试运行                        
            10 上线                                        
            11 上线支持                        

需求转化成方案
            1 app1 app2要改动最小,最好是不改动
            2 保证两个点之间的安全
            对称加密体系     需要解决对称密钥的安全分发    
            3 如何保证系统的高性能
            4 如何保证网点管理
    方案的强化
                注意1:安全传输平台没有托管第三方信息系统的消息的(报文的)发送和接受
                            app1和app2 原理是怎么发送的,还怎么发送  
                注意2: app1信息系统如何区分数据是来自客户端1 还是  客户端2
            密文:xxxxxx
            +客户端的身份信息  clientid serverid xxxxxx
             外联接口是如何找到两个网点之间的密钥        
            服务器端:要存储所有的客户端的网点密钥
                                
                                clientid     serverid     xxxxxxxx  time desc keystatus
                                1111        0001        xxxxx1    
                                2222        0001        xxxxx2    
                                3333        0001        xxxxx2        
报文需要单独的处理
            以密钥协商为例: client发送密钥协商请求
            clientid serverid time r1 desc authcode(密码)
            //密钥请求报文
            typedef struct _MsgKey_Req
            {
                //1 密钥更新      //2 密钥校验;     //3 密钥注销
                int                cmdType;        //报文命令码
                char            clientId[12];    //客户端编号
                char            AuthCode[16];    //认证码
                char            serverId[12];    //服务器端I编号
                char            r1[64];        //客户端随机数
            }MsgKey_Req;
            //密钥应答报文
            #define  ID_MsgKey_Res  61
            typedef struct  _MsgKey_Res
            {
                int                    rv;                //返回值
                char                clientId[12];    //客户端编号
                char                serverId[12];    //服务器编号
                unsigned char        r2[64];            //服务器端随机数
                int                    seckeyid;        //对称密钥编号 //modfy 2015.07.20
            }MsgKey_Res;
配置管理终端
        1 完成网点的管理(增删改查)
        2 完成后台服务器keymngserver的服务参数的配置(ip port 支持的最大的网点个数 自己serverdid servername )
                读数据库的配置信息
                请问 连接数据库的配置信息应该放在什么地方(soctt/11@orcl)
        结论: keymngsever启动的时候
        数据库的连接信息(soctt/11@orcl)写在配置文件中
        其他所有的配置信息, 都需要从数据库中获取        
演示数据库系统
        分析 设计什么表/字段
            1 配置信息表
                    存储服务器ip端口/最大网点个数
                    服务器自己的id/名称/创建时间/描述信息
                    key          value
                    "ip"        192.168.11.22
                    "port"            8081
            2 网点信息表
            3 密钥信息表
oracle数据库解决方案 是基于用户的数据库解决方案
            设计一个用户 SECMNG  /    设计表
            设计一个表空间 ,在表空间之下存放表
mysql数据库是基于数据库的解决方案
            设计一个数据库 SECMNG  /设计表
1 实施安全传输平台数据库解决方案
    在oracle用户下  使用sys用户
    sqlplus /nolog
    connect /as sysdba
    sql> 执行脚本
    2 实施密钥协商服务器和密钥协商客户端    
        规定:     1 不要在root用户下部署     keymngserver和keymngcient
                2 不要在oralce用户下部署     keymngserver和keymngcient
        1 创建一个用户 修改密码
                    [it01@localhost home]$ su -
                    密码:
                    [root@localhost ~]#
                    [root@localhost ~]#
                    [root@localhost ~]# useradd test11
                    [root@localhost ~]# passwd test11
                    更改用户 test11 的密码 。    
        2 keymngserver和keymngclient tar包 传到linux用户test11之下    
        3 解包
        [test11@localhost ~]$ tar zxvf keymng_linux_bin.tar.gz     
        4 运行keymngsever
3 linux服务器上oracle数据库的启动和关闭
                1规定:     要用oracle用户启动和关闭oracle数据库
                2 关闭数据库
                    步骤1 关闭oralce后台进程组
                            /home/oracle
                            [oracle@localhost ~]$ sqlplus /nolog
                            SQL> connect /as sysdba
                            SQL> shutdown immediate
                    步骤2  关闭监听端口1521
                            [oracle@localhost ~]$ lsnrctl stop
                3     启动oracle数据库
                        启动后台进程组
                        [oracle@localhost ~]$ sqlplus /nolog
                        SQL> connect /as sysdba
                        SQL> startup
                                    Total System Global Area  780824576 bytes
                                    Fixed Size                  2217424 bytes
                                    Variable Size             608176688 bytes
                                    Database Buffers          167772160 bytes
                                    Redo Buffers                2658304 bytes
                                    Database mounted.
                                    Database opened.
                        启动  监听服务            
                        /home/oracle
                        [oracle@localhost ~]$ lsnrctl start
                                    
                                    
oracle数据库使用常见问题
        //win环境连接oracle服务器
        1)    oracle所在的虚拟机 固定ip
        2)     oracle用户启动oracle服务器
        3)    在linux环境中,test07用户 用sqlplus工具访问oracle服务
                sqlplus SECMNG/SECMNG@orcl
                sqlplus    scott/11@orcl
                     1 ) 说明数据库环境跑通了
                      2)就可以启动keymngserver
                    3) root用户  chmod 777 /home/oracle_11/app/ -R
                                            
        4) win连接linux下的服务器
                ping
                防火墙                                
1 报文编码解码流程
2 win dll
3 linux so
报文的重要性
        是项目入门的关键   
常见的报文类型
    html  json xml   der 自定义
超文本传输协议http 和 html
            http 一种机制   
            html 数据交换的一种格式
ASN.1抽象语法标记和DER    
    ASN.1有数据类型的概念
        ASN.1抽象语法标记          是一种数据交换格式  有数据类的概念   完成数据类型的定义
    DER(唯一的编码和解码方法 ) 把ASN.1定义的变量 进行TLV打包的方法 叫DER ====> 唯一性问题 快平台
    

把win环境下的动态库 测试程序 移植到 linux环境下
        1) 动态库的移植
        ---查看linux 动态库的api函数            
                    [it01@localhost mymsgreal]$ nm libmymessagereal.so | grep Msg*
                    000000000000560f T MsgDecode
                    00000000000053f2 T MsgEncode
                    00000000000058a6 T MsgMemFree
        2)
            tar zcvf mymsgreal.tar.gz mymsgreal mymsgrealtest/
            tar tvf mymsgreal.tar.gz     --查看tar包的内容
gcc 和makefile 复习
        gcc [-c|-S|-E] [-std=standard]
           [-g] [-pg] [-Olevel]
           [-Wwarn...] [-pedantic]
           [-Idir...] [-Ldir...]
           [-Dmacro[=defn]...] [-Umacro]
           [-foption...] [-mmachine-option...]
           [-o outfile] [@file] infile...
//1.c ====> 1.exe
        gcc 1.c -o 1.exe
// 1.exe  有n个1.c 2.c 3.c;  有n头文件目录  有n个动态库 libmymsgreal1.so libmymsgreal2.so  libmymsgreal3.so        
        gcc -o 1.exe      -I/home/it01/incl1 -I/home/it01/incl2  1.c 2.c 3.c -L/home/it01/lib     -lmymsgreal1      -lmymsgreal2  -lmymsgreal3           
// 1.exe  有n个1.c 2.c 3.c;  有n头文件目录  有n个动态库 libmymsgreal1.so libmymsgreal2.so  libmymsgreal3.so        
        gcc -o 1.exe      -I/home/it01/incl1 -I/home/it01/incl2  /home/it01/src1/*.c  /home/it01/src2/*.c 3.c -L/home/it01/lib     -lmymsgreal1      -lmymsgreal2  

-lmymsgreal3  
make :  要站在make工程的角度 去思考makefile文件  
make有一个最基本的文件组织的原理


1 在项目开发中 socket开发需要注意的点
//在客户端搭建一条api函数  客户端连接池模型
            //连接池初始化  建立连接池环境
            //handle 内存首地址
            int clientpool_init(char *ip, int port, int maxnum, int conntime, void **handle);
            int clientpool_get(void *handle, int *fd);
            //利用连接发报文
            int clientpool_send(void *handle, int fd, unsigned char *indata, int indatalen);
            
            int clientpool_rev(void *handle, int fd, unsigned char **outdata, int *outdatalen);
            //把连接放回到连接池中
            int clientpool_put(void *handle, int fd);
            //释放连接池环境
            int clientpool_destory(void *handle);
            统一报文通讯思想
客户端socket连接池api函数
int socketpool_init(char *serverip, int serverport, int connnum, int time, void *handle);
int socketpool_getConn(void *handle, int *sckfd);
int socketpool_putConn(void *handle, int sckfd);
int socketpool_send(void *handle, int sckfd, unsigned char *data, int datalen);
int socketpool_recv(void *handle, int sckfd, unsigned char **outdata, int *outdatalen);
int socket_free(void *data);
线程个数
线程圈数
                        
密钥协商客户端KeyMngClient   
密钥协商服务器KeyMngServer  
1 总体业务流程
2 密钥协商客户端KeyMngClient
3 密钥协商服务器端KeyMngServer            
                        
共享内存业务流程集成
        初始化共享内存
        读网点的共享内存
        写网点的共享内存
    
应用程序的优雅退出
        信号处理
        信号接收
        信号发送  

项目中数据库操作常用点

01背景
        informix          
        db2            c sql proc            
        oracle        jdbc  java  win/linux 96----2004
                    win ----- oracle
                    ------ sqlsever     odbc
                        mysql
                mgdb  
02 所有的数据库操作都是C/S
    [it01@localhost lib]$ cd $ORACLE_HOME/lib libclntsh.so
03 数据库的增删改查
            1)用oracle用户启动和关闭oracle数据库    
                关闭后台服务 和 监听服务     
                [oracle@localhost ~]$ sqlplus /nolog
                SQL> connect /as sysdba
                SQL> shutdown immediate
                [oracle@localhost ~]$ lsnrctl stop  //在家目录下  不要在sqlplus环境中
                启动oracle后台进程组服务  和 启动监服务
                SQL> startup
                SQL> connect /as sysdba
                SQL> startup
                [oracle@localhost ~]$ lsnrctl start
            2) 在it01用户下 scott/11@orcl用户         
            3) 事物       定义:是一个或者是多个sql语句的组合 要么都成功要么都失败
                        特性: 隔离性
                        oracle事物
                        默认是自动打开, 但是不自动提交
              sql1 sql2 这2个sql语句在同一个事务里面
                            事物的提交的话题: commit;
                                显示提交  隐身提交
                                        sql3 sql4
                                        
                        mysql事物区别
                                    默认自动提交
                                    sql1 回车后 事物提交
            sql2  在开sql2 , sql1和sq2 不再同一个事物中
3) 统一数据库访问组件api编程        
    sql语句是分类
        根据返回结果 进行分类
            建表 修改表
            增删改查
    
    游标:    
如何打造sql语句
    //组织sql语句
1)    sprintf(mysql, "Insert Into SECMNG.SECKYEINFO(clientid, serverid, keyid, createtime, state, seckey) \
                    values ('%s', '%s', %d, '%s', %d, '%s') ", pNodeInfo->clientId,  pNodeInfo->serverId, \
                    pNodeInfo->seckeyid, optime, 0, tmpseckey2 );                
2)    为什么密钥要进行转换
aabbccdd...... ......发放的撒范德萨范德萨地方撒天天

        varchar1
        varchar2        
            zhangsan@oracle.com
            zhangsan2@oracle.com
        varchar2数据类型  4096  blob 不能存储二进制码 (只能存储C风格的字符串)
3)     010101010111111 ------->     
    //每一个bit 位  按照8个字节存储   ========> 8倍
    <0 101 0101   00011, 1111 > =======>  '0' '1'  '0' '1 ' .....'0''1''0' '1.
4) base64编码
        3*8 = 4 * 6
        0101 0101   0011 1111  0101 0101   0011 1111
static char base64_table[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
    

密钥注销流程
    客户端        
            1 组织请求报文
            2 发送请求
            4 接受服务器端的应答 解析rv
            5  读网点密钥 再写信息  statrt  //status=1    
    服务器        
            1 接受请求报文
            2 处理业务
                1) 修改共享内存读网点密钥 再写信息  statrt  //status=1
                2) 修改数据库 keysn
                应答报文 MsgKey_Res rv = 0;    
                //
                
            3 回发密钥注销应答报文应答rv
        
        
1 基础组件 (指针/接口封装和设计)
2 linux 密钥协商客户端 和服务器 开发/调试/对接 shm db
3  win环境下 SecAdmin管理终端
   win         密钥协商客户端(win 报文编码解码 发送报文 写共享内存)
====>前台界面开发/前台业务开发
SecAdmin管理终端     
总体作用:为后台服务器做配置管理  实现网点信息的管理
    系统初始化
        录入的配置信息写配置文件 下一次启动的时候直接读配置文件
    参数配置管理
        服务器启动参数
        根网点的配置信息    
    网点生命周期管理
        查询    模糊查询/精确查询
        网点的增删改查
    后台交易信息
        系统的交易日志 查询出来
        打印报表        
错误1 中文语言包的问题
e:\01_work\23_项目\myadmin\myadmin\gfxoutbarctrl.cpp(179):
error C2664: “LPWSTR lstrcpyW(LPWSTR,LPCWSTR)”: 无法将参数 1 从“char *”转换为“LPWSTR”        
错误2:修改获取版本api函数
MFC管理资源的理念
    物理资源         ====  c++ 类 来控制物理资
 

备注    



Logo

更多推荐