Linux基础总结
Linux基础部分总结
-
基础命令部分
-
文件管理命令
(1)mkdir: 创建新目录
(2)rmdir: 删除空目录
(3)mv: 剪切,更名
(4)rm: 删除文件
(5)touch: 创建空文件
(6)cat: 显示文件内容
(7)less: 分页显示文件内容
(8)more:分屏显示文件内容;
(9)nl:可以显示文件内容和行号;
(10)locate:查找包含指定字符串的文件或目录(locate + 字符串);
(11)find: 文件搜索(#find / -size [+ -]2048)
(12)head:显示文件前几行,-n指定行数
(13)tail: 显示文件后几行
(14)grep: 在文件中搜索字符串匹配的行,并输出;
(15)whereis: 搜索命令所在目录及帮助文档路径;
2.权限管理命令
(1)chmod: 改变文件或目录文件,-R 递归修改(chmod[{ugoa}{+ -}{rwx}][ 文件或目录] 或 [mode = 421][文件或目录])
(2)chown: 改变文件或目录的所有者
(3)chgrp: 改变文件或目录的所属组
(4)umask: 显示,设置文件的缺省权限
-
系统管理命令
(1)adduser: 增加用户
(2)passwd: 修改密码
(18)who/w: 查看登陆用户/详细信息;
(4)uptime:运行时间
(5)free: 查看内存使用情况
(6)last: 查看最后登陆系统的用户
(7)mpstat:实时监测多处理器系统上的每个CPU使用情况
(8)vmstat:实时输出系统各个资源的使用情况
(9)help: 获得shell内置命令的帮助信息
(10)id: 显示用户的id,以及所属组的id
(11)logout: 退出系统
-
进程命令
(1)ps: 报告程序状况
(2)pstree: 以树状图显示进程
(3)kill: 删除执行中的程序或工作
(3)tcpdump:用于过滤数据包或制定打开格式;
(4)lsof:列出当前系统打开的文件描述符;
(5)ipcs:查看信号量;
(6)ipcsm:查看已经销毁的信号量;
-
网络通讯
(1)ping: 测试网络连通性
(2)telnet:远端登入
(3)ifconfig:显示或设置网络设备
(4)netstat: 显示网络相关信息
(5)topdump:可列出经过指定网络界面的数据包头
(6)traceroute:显示数据包到主机间的路径
(7)write:给用户发信息
-
wall:发广播信息
-
mail: 查看,发送电子邮件
-
setup: 管理员权限下,配置网络
6.关机/重启命令
-
poweroff:立即关机;
-
reboot:立即重启;
-
halt: 立即关机
-
shutdown: 立即关机
-
init 0: 立即
-
init 6:
7.文件压缩解压命令
(1)gzip: 压缩文件->.gz文件,只压缩,不保留;
(2)gunzip: 解压缩.gz压缩文件;
(3)tar: 打包,压缩->.tar.gz 文件;
(4)zip: 压缩文件或目录命令;
(5)unzip: 解压.zip的压缩文件;
(6)bzip2:(-k)产生压缩文件并保留源文件;tar解压;
8.gdb调试命令
(1)调试多进程
1.1 单独调试子进程:(gdb)attach 子进程pid;
1.2 使用调试器选项 set follow-fork-mode:
gdb set follow-fork-mode mode(*mode 可以选择为parent 或者 child)
(2)调试多线程
a.利用info threads查看线程信息;
b.thread id 调试目标id指定的线程;
c.set scheduler-locking off | on | step;
*"off"表示不锁定任何线程;
"on"只有当前被调试的线程继续运行;
"step"在单步执行的时候,只有当前线程会执行;
(3)普通调试
-l:显示代码;
-r:运行;
-p:打印;
-s:进入到函数;
-b:加上断点;
-bt:查看函数执行到那块;
-n:单步执行;
-c:程序继续向下一个执行
-
-
Makefile文件
主要包括五方面内容:显示规则,隐晦规则,变量定义,文件指示和注释;
(1)功能:关系到整个工程的编译规则,也可执行操作系统的命令,(好处)实现自动化编译整个工程;
(2)简单的编写步骤:
现在有5个文件,分别为:main.c my1.h my2.h my1.c my2.c ;
现在编写makefile文件,名称为makefile.过程如下:
a. main: main.o my1.o my2.o
gcc –o main main.o my1.o my2.o
main.o: main.c my1.h my2.h
gcc –c main.c
my1.o: my1.c my1.h
gcc –c my1.c
my2.o: my2.c my2.h
gcc –c my2.c
clean:
rm –f *.o main
b.执行命令:make 就可以生成可执行文件main. 如果要删除可执行文件以及所有的中间目标文件,只需要执行下 make clean .
**clean不是一个文件,它只是一个动作的名字,如C语言中的lable,冒号后什么都不做;
(3)文件说明
在上面的makefile文件当中,目标文件包括:执行文件main 和中间目标文件(*.o),依赖文件(就是冒号后面的那些文件)。每一个 .o 文件都有一组依赖文件,而这些 .o 文件又是执行文件main的依赖文件。依赖关系的实质上就是说明了目标文件是由那些文件生成的。
-
make的工作原理
Make 是解释Make File中指令的命令工具。
当我们输入make时:
-
make首先会在当前目录下寻找makefile文件。
-
如果找到,它会找到文件中的第一个目标文件main,并把这个文件作为最终的目标文件。
-
如果main文件不存在或者是main所依赖的后面的 .o 文件的修改时间要比main这个文件新,那么,他就会执行后面所定义的命令来生成main这个文件。
-
如果main所依赖的 .o 文件也存在,那么make会在当前文件中找目标为 .o 文件的依赖性,如果找到则再根据那一个规则生成 .o 文件。
-
由于我的c文件和h文件都是存在的,所以make会生成 .o 文件,然后再用 .o 文件声明make的最终文件,也就是执行文件main.
(5)make和makefile的联系
make是一个命令,makefile是一个文件,当makefile 文件写好后,只需要一个make命令就可以将makefile文件运行起来。
4.库
(1)库是什么?
预先编译好的方法的集合。
分类
-
静态库
I 特点:程序在链接的过程当中,连接器从库文件中取得所需代码,复制到生成的可执行文件当中。因此,静态库
是在程序的链接阶段被复制到程序当中,和程序的执行过程没有关系。
II 创建
Eg:有两个源文件:main.c max.c
-
gcc –c main.c max.c (得到main.o 和max.o)
-
使用ar将目标文件归档
ar crv libxxx.a main.o max.o
**libxxx.a即为我们创建的静态库。
-
动态库
I 特点:动态库在链接阶段并没有被加载到程序当中,而程序在运行时被系统动态加载到内存当中供程序使用
II 创建
Eg:有一个源文件 max.c
-
gcc –fPIC –shared –o libxx.so max.c
上行代码等价与:gcc –c –fPIC max.c
gcc –shared –o libxx.so max.o
-
区别
静态库的可执行文件当中包含了库代码的一份完整的拷贝,因此,当它被多次使用时就会有多份没用的拷贝在内存当中,所以比较占内存;而动态库系统只需载入一次动态库,不同的程序可以得到内存当中相同的动态库的副本,因此会节省很多内存。
二.进程间相关知识
进程:一个正在运行的程序。一个进程主要包括三个因素
-
-
fork()
(1)函数原型:pid_t fork()
*pid_t是一个宏,其实质是一个整型数,是一个16位的整型(-32768-----32768),因此linux中可以创建的最大进程数为32768。
-
fork的复制过程
I:先申请一个pid(如果当前进程数已经达到了版本规定的上限,那么fork时将会出错);
II:先进行进程描述符(PCB)的复制,在接下来进行进程实体的复制;
III:然后进行写时拷贝
*写时拷贝:内核并不需要复制整个进程地址空间,只有在写入的时候才进行资源的复制。而且,其操作是以页为单位进行操作的。
-
fork函数的简单应用
Eg:A.有如下代码段:
int main()
{
pid_t pid1;
pid_t pid2;
pid1 = fork();
pid2 = fork();
}
问:a.执行这个程序后一共会产生几个进程?
b.如果其中一个进程的输出结果是pid1:1001;pid2:1002其他进程的输出结果为?
解:设由shall启动的进程为p0;
I:当程序执行到pid1=fork()时,p0启动一个新进程p1,且p1的pid为1001;
II:p0中的fork将1001返回给pid1,继续执行pid2=fork(),此时启动了一个新的进程为p2,p2的pid2为1002;
III:p0中的第二个fork将1002返回给pid2。因此,pid1:1001,pid2:1002;
IV:因为p2生成时p0中的pid1为1001,所以p2继承了p0中的pid1:1001,但是p2作为一个子进程它的返回值pid2:0,p2是从第二个fork后运行的,所以,pid1:1001,pid2:0;
V:p1作为p0的一个子进程,在第一个fork之后将0返回给pid1,在进行第二个fork时,p1产生了一个新进程p3;p1中的第二个fork将p3的pid返回给pid2,由题可知pid2为1003,因此,p1进程 pid1:0 pid2:1003
IV:p3作为p1的子进程继承p1中的pid1:0,而它本身也为一个子进程,所以 pid2 :0
综上所述:执行这个程序后一共会产生4个进程,分别为:
p0;
p1:pid1:0 pid2:1003
p2: pid1:1001 pid2:1002
P3: pid1:0 pid2:0
B.fork() && fork()会产生几个进程?
解:当第一个fork为0,不执行第二个fork,则只有一个fork() && fork();当第一个fork>0,执行第二个fork,则会出现两个fork() && fork();所以总共会有3个进程产生;
-
vfork()
特点:父子进程共享数据段,并且保证子进程先于父进程运行,在它调用exec或者_exit时,父进程才会被运行;
-
两种特殊的进程
-
僵死进程
I.描述:子进程已经结束,而父进程还在继续
II.处理方法:
-
程序调用signal(SIGCHLD,SIG_IGN),来忽略SIGCHLD信号,这样子进程结束后会由内核释放资源;
-
对子进程的退出捕获他们的退出信号SIGCHLD,父退出信号时,在信号处理函数中调用waitpid()操作来释放他们的资源。
-
B.孤儿进程
I.描述:父进程已经结束,而子进程还在继续。
II.处理:孤儿进程会由进程号为1的init所收养,并且会为他们完成状态收集工作。
-
-
-
exec()
-
函数的功能:调用它并没有产生新的进程,一个进程一旦调用exec()函数,它本身就死亡了。就好比鬼上身了一样,身体还是你的,但是灵魂和思想已经被替换了-----系统把代码段替换成新的程序的代码,其中唯一保留的就是进程的PID,对于系统而言,还是同一个进程,只不过是执行另一个程序罢了。
***I.只有fork()和vfork()才能创建一个新的进程;
II.在使用exec()之前,首先要使用fork(),创建一个子进程,子进程调用exec()函数.
-
exec()函数族
I.说明:
(1) int execv(const char *path,char *const argv[]);
*通过路径名方式调用可执行文件作为新的进程映像;
(2) int execve(const char*pathname,const char *argv[],char *const envp[]);
*参数pathname是将要执行的程序的路径名,参数argv,envp与main函数的argv,envp对应;
(3) int execvp(const char *file,char*const argv[] );
*与execv函数用法类似
(4) int execl(const char *path,const char *arg,...);
*与execv函数的用法类似,只是在传递argv参数的时候每个命令行参数都声明为一个单独的参数("....."说明参数个数不确定),这些参数要以一个空指针结尾。
(5) int execle(const char *path,const char *arg,....,char *const envp[]);
*与execl的用法类似;
(6) int execlp(const char *file,const char *arg,....);
*与execl函数类似
II.区别
-
(1)(2)(4)(5)取路径名做参数,(3)(6)取文件名做参数;
-
与参数表的传递有关(l表示list,v表示矢量vector)。函数execl、execlp和execle要求将新程序的每个命令行参数都说明为一个单独的参数,这中参数表以空指针结尾。而execv、execve和execvp则要先构造一个指向各参数的指针数组,然后将该数组的地址作为这三个函数的地址。
3.进程间通讯
-
进程间通讯的几种方式
管道 消息队列 信号量 共享内存 套接字
A.管道(pipe)
实现进程间通讯的原理:
就像现实中管道的两端一样,由一个进程进行写操作,其余的进程进行读操作。如果管道为空,那么read会阻塞;如果管道为满则write会阻塞。
分类:管道可以分为有名管道和无名管道两类。
区别:
I.有名管道:可以在任意进程之间进行通讯,通讯是双向的,任意一端都可读可写,但同一时间只能一端读,一端写。
II.无名管道:只能在具有亲缘关系的进程(父子进程)间通讯,不能在网络间通讯,并且是单向的,只能一端读另一端写。
特点:通讯数据遵循先进先出的原则
B.消息队列(message queue)
特点:
I.是消息的链表。具有特定的格式,存放在内存当中,由消息队列标识符标识。
II.消息队列允许一个或者多个进程向他写入与读取消息。
III.消息队列可实现消息的随机查询,不一定要以先进先出的顺序读取,也可以按照类型进行读取。
相关函数
msgget(): 用来创建或访问一个消息队列;
msgsnd(): 用来把消息添加到消息队列中;
msgrcv(): 从一个消息队列中获取消息;
msgctl(): 作用与共享内存的控制函数相似;
C.信号量(semaphore)
-
概念:用来同步进程的特殊变量;
-
手段:通过控制程序的推进速度,使得同一个过程只有一个进程访问;
-
操作方式:对信号量进行操作使用p v操作,p v操作都是原子操作;
*p操作:获取资源,信号量的值减1;
*v操作:释放资源,信号量的值加1;
D.共享内存(shared_memory)
实现原理:共享内存区域说白了就是多个进程共享的一块物理内存地址。假设有10个进程将这块区域映射到自己的虚拟地址上,那么,这10个进程间就可以相互通信。由于是同一块区域在10个进程的虚拟地址上,当第一个进程向这块共享内存的虚拟地址中写入数据时,其他9个进程也都会看到。因此共享内存是进程间通信的一种最快的方式。
相关函数
shmget(): 创建共享内存;
shmat(): 启用对第一个创建的共享内存的访问;
shmdt(): 将共享内存从当前进程中分离;
shmctl(): 共享内存的控制函数;
E.套接字(socket)
一种进程间通讯机制,客户/服务器系统的开发工作可以凭借该机制在本地单机上进行,也可跨网络进行。
套接字属性:
-
-
套接字的域:最常见的 AF_INET,指Internet网络;
-
套接字类型: 流套接字和数据报,流式套接字提供一种可靠,有序,双向字节流的连接,而数据报则作为一种单独的网络消息被传输,(通过UDP/IP实现连接)可能会复制,丢失或乱序到达。
套接字协议
具体实现:
-
int socket(): 创建套接字;
-
struct sockaddr_in(): 套接字地址;
-
int bind(): 命名套接字;
-
int listen(): 创建套接字队列;
-
int accept(): 建立连接;
-
int connect(): 请求连接;
-
close(): 关闭连接。
-
信号(signal)
(1)signal()函数[响应方式]
signal(参数1,参数2);
参数1:需要进行处理的信号;
参数2:处理的方式(1.默认 2.忽略 3.自定义)
Eg:a.signal(SIGINT,SIG_ING)
*SIG_ING代表忽略SIGINT这个信号
b.signal(SIGINT,SIG_DEF)
*SIG_DEF表示默认操作(对于大多数信号,系统的默认操作是结束该进程)
(2)kill()函数【是一个信号发送函数】
int kill(pid_t pid,int sig)
pid = 0:信号被发送到和当前进程在同一个进程组的进程
pid = 1:信号发给所有进程表中的进程;
int raise(int sig);
@@@ raise()也是一个信号发送函数,可以允许进程向自己发送信号。
(3)信号集
在实际的应用中一个应用程序需要对多个信号进行处理,为了方便,linux系统引进了信号集的概念。信号集用多个信号组成的数据类型sigset_t.可用以下的系统调用设置信号集中所包含的数据。
int sigemptyset(sigset_t *set); 将set集合置空,成功则返回0,失败返回-1.
int sigemptyset(sigset_t *set); 将set集合置空,成功则返回0,失败返回-1.
int sigaddset(sigset_t *set,int signo)将signo信号加入到set集合,成功则返回0,失败返回-1.
int sigdelset(sigset_t *set,int signo); 从set集合中移除signo信号,成功则返回0,失败返回-1.
int sigismember(const sigset_t *set,int signo); signo判断信号是否存在于set集合中,若真返回1,假返回0,失败返回-1.信号集的使用
int sigprocmask(int how,const sigset_t *set,sigset_t *oset);用于检测或更改其信号屏蔽字,包含头文件<signal.h>,成功则返回0,失败则返回1.参数:how 指示如何修改屏蔽信号
set是一个非空指针时,根据how修改屏蔽信号
oset是一个非空指针时,存放当前屏蔽信号集
若set为NULL,不改变该进程的信号屏蔽字,how也无意义 -
进程状态转移图
6.守护进程
1.守护进程有称精灵进程,常常在系统启动时自启,仅在系统关闭时才终止,生存期比较长!一般都是在后台运行。
可通过 ps -axj 命令查看常用系统守护进程,其中最为常见的 init 进程,负责各运行层次间的系统服务。
2.守护进程编程规则
(1)首先调用umask(mode_t umask())函数将文件模式创建屏蔽字设置为0;
-
调用fork(),然后使父进程退出(exit);
-
调用setsid()创建一个新会话;
-
将当前目录更改为根目录;
-
关闭不再需要的文件描述符;
-
某些守护进程打开/dev/null 使其具有文件描述符0,1,2,这样任何一个进程就不会产生其他不好的效果;
-
线程(thread)
1.概念:进程内部的一条执行路径或执行序列;(线程是进程的一个实体,一个程序中的多个执行路线)。
-
实现方式:
a.用户线程:创建销毁管理由线程库完成,无法使用多线程处理器的好处;
b.内核级线程:创建销毁管理由内核完成,创建代价高,但可以使用多线程处理器的好处;
-
线程同步
互斥锁 条件变量 信号量
互斥锁
对于多线程多任务互斥,一个线程占用了某一个资源,那么别的线程就无法访问,直到这个线程unlock,其他线程才可利用这个资源。
I.互斥锁的实现
有一台打印机,A正在打印东西,但是还没有打印完,B又想使用打印机,如果此时打印机不做任何事情的话打印出来的东西必定是乱的。因此,必须要对打印机做一个处理,我在打印东西的时候别人是不允许打印的,只有我用完别人才能使用。这个过程有点类似于,我将打印机放在一个房间里面,给门上放一把锁,这个锁是默认打开的,当我进去的时候会将锁锁住,然后下一个人来打印东西时,首先要检查这个锁,如果是锁住的,那么他只能在门外等,直到我在里面打开锁。
互斥锁的实现:
-
初始化锁。
在Linux下,线程的互斥量数据类型是pthread_mutex_t。在使用前,要对它进行初始化。
静态分配:pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
动态分配:int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutex_attr_t *mutexattr);-
加锁。对共享资源的访问,要对互斥量进行加锁,如果互斥量已经上了锁,调用线程会阻塞,直到互斥量被解锁。
int pthread_mutex_lock(pthread_mutex *mutex);
int pthread_mutex_trylock(pthread_mutex_t *mutex); -
解锁。在完成了对共享资源的访问后,要对互斥量进行解锁。
int pthread_mutex_unlock(pthread_mutex_t *mutex); -
销毁锁。锁在是使用完成后,需要进行销毁以释放资源。
int pthread_mutex_destroy(pthread_mutex *mutex);
条件变量
条件变量分为两部分: 条件和变量。条件本身是由互斥量保护的。线程在改变条件状态前先要锁住互斥量。条件变量使我们可以睡眠等待某种条件出现。条件变量是利用线程间共享的全局变量进行同步的一种机制,主要包括两个动作:一个线程等待"条件变量的条件成立"而挂起;另一个线程使"条件成立"(给出条件成立信号)。条件的检测是在互斥锁的保护下进行的。
如果一个条件为假,一个线程自动阻塞,并释放等待状态改变的互斥锁。如果另一个线程改变了条件,它发信号给关联的条件变量,唤醒一个或多个等待它的线程,重新获得互斥锁,重新评价条件。如果两进程共享可读写的内存,条件变量可以被用来实现这两进程间的线程同步。
条件变量的实现:
-
初始化条件变量
静态态初始化,pthread_cond_t cond = PTHREAD_COND_INITIALIER;
动态初始化,int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *cond_attr);-
等待条件成立。释放锁,同时阻塞等待条件变量为真才行。timewait()设置等待时间,仍未signal,返回ETIMEOUT(加锁保证只有一个线程wait)
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
int pthread_cond_timewait(pthread_cond_t *cond,pthread_mutex *mutex,const timespec *abstime); -
激活条件变量。pthread_cond_signal,pthread_cond_broadcast(激活所有等待线程)
int pthread_cond_signal(pthread_cond_t *cond);
int pthread_cond_broadcast(pthread_cond_t *cond); //解除所有线程的阻塞 -
清除条件变量。无线程等待,否则返回EBUSY
int pthread_cond_destroy(pthread_cond_t *cond);
信号量
以一个停车场的运行为例。假设一个停车场只有三个车位,一开始三个车位都是空的,这时候来了5辆车,看门人只允许其中三辆进入,然后放下车栏,剩下的2辆只能在外面等着,此后,如果有来的车辆也在外面等着。这时,有一辆车离开,看门人就可以放入外面的一辆车进去,接下来如果再有两辆离开,看门人又可以放入两辆车进去,如此反复。在上例当中,车位属于公共资源,每个车辆相当于一个线程,看门人就是信号量。
信号量的实现:
如同进程一样,线程也可以通过信号量来实现通信,虽然是轻量级的。信号量函数的名字都以"sem_"打头。线程使用的基本信号量函数有四个。
-
信号量初始化。
int sem_init (sem_t *sem , int pshared, unsigned int value);
这是对由sem指定的信号量进行初始化,设置好它的共享选项(linux 只支持为0,即表示它是当前进程的局部信号量),然后给它一个初始值VALUE。 -
等待信号量。给信号量减1,然后等待直到信号量的值大于0。
int sem_wait(sem_t *sem); -
释放信号量。信号量值加1。并通知其他等待线程。
int sem_post(sem_t *sem); -
销毁信号量。我们用完信号量后都它进行清理。归还占有的一切资源。
int sem_destroy(sem_t *sem);
I.信号量的特性
信号量是一个非负整数,所有通过它的线程都会将该整数减一(通过它是为了使用资源),当该整数值为0时,所有试图通过它的线程都将处于等待状态。在信号量上我们定义两种操作:wait(等待) 和release(释放)。当一个线程调用wait操作时,它要么得到资源然后将信号量减一,要么一直等待下去,直到信号量大于等于一时。Release实际上是在信号量上执行加操作,对应于车辆离开停车场
*临界资源:一次仅允许一个进程使用的资源(这里的资源可以是软硬件设备)
*临界区:访问临界资源的代码段;
信号量与互斥锁的区别
I.作用域:信号量:进程或线程间;
互斥锁:线程间;
II.上锁时:信号量:只要信号量的值大于0,其它线程就可以被sem_wait成功,成功后,信号量的值减一;如果信号量的值小于0,则sem_wait阻塞,直到sem_post释放后,信号量的值加1.
互斥锁:只要被锁住,其它线程都不可以访问B被保护的资源。如果没有锁,获取资源成功,否则进行等待资源可用。
线程安全概念及原理:
如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码。如果每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的。或者说:一个类或者程序所提供的接口对于线程来说是原子操作或者多个线程之间的切换不会导致该接口的执行结果存在二义性,也就是说我们不用考虑同步的问题。
若每个线程中对全局变量、静态变量只有读操作,而无写操作,一般来说,这个全局变量是线程安全的;若有多个线程同时执行写操作,一般都需要考虑线程同步,否则的话就可能影响线程安全。
-
-
网络编程
-
OSI七层模型和TCP/IP五层模型
-
OSI七层模型:
应用层---------------------为应用程序提供服务
表示层---------------------数据格式转化,数据加密
会话层---------------------建立,管理和维护回话
传输层---------------------建立,管理和维护端到端的连接
网络层---------------------IP选址及路由选择
数据链路层---------------提供介质访问和链路管理
物理层---------------------以二进制数据的形式在物理媒介上传输数据
B.TCP/IP四层协议模型:
应用层传输层网络层数据链路层
(2)tcp协议(在传输层)
*协议:通信的计算机双方必须遵守的一组约定
*端口:整型值,应用程序的代号
I.详解:是一种端到端的面向连接的传输控制协议,为应用提供可靠的数据传输服务
II.TCP连接建立(三次握手):
TCP断开(四次挥手)
III.TCP协议滑动窗口协议的功能
-
-
实现tcp协议的可靠传输;
-
流量控制:当tcp没来的及对前面的数据做处理确认时,A的可用发送窗口将变小,甚至为零。这样就减少了发送端的流量输出,反之则增大输出,这种方式能够实现发送端与接收端的协调,即实现了流量控制。
IV:拥塞控制
A.慢启动
由于tcp三次握手成功后,会向网络中发送大量的数据包,这样很容易出现拥塞。因此新建立的连接不能一开始就发送大量的数据包,而只能根据网络情况逐步增加每次发送的数据量。
B.快速重传
当丢包时,接收端重复发送丢包前的ACK,发送端每发送一个包过来,接收端就发相同的ACK过去,这个ACK是对丢包之前的确认。当接收端收到三个相同的ACK,就知道他发生丢包了,根据ACK序号就能重发丢的包。
C.快速恢复
快速恢复算法
-
TCP协议与UDP协议的区别
TCP:可靠的,有连接的,流式服务
UDP:无连接,不可靠,数据报
-
socket套接字
-
bind()函数
功能:通过给一个未命名的套接口分配一个本地的名字来为套接口建立本地捆绑(主机地址/端口号)。
使用:在connect()或者listen()调用前使用。
详情:int bind(int s,const struct sockaddr *name,namelen);
*"s"是socket()创建的文件描述符;
* "name"指向要连接套接字的sockaddr结构体的地址
*"namelen"sockaddr结构体的字节长度
B. connect()函数
功能:用于建立与指定socket的连接,发起三次握手
详情:int connect(int s,const struct sockaddr *name,namelen);
*"s"标识一个未连接的socket;
* "name"指向要连接套接字的sockaddr结构体的地址
*"namelen"sockaddr结构体的字节长度
C.listen()函数
功能:其可以使主动连接套口变成被动连接套口,使得一个进程可以接受其他进程的请求,从而成为一个服务器进程。
详情:int listen(int s,int backlog);
*"s"是socket()创建的文件描述符;
D.accept()函数
功能:接纳客户的请求;
详情:int accept(int s, struct sockaddr *addr,addrlen);
*"s"套接字描述符,该套接口在listen后监听连接;
*"addr"(可选)指针,指向一个缓冲区,接收连接实体的地址;
*"addrlen"(可选)指针,指向存有addr地址长度的整型数;
tcp编程流程: udp 编程流程:
.ser : .cli: .ser: .cli:
socket(); socket(); socket(); socket();
bind(); connetc(); bind(); sendto();
listen(); send(); sendto(); recvform();
c=accept(); recv(); recvform(); close();
recv(); close(); close();
send();
close();
-
I/O复用 select,epoll,poll函数
A.select()函数
I.原型:
int select(int nfds,fd_set* readfds,fd_set* writefds,fd_set* exceptfds,struct timecal* timeout);
*nfds指定被监听的文件描述符的总数;*readfd,writefd,exceptfds分别指向可读可写和异常事件对应的文件描述符的集合。*timeout设置select函数的超时时间。
II.使用
应用程序在调用select函数时,通过readfd,writefd,exceptfds三个参数传入自己感兴趣的文件描述符。Select函数调用返回时,内核将修改他们来通知应用程序那些文件描述符已经就绪。fd_set结构体只包含一个整形数组,数组每一位包含一个文件描述符。
III.描述符就绪条件
a.满足下列条件之一,套接字准备好读
a1:套接字接收缓冲区当中的数据字节数大于等于套接字接收缓冲区中设置的最最小值。(对于TCP和UDP来说默认值为1)
a2:改连接的读半部关闭(也就是接收了FIN的TCP);
a3:该套接字是一个监听套接字,且已完成的连接数不为0;对于这样的套接字,accept通常不会阻塞;
a4:其上有一个套接字错误处理;
b. 满足下列任意一个条件时,套接字准备好写
b1:该套接字发送缓冲区中可用空间的大小大于等于套接字发送缓冲区当中设置的最小值时,并且或者该套接字已经连接,或者套接字不需要连接(UDP);
b2:该连接的写半部关闭;
b3:使用非阻塞式的connect的套接字已建立连接,或者connect已经以失败告终;
b4:其上有一个套接字待错误处理;
IV:缺点
a.每次调用select,都需要把fd集合从用户态拷贝到内核态,这个开销在fd很多时会很大;
b.每次调用select都需要在内核遍历传递进来的所有fd,这个开销在fd很多时也很大;(时间复杂度为O(N));
c.单个进程能够监视的文件描述符存在最大的限制。
*用户态:执行自己的代码;
*内核态:执行操作系统的代码;
*在执行系统调用,中断服务,或者程序异常时,会从用户态切到内核态;(系统调用:在内核实现;函数:在库中执行,不一定会出现用户态和内核之间的切换);
*调用系统调用后用户态和内核态之间的切换:
B.poll()函数
I.函数原型:
int poll(struct pollfd *fds,nfds_t nfds,int timeout);
II.函数使用
与select函数一样,它也是在指定时间内轮询一定量的文件描述符,以测试其中是否有已经就绪的。
III.缺点
包含大量的文件描述符的数组被整体复制于用户态与内核态之间,而无论文件描述符是否就绪,它的开销随着文件描述符数量的增加而线性增大。(时间复杂度为O(N));
IV.优点
没有最大文件描述符数量的限制(不过随着文件描述符数量的增加,它的性能也会下降)。
C.epoll()函数
I.epoll函数的创建:int epoll_create(int size);
*size只是给内核一个提示,告诉它需要多大的事件表;
II.epoll内核事件表的操作:
int epoll_ctl(int epdf,int op,int fd,struct epoll_event*event);
*fd为要操作的文件描述符;*op指定操作类型(往事件表中注册,修改,删除fd上的注册事件);*event 指定事件;
III.epoll系列系统调用的主要接口:
int epoll_wait(int epfd,struct epoll_event *events,int maxevents,int timeout);
此函数如果检测到事件,就将所有就绪事件从内核事件表中(由epfd中的参数指定)复制到它的第二个参数events指定的数组中,这个数组只输出epoll_wait检测出的就绪事件。
*LT模式:在数据到达后,无论程序是没有接收,还是接收了,但没有接收完,下一轮epoll_wait仍然会提醒应用程序该描述符上有数据,直到数据被接收完;
*ET模式:在数据到达后,无论程序是没有接收,还是接收了,但没有接收完,都只提醒一次,下一轮不在提醒应用程序该描述符上有数据。所以,要求程序在收到提醒时必须将数据接收完,否则将会出现丢掉数据的可能。
三个函数的区别
应用层协议
http协议(端口号为80)
特点:
A1:支持客户/服务器模式;
A2:简单快速。客户向服务器请求服务时,只需传送请求方法和路径。
*常用的请求方法有:
GET:请求获得资源
POST:请求附加新的数据
PUT:请求服务器存储一个资源
A3:灵活。允许传输任意类型的数据对象。
A4:无连接。其含义是限制每次连接只处理一个请求。服务器处理完客户的请求并收到客户的回应后,断开连接。
A5:无状态,即对处理事务没有记忆力;
B.http协议的响应
响应由三个部分组成:状态行,消息报头,响应正文
B1:常用的应答状态码:
1XX:指示信息---表示请求已接收,继续处理;
2XX:成功------表示请求已经被成功接收,理解,接受
3XX:重定向-----要完成请求必须进行更近一步的操作
4XX:客户端错误----请求有语法错误或者请求无法实现
*400----客户端请求有语法错误,不能被服务器理解;
*401-----请求未经授权
*403-----服务器收到请求,但拒绝提供服务
*404-----请求资源不存在
5XX:服务器端错误----服务器未能实现合法的请求
*500---服务器发生不可预期的错误
*503----服务器当前不能处理客户端的请求,一段时间后可能恢复正常。
http和https协议(网络协议)的区别:
A.https协议需要申请ca证书,一般免费证书很少,需要交费;
B.http是超文本传输协议,信息是明文传输,https则是具有安全性的SSL加密传输协议;
C.http和https使用的是完全不同的连接方式,用的端口也不一样(http:80;https:443);
D.http协议的连接很简单,是无状态的;
E.https协议是由SSL+HTTP协议构成的可进行加密传输,身份认证的网络协议。比http协议安全。