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
//导入头文件 (/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、预处理:去掉程序中所有注释 头文件展开 常量的值直接写在程序代码中 (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
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
count++;
printf("nin")
}
#include
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
QString fileName= QFileDialog::getOpenFileName();
//cout <<"file name = "< //更改编码格式 Qt 默认的中文编码格式是utf-8 需要转成GBK 需要头文件 #include
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 {
for(int j=0;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 一轮以后,有一个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::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. 信号量 -- 头文件
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(
,
,
{
upsert: ,
multi: ,
writeConcern:
}
)
将 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(,{justOne:,writeConcern:})
删除球员位置为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
[email protected]
[email protected]
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++ 类 来控制物理资
备注