Linux总结

   1 Linux第一天====================================================================================
   2 一.基础命令
   3 1.history 查看历史命令,此终端自安装至现在的所有。
   4 2.命令解析器:
   5     1)shell. 针对Unix操作系统
   6     2)bash.  针对Linux操作系统
   7     3)工作方式: 用户在终端输入命令, 命令解析器搜索(通过环境变量中的路径)对应的路径,查找同名命令,如果找到了就执行该命令。
   8 
   9 3.遍历历史命令:
  10     1)从当前位置往上: ctrl+p(方向键上也行);
  11     2)从当前位置向下: ctrl+n(方向键下也行);
  12 
  13 4.光标移动:
  14     1)向前 ctrl+b;  2)向后 ctrl+f;  3)移动到行首 ctrl+a;  4)移动到行首 ctrl+e;
  15 
  16 5.删除光标位置的字符:
  17     1)光标后面的字符(即光标盖住的那个字符):ctrl+d;
  18     2) 光标前面的字符:ctrl+h;
  19     3) 删除光标前面的字符串: ctrl+u;
  20     4) 删除光标后面的字符串:ctrl+k;
  21 
  22 二.Linux目录结构:
  23 1. / 根目录:根目录下面的是系统文件,一般别动。
  24 2. 终端下面的各种颜色代表不同的文件
  25     1).白色: 普通文件
  26     2)蓝色: 目录
  27     3)绿色: 可执行文件
  28     4)青色: 链接文件
  29     5)红色: 压缩文件
  30     6)黄色: 设备文件
  31     7)灰色: 其他文件
  32 
  33 3.根目录下面各文件的内容: 
  34     1)bin: (binary)二进制文件,存放命令(系统命令,可执行的程序)
  35     
  36     2)etc: 系统或用户安装的软件用到的"配置文件";
  37         如:查看用户:etc/passwd (可以看到当前系统下的所有用户)用户登录时的用户名和密码也在该目录
  38     
  39     3) lib: Linux操作系统运行时使用的一些基本动态库(.so)。
  40     
  41     4)media:自动挂载外设,会将外设自动挂载到该目录下。
  42     
  43     5)mnt: 手动挂载目录。一般默认挂载到该目录。
  44     
  45     6)opt: 安装目录,一般是空的。
  46     
  47     7)root: 超级用户的家目录
  48     
  49     8)sbin: 超级用户运行时用到的命令(存放的地方)    
  50     9)usr(user software resource): 用户软件资源目录;(1.当前用户安装的一些应用程序,2.一些库文件(如C 的标准库))。    
  51     10)boot: 开机启动项
  52     11) dev: 存放设备文件 
  53     12)home: Linux操作系统所有用户的家目录。
  54     13)tmp: 存放临时文件(每次系统重新启动,都会清空)
  55     
  56 4.yyx@yyx-virtual-machine:/home$: 字符的意义
  57     1)$:表示普通用户;  #:表示超级用户
  58     2)@前的内容: 表示当前用户名;
  59     3)@后的内容,在冒号之前: 表示主机名。
  60     4)冒号后的内容: 表示当前所在的目录。
  61 
  62 5.系统安装程序后缀
  63   ubuntu  系统安装程序后缀:.deb
  64   windows 系统安装程序后缀:.exe
  65   安卓      系统安装程序后缀:.gdb
  66 
  67 6.Linux 七种文件类型
  68     1) d: 目录
  69     2)    -: 普通文件
  70     3) c: 字符设备
  71     4) b: 块设备
  72     5) p: 管道
  73     6) s: 套接字(实现局域网的传输 sorckt)
  74     7) l:符号链接
  75 
  76     在细分为:
  77         d, -, l;  (占用内存空间)
  78     伪文件:(不占用内存空间)
  79         c, b, p, s;
  80 7.创建文件:
  81     1)touch 文件; 文件存在,就会更新文件的日期; 
  82                     文件不存在,就会创建文件。
  83 
  84 8.创建嵌套目录:
  85     mkdir wbq/aa/bb    -p;(注意后面的参数-p,可以加在最后面,也可以加在紧随mkdir后面)    bb目录在aa目录里,aa在wbq目录里。
  86     pwd 查看当前用户所在的目录。
  87 
  88 
  89 8.查看文件内容:
  90     1)cat+文件名:  文件内容显示在终端上
  91     2)more+ 文件名:只能向下翻。回车向下走一行,空格,显示下一页。
  92     3)less+ 文件名: 可以上下翻页
  93     4)head -行数 文件名(从文件头部数)
  94     5)tail -行数 文件名(从文件尾部数)
  95 
  96 9.拷贝文件/目录
  97     cp 存在的文件 新文件(当拷贝文件时,需要有"读权限").
  98         1)如果新文件名不存在,就创建
  99         2)如果新文件名存在,就覆盖源文件(这里要注意,新文件名一般要写成不存在的,否则原来的内容就丢了)
 100     cp拷贝目录
 101         需要加参数 "-r";
 102 
 103 10.创建软硬链接
 104     1)创建软连接:
 105         ln -s 创建软连接的源文件名  软链接名字
 106             1)使用相对路径创建的软连接,移动链接到不同的目录就不可以使用了。
 107             2)解决这种问题办法: 创建绝对路径的软连接。在原文件名前加上它的绝对路径。
 108     2)创建硬链接:
 109         ln 创建硬链接的源文件名  硬链接名字
 110             1)硬链接的文件不区分相对和绝对路径。
 111             2)硬链接文件不占用磁盘空间,一改全改(不管哪个,相当于程序的静态变量)
 112 
 113 11.修改文件权限的方法(chmod)
 114     1)文字法 (r w x ) chmod 修改对象+(-)权限 文件名
 115         u 用户        g 用户组       o其他人        a所有人
 116 
 117     2)数字设定法: chmod 数字    文件名
 118         写权限 对应的数字      2
 119         读权限 对应的数字     4
 120         执行权限 对应的数子 1
 121 
 122 12.修改文件所有者(chown)    
 123     1)修改文件的所属者:  chown 所有者 文件名(要使用超级用户权限)
 124     2)修改文件的所属组      chown  所有者:所属组  文件名
 125     3)修改文件所属组(第二种方法): sudo chgrp 所属组 文件名;
 126 
 127 13.文件的查找和检索
 128     1)根据文件的属性查找 find
 129         1)根据文件名  find 查找的路径 -name "查找的文件名"
 130 
 131         2)根据文件大小    find 查找的路径 -size 查找的大小
 132             大于:+; 小于:-;  等于:(不用写)
 133             如: find ~ -size +100k -size -100M    (查找大于100k小于100M的文件)
 134             注意: k 小写  M大写
 135             "~ 当前用户的家目录(或者宿主目录)"
 136 
 137         3)根据文件类型:find 查找的路径 -type 文件类型
 138 
 139     2)根据文件内容查找 grep
 140         1)grep -r "查找的内容" 查找的路径(可以和一些命令搭配):find ./ -type f | grep 01.c;  "查找当前目录下,名字含有01.c的普通文件"
 141 
 142     3)locate 文件名;这个命令更快。直接搜索linux数据库,有索引,所以很快。
 143         但是缺点:如果数据库没有更新,会有内容找不到。不关机的情况下,linux默认每天半夜两点左右更新。
 144 
 145 14.查看文件或目录属性        
 146     1)查看文件文件的字节数,字数, 行数; wc 文件名 参数
 147     2)参数:-c:  文件的字符数
 148              -l:  文件的行数(也可以用来喝一些命令搭配); find ./ -type f | wc -l;  "查找当前目录下,普通文件的个数" 149              -w:  文件的字节数。    
 150 
 151 15.查看指定命令所在的路径:
 152      which  命令;可以查看该命令所在的文件。(cd命令例外, 它被嵌入在系统中)
 153 
 154 16.软件的安装和卸载:
 155     1.
 156         1)主流安装(在线):
 157             1)sudo apt-get install 软件名    :  安装
 158         2)主流卸载:
 159             1)sudo apt-get remove  软件名    :  卸载
 160         3)更新软件列表
 161             1)sudo apt-get update ; (它的工作原理是:更新软件的下载地址。)更新完后再重新下载
 162         4)清空缓存:
 163             1) sudo apt-get clean ;    清空在线安装后的缓存
 164                 缓存的路径:
 165 
 166     2.
 167            5)安装包程序安装:.deb后缀(安装包的名字)
 168 
 169         6)安装包安装的软件卸载:
 170 
 171 17. U 盘的挂载和卸载
 172     1)挂载: mount 命令
 173         1)mount + 设备名 + 挂载的目录
 174         2)获取U盘设备名:
 175             sudo fdisk -l;
 176         3)显示中文:
 177             加上参数: -o iocharset utf8
 178 
 179 18:  sd 闪盘(U盘)
 180      hd 移动硬盘
 181      fd 软盘;
 182 
 183 19.压缩包管理(Linux):
 184     1) 压缩格式:    "(只压缩不打包)"
 185         .gz格式: 
 186             压缩:   gzip 压缩的文件;
 187             缺点:   1. 压缩不保留原文件, 2.压缩的时候不能能打包。  3.不能压缩目录
 188             解压缩:     gunzip 压缩包名
 189 
 190         .bz2格式:
 191             压缩:   bzip2    压缩的文件;(如果想保留源文件;加参数 -k;  bzip2 压缩的文件 -k )
 192             解压缩: bunzip2  压缩包名;
 193             缺点: 1.压缩的时候不能打包,  2.不能压缩目录
 194 
 195     2)常用的压缩工具:"(只打包不压缩)"
 196          1)tar:打包。
 197             参数:j- (指定的压缩工具)
 198                   z- (指定的压缩工具)
 199                   c-  创建新的压缩文件
 200                   x-  从压缩文件中释放文件    
 201                   v-  详细报告压缩文件信息
 202                   f-  指定压缩文件的名字
 203 
 204         2)压缩语法:
 205             tar 参数 压缩包名称 原材料(参数,与压缩工具配合;zc. jc. )
 206                 1)zcvf
 207                 tar zcvf test.tar.gz 需要压缩的文件 
 208                 2) jcvf
 209                 tar jcvf test.tar.bz2 需要压缩的文件
 210 
 211         3)解压缩:
 212             tar 参数 压缩包名
 213                 语法:
 214                     .tar.gz后缀的压缩文件    tar zxvf test.tar.gz 
 215                     .tar.bz2后缀的压缩文件  tar jxvf test.tar.bz2
 216 
 217             解压缩到指定目录:
 218             (参数)    -C 指定的目录(路径)。
 219                     tar zxvf test.tar.gz  -C 路径
 220                     tar jxvf test.tar.bz2 -C 路径
 221 
 222     3) 非主流的压缩文件:rar
 223         1)安装软件: rar  (sudo apt-get install rar)
 224         
 225         2)压缩:
 226             1)rar a 压缩包名 原材料; (压缩包的名字不需要添加后缀)
 227                 rar a test  需要压缩的文件
 228             2)-r 参数(压缩目录): rar a test 需要压缩的文件 -r;
 229         
 230         3)解压缩:rar x 压缩包名
 231             rar x test.rar (知道那个的目录,可加可不加);
 232 
 233     4)zip
 234         1)压缩:
 235             压缩目录(加参数 -r);(zip (-r) 压缩包名 原材料);包名不需要后缀
 236             zip test 需要压缩的文件;  zip -r test 需要压缩的目录 ;
 237         2)解压缩:
 238             unzip 压缩包名 (-d) 解压缩的目录(路径)
 239             unzip test.zip (解压在当前目录);   unzip test.zip -d 指定的目录
 240 
 241 4)-的使用时机:
 242 
 243 20.进程管理:
 244     1) who 当前登录操作系统的用户和终端设备编号
 245     2) tty
 246     3)  PID 进程的ID号。
 247     查看整个系统 的运行状况:ps
 248     4) ps aux | grep 查找的东西
 249     5 250 
 251 21.手动杀死进程
 252     1)kill -9(SIGKILL) 进程PID;
 253     )查看kill的信号: kill -l;
 254 
 255 22.查看环境变量:env
 256     1)环境变量的格式:key = value:value:value:……;
 257 
 258 23.任务管理器: top
 259     1)关闭它: q 或者 ctrl +
 260 
 261 24.网络管理:
 262     1)获取网络接口的信息:ifconfig
 263     2)测试与其他主机的连通性
 264         参数
 265 
 266     3 267 
 268 三。用户管理
 269     1.添加用户:
 270         1) sudo adduser 
 271     2.添加用户组
 272 
 273     vi etc/passwd;       vi etc/group;
 274 
 275     3.切换用户:su; sudo su;这两个操作的区别
 276         1)普通用户
 277         2)超级用户
 278     4.修改用户密码:
 279         1.修改普通用户的密码
 280         sudo passwd 用户名
 281         2.修改超级用户的密码
 282         
 283     5.删除用户
 284         sudo deluser 用户名
 285     6.删除组:
 286     
 287     7.退出:
 288 
 289     命令:动词在后面
 290     脚本:动词在前面
 291 
 292 四。常用服务器搭建:
 293     1.FTP服务器的搭建:
 294         1)它是:一款软件;用户来上传和下载文件。
 295         2)服务器端:
 296             a.安装软件:sudo 
 297             b.服务器端需要设置:
 298 
 299         3)重新启动服务,使配置生效
 300             sudo 
 301 
 302         4)客户端的操作
 303             1.实名用户登录:
 304 
 305             2.上传个下载文件(注意:只能操作文件,不能操作目录)
 306 
 307                 1)缺点:
 308                     1)需要告诉对方服务器密码;
 309                     2)用户可以在服务器任意目录切换
 310 
 311             3.匿名用户登录:        
 312                 必须指定匿名用户登录目录
 313 
 314 五:lftp客户端的使用
 315     1.只是一个登录ftp 服务器的客户端
 316     2.使用前先安装: sudo apt-get install lftp
 317     3.登录ftp服务器
 318         1.lftp IP(服务器的IP);
 319         2.login --- 匿名用户登录
 320 
 321     4.基本操作: 
 322     基本操作:    put, get;(与自带的ftp功能一样)  。
 323     上传多个文件: mput 文件名;
 324     下载多个文件: mget 文件名
 325     下载目录: mirror 目录名;
 326     上传目录: mirror -R 目录名;
 327 
 328 六: 查看掩码:命令 umask ;
 329         它的作用: 本地的掩码: 0002;本地创建的目录权限为 775; 满权限为 777; 可以看出实际权限 = 满权限-掩码
 330                                       本地创建的文件的权限为 664; 本地的目录权限 - 文件权限 = 111; 可以看出:文件默认不会给它执行权限。
 331 
 332         "目录必须有执行权限才能被打开。"
 333         ftp上传的文件默认权限为 700; 此时只有 ftp 用户可以操作,不合理;需要修改配置文件;anoy_umask=022; 这样ftp用户传输到服务器的文件权限就为 755了;
 334         目录权限与掩码的关系: 目录权限 = 777 - 掩码;
 335         文件权限与掩码的关系: 文件权限 = 777 - 掩码 - 111 336 
 337 七:nfs网络共享服务器的搭建和使用
 338     1.nfs ===相当于windows的共享目录
 339     2.服务器端:
 340         1.安装服务器程序:sudo apt-get install nfs-kernel-server;
 341         2.创建一个共享目录:
 342             1)设置配置文件: /etc/exports;  添加一个共享的目录(绝对路径)和 ip 地址段和(权限);
 343             2)权限: ro ----只读;    rw ----读写;             sync -----数据实时同步
 344             例如: /home/yyx/xuexi 192.168.22.*(rw,sync) 这个ip地址为:允许这个网段的用户挂载共享路径。
 345         3.重启服务:  sudo service nfs-kernel-server restart;
 346 
 347     3.客户端:
 348         1.通过挂载服务器共享目录的方式:
 349             sudo mount serverIP(服务器的Ip):共享的路径 挂载的路径;
 350         2.退出方式:
 351             与普通挂载一样。sudo umount 挂载的路径
 352 
 353 八。ssh服务器的使用:(管理员才会使用)。
 354     1.作用: 远程登录程序;
 355     2.软件的安装: sudo apt-get install openssh-server;
 356     3.远程登陆:
 357         ssh 用户名@IP(要远程登录的用户名和它的IP)
 358     4.退出:logout。
 359 
 360 九。超级拷贝
 361 scp命令: 安装软件:sudo apt-get install openssh-server
 362     命令: scp-r (远程服务器用户名@IP(远程):远程服务器的目录  拷贝到的本地目录)
 363         scp -r Robin@192.168.28.37:/home/Robin/share ~/test
 364 
 365 
 366 "——————————————————————————————————————————————————————————————————————————————————————————————————————————————————"
 367 FTP 服务器搭建和登录
 368 1.先安装服务器: 命令: sudo apt-get install vsftpd
 369 2.修改配置文件: 命令:sudo vi /etc/vsftpd.conf;
 370 3.重启ftp服务: 命令: sudo service vsftpd restart;
 371 4.此时只允许实名用户登录和操作,匿名用户只能登录。
 372 5.匿名用户登陆设置:创建一个专属匿名用户根目录。
 373     1)修改配置文件:添上专属根目录
 374     2)重启ftp服务器
 375 6.此时就都可以登录和使用了
 376 7.用户登录:ftp IP(服务器的IP);
 377     1)匿名用户登录:用户名:anonymous;  密码:不需要
 378 8.传输数据: put;  下载数据: get;
 379 9.弊端:只能操作文件,不能操作目录。
 380 "——————————————————————————————————————————————————————————————————————————————————————————————————————————————————"
 381 
 382 "——————————————————————————————————————————————————————————————————————————————————————————————————————————————————"
 383 lftp 客户端的使用;
 384 
 385 "——————————————————————————————————————————————————————————————————————————————————————————————————————————————————"
 386 
 387 十。 VI 的三种模式:
 388     1. 命令模式:
 389         h左 j下 k上 l下(左右在两端,上下在中间);
 390     2. 文本模式:
 391     3. 末行模式:
 392 
 393     4.一些基本操作:
 394         1.光标移动到文件开头: gg;  光标移动到文件尾部: G;
 395         2.光标移动到行首:0; 光标移动到行尾: $;
 396         3.行跳转【如: 13行】: 13G;
 397         4.删除光标后的字:x; 删除光标前的一个字: X; 删除一个单词: dw;
 398         5.删除光标前本行的内容:d0; 删除光标后本行的内容: D;
 399         6.删除光标所在行: dd;   删除多行:  行数 + dd;
 400         7.撤销: u;  反撤销: ctr + r;
 401         8.复制:复制当前行:yy;  复制从当前行以下 n 行: n yy;  
 402         9.粘贴: 从光标所在位置下一行粘贴: p;  从光标当前行位置粘贴: P;
 403 
 404     5.可视模式:
 405         命令: v;    进入模式后,可以有选择的拷贝和删除(如只拷贝一行中的某些内容)
 406 
 407     6.查找操作:
 408         从光标位置向上查找:?+查找的内容;  从光标位置向下查找:/ + 查找的内容; n ----遍历操作
 409         查光标所在位置的内容: 命令 #;
 410     7.替换操作: 单字符替换:命令 r(再输入自己想输入的内容。)替换光标后的字符;
 411 
 412     8.创建新行:命令 o:当前光标所在行的下面;   命令 O:相反。
 413       删除光标当前行:S,然后进入文本模式;   删除光标前一个字符:s,然后进入文本模式;
 414 
 415     9.末行模式下的替换:
 416         1)先在命令模式下进行查找操作,然后在末行模式下替换。
 417         2):s/查找的内容/替换的内容; 
 418 
 419     10.分屏操作:
 420         水平分屏:sp+文件名 ;    垂直分屏:vsp+文件名 ;     屏幕切换:ctr + ww;
 421 
 422     11.打日志: -D, -I; -g;  -Wall;  -O优化等级
 423 
 424 
 425 "----------------------------------------------------------------------------------"
 426     12.生成静态库:
 427         1.命名格式:libxxx.a
 428         2.制作:
 429             1)生成 .o文件;gcc -c .c文件 -I(.c文件中使用的)头文件所在路径(自己编写的头文件);
 430                 如:"gcc -c *.c -I ../include;"
 431                 
 432             2)将生成的.o文件打包; ar rcs 静态库的名字 所有的.o文件
 433                     例如: "ar rcs libCalc.a *.o;"
 434 
 435                     查看打包后的静态库: nm 名字;
 436                            "nm libCalc.a;"
 437 
 438         3.静态库的使用:"(-I 后面的路径可以加空格(版本高的,可加可不加),可以不加(版本低的))"
 439             1)第一种方法;   gcc + 测试.c文件 -o 可执行程序的名称  -I头文件的路径 静态库的路径
 440               例如:gcc main.c -o app -I ./include lib/libcacal.a;
 441                2)第二种方法: gcc + 测试文件 -o 可执行程序名称 -I 头文件的路径 -L 静态库的路径 -l xxx(静态库的名字,掐头去尾(去掉lib 和 .a));
 442                例如:gcc main.c -o app1 -I ./include -
 443                L ./lib -l cacal;
 444         4.静态库的优缺点:
 445             优点:  1)库被打包到可执行程序中,直接发布可执行即可使用。2)寻址方便,速度快
 446             
 447             缺点:   1)静态库的代码在编译时被载入可执行程序,所以静态库越大,可执行程序越大(这也导致了它的第一个优点);
 448                     2)当静态库的改变了,可执行程序也必须重新编译。
 449                     3)每个使用静态库的进程都要将库编译到可执行文件中再加载到内存中,内存消耗严重。
 450 
 451         5.问题1:
 452     13.生成动态库:
 453         1.命名格式:libxxx.so
 454         2.制作:
 455             1)生成与位置无关的 .o; gcc -fPIC -c 编译的.c文件 -I (.c文件中)头文件的路径;
 456                 例如: gcc -fPIC -c *.c -I ../include;
 457 
 458             2) 打包;gcc -shared -o 生成的动态库的名字 .o文件;
 459                 gcc -shared -o libCalc.so *.o ;
 460 
 461         3.动态库的使用:gcc main.c -o 可执行程序的名字 -I (main.c中头文件路径) 动态库的路径+动态库的名字
 462                 gcc main.c -o app2 -I ../include lib/libCalc.so;    "注意:这里面动态库的路径不加-L参数,生成的执行程序运行不起来"
 463 
 464         4. ldd + 可执行程序;
 465             查看程序执行时,调用的动态库。
 466         
 467         5.手动添加动态库的方法:
 468             1)临时添加:(测试的时候使用;)
 469             2)永久添加:(修改家目录的bashrc文件)
 470             3)修改配置文件的方式:修改动态链接器的配置文件
 471                 更新动态链接器:
 472                 查看链接器的路径:sudo ldconfig -v参数。
 473 
 474         6.动态库加载失败的原因:
 475             1)解决方案:
 476                 a. (临时方案,用于程序测试)使用环境变量: LD_LIBRARY_PATH=动态库的路径;   然后倒入环境变量: export LD_LIBRARY_PATH
 477                 b.(永久设置。)修改动态链接器的配置文件:
 478                 c.(修改家目录的 .bashrc文件);
 479         7.动态库的优缺点:
 480             优点:多进程共享一份库文件,节省内存,易于更新 。
 481             缺点:相较于静态库而言库函数访问略慢。
 482     14.使用静态库的参数操作动态库,看能否跑起来。(不能加静态库的操作参数)
 483     
 484 "-----第四天-----------------------------------------------------------------------------"
 485     15. 其他命令:
 486         1) alias 当前命令="加强版的命令" 487             alias gcc='gcc -std=c99';    使gcc 的默认编译按照 c99 规范。(在家目录的bashrc配置文件中);
 488         2)echo 取变量的值:echo $变量名;(取出变量的值)变量名前加 $;
 489             例如:echo $PATH;(取出环境变量的值);
 490             "取最近的函数的返回值:  echo $? "
 491 十一。windows中的动态库
 492     1.  xxx.dll(windows中的动态库名后缀);
 493     2." 动态库与静态库发布时的区别:"
 494 
 495 
 496 十二。gdb 调试:
 497     1.得到包含调试信息的可执行性程序:
 498     gdb -g main.c -o app -I ../include ;
 499 
 500     2.运行调试程序:
 501         gdb 可执行程序名;
 502         
 503     3.命令: 
 504         1)执行命令:两种  start 一行一行执行,执行过程中可以添加其他参数调试。 
 505                         run  直接执行到程序结束。
 506         2)查看程序内容: l (什么都不加);l + 行号; l + 函数名; l + 函数名:行号;
 507 
 508         3)加断点:b 行数 ; 查看断点信息:i  b;    删除断点:del b 断点编号
 509 
 510         4)继续执行程序:c ; c + 断点编号(跳到此段的位置); 打印内容:p  变量名;  往下执行一步:n
 511 
 512         5)自动追踪变量的值:display + 变量名;查看追踪变量的编号:i display;  取消追踪: undisplay + 追踪变量编号
 513 
 514         6)跳出循环:1)设置变量的值:set var 变量名=值;
 515                      2)在循环的的外面设一个断点,跳到该断点。
 516         
 517         7)获取变量的类型:ptype 变量名;
 518         
 519         8)进入到函数体的内部:s ;跳到下一个断点: c 。
 520         
 521         9)跳出当前函数:finish;
 522 
 523         10)条件断点:b 17 if i == 10(当i等于 1017 行打断点,注意:断点不能设在 for 循环的那行,不会停止);
 524 
 525 十三. Makefile 文件编写;
 526     掌握内容:
 527         一个规则,两个函数,三个自动变量
 528     
 529     1.命名:makefile;  Makefile;
 530     2.用途:
 531         项目代码编译管理;节省编译项目的时间;
 532     3.Makefile 中的规则:
 533         目标:依赖;
 534             生成一个 app 执行程序;需要依赖的函数;
 535         命令:编译命令;
 536         
 537         标准格式:"目标 : 依赖(依赖这个位置不一定非得有,有的时候是没有的。如下面的 make clean:)
 538                          命令"
 539     操作界面的:make 目标;========== 540     4.工作原理:
 541         1)若想生成目标,检查规则中依赖的条件是否存在;如果不存在,寻找是否有规则用来生成该条件
 542 
 543         2)检查规则中目标是否需要被更新,必须检查它的所有依赖;依赖中有任何一个被更新,则目标必须被更新。(规则:"依赖文件比目标时间晚,则需要被更新。" 544 
 545     5.Makefile编译版本:"makefile中所有的函数都有返回值"
 546         1)版本1:
 547 
 548             缺点:效率低,每次修改任意一处,都需要编译所有的源文件。
 549 
 550         2)版本二:
 551 
 552             缺点:冗余。
 553             "app:main.o div.o mul.o sub.o            //第一个条件
 554                 gcc main.o div.o mul.o sub.o -o app
 555 
 556             main.o:main.c                             //第一个条件的依赖条件生成
 557                 gcc -c main.c
 558             div.o:div.c
 559                 gcc -c div.c
 560             mul.o:mul.c
 561                 gcc -c mul.c
 562             sub.o:sub.c
 563                 gcc -c sub.c"
 564 
 565         3)版本三:(使用了变量)
 566             1.Makefile中的变量: 变量名=value
 567             
 568             2.Makefile中的自动变量:
 569                 1)$@:    规则中的目标
 570                 2)$<:    规则中的第一个依赖
 571                 3) $^:    规则中的所有的依赖
 572             注意: 这三个只能在命令中使用。
 573 
 574             3.Makefile中自己维护的变量:
 575                 CPPFLAGS=头文件的路径;
 576 
 577             缺点:相关的文件不能自动查找
 578             "代码:
 579                 obj = main.o div.o mul.o sub.o          
 580                 target = app3
 581                 CPPFLAGS=-I ../aa/
 582 
 583                 #第一条目标
 584                 $(target):$(obj)
 585                     gcc $(obj) -o $(target)
 586 
 587                 #第二条目标
 588                 %.o : %.c
 589                     gcc -c $< $(CPPFLAGS)"
 590 
 591 
 592         4)    版本四:
 593             1.makefile 中的函数:
 594                 1)所有的函数都有返回值
 595                 2)wildcard---查找指定目录下指定类型的文件
 596                         "src = $(wildcard ./*.c);"
 597                    patsubst---匹配替换
 598                            obj = $(patsubst %.c,%.o,$(src))
 599             缺点: 功能不够强大
 600             "代码:
 601                 src = $(wildcard ./*.c)
 602                 target = app
 603                 obj = $(patsubst %.c, %.o, $(src))
 604                 CPPFLAGS = -I ./
 605 
 606                 $(target):$(obj)
 607                     gcc $(obj) -o $(target)
 608 
 609                 %.o:%.c
 610                     gcc -c $< $(CPPFLAGS)"
 611 
 612         5)  版本五:删除编写过程中的临时文件
 613             1.生成一个与终极目标无关的目标:clean
 614                 make clean
 615 
 616             1.声明伪目标:.PHONY:变量的名;
 617                 伪目标特点:makefile 不会检查目录下是否有这样的文件,也不会对它的时间做检查
 618 
 619             2.-的作用:、该命令如果错误,跳过该命令,继续执行下一条命令。
 620                 clean:
 621                     -rm $(obj);        有提示:(当前命令无法执行时有提示)
 622               -f的作用:强制删除当前    
 623                     -rm -f $(obj);    无提示:(当前命令无法执行时无提示)
 624                 "代码
 625                     src = $(wildcard ./*.c)
 626                     target = app
 627                     obj = $(patsubst %.c, %.o, $(src))
 628                     CPPFLAGS = -I ./
 629 
 630                     $(target):$(obj)
 631                         gcc $(obj) -o $(target)
 632 
 633                     %.o:%.c
 634                         gcc -c $< $(CPPFLAGS)
 635                     .PHONY:clean
 636                     clean:
 637                         -rm -f $(obj)"
 638 
 639         终极目标:第一条目标所依赖的规则;
 640         "每级目标时间比每级依赖时间晚。"        
 641 
 642 十四。linux系统的 IO 函数:
 643     linux 每运行一个程序(进程),操作系统都会为其分配一个 0~4G 的虚拟内存空间(32位操作系统)。
 644     
 645     1.C库函数:
 646         1)fopen 函数返回值:一个文件指针(结构体):(包含三个方面:1.文件描述符, 2.文件读写位置, 3.I/O缓冲区(内存地址));
 647             文件描述符:描述符表:相当于一个数组,大小为1024;
 648             一个进程最多打开1024个文件;
 649 
 650         2)缓冲区的数据往磁盘上写内容的三种情况:
 651             1.刷新缓冲区:(fflush)
 652             2.缓冲区已满;
 653             3.正常关闭文件(fclose);  或者:main函数中(return, exit);
 654             "注意:标准C库函数 有缓冲区,而linux 系统函数 没有缓冲区 ";
 655         3)ELF(Linux下可执行程序的格式):
 656             ELF中存放的是源代码,他有一个入口,main函数。主要分为三段:.bss(未初始化的全局变量);.data(已初始化的全局变量);    text(代码段) ;
 657 
 658 
 659         4) PCB:进程控制块;其中有一个文件描述表(其中存储文件描述符;一个文件占用一个文件描述符),是一个数组,有1024个大小;所以一个进程之内最多只能打开1024个文件。
 660             每打开一个新文件,则占用一个文件按描述符,而且使用的是空闲的最小的一个文件描述符。
 661 
 662     "代码区是栈模型"。
 663 
 664 
 665 
 666 "-----第五天-----------------------------------------------------------------------------"
 667 一.系统 I/O 函数:
 668     1.open() 函数当文件不存在时,创建文件,并为它指定权限;(函数的第二个参数,各种宏定义; 第三个参数:指定权限,与掩码的取反再按位与得出文件最后的权限)。;
 669         1)正常打开, 2)创建文件;  3)文件截断;
 670     2.文件截断:
 671     
 672     3. perror() 函数的使用:头文件stdio.h; 打印错误信息。;
 673 
 674     4. read() ;  size_t(无符号整型);   ssize_t(有符号整型);(UNIX 的类型);
 675 
 676     5. linux系统函数的注意事项:需要判断函数的返回值。-1,失败;  
 677     
 678     6.Linux系统函数实现文件的拓展:
 679         第一步 lseek(fd, 1000, SEEK_END );
 680         第二步 write(fd, "a", 1);    (必须有一次写操作)。
 681         "拓展的部分为空洞文字;有空洞文字的文件叫空洞文件,作用: 提前占用磁盘空间,下载中的应用:防止下载一半,空间不够。"
 682 
 683     7.stat()函数: 
 684         1.off_t(unix 中的整型);
 685         2.应用(断点续传;)
 686 
 687 
 688     8.一些命令的穿透功能:如:vi; cat; (遇到软连接时)
 689         不穿透的:如: rm;    (遇到软连接时)
 690 
 691     9.chmod()
 692 
 693     10.chown()函数的参数查找;必须在配置文件中查找
 694 
 695     11.unlink()函数;(删除文件目录项,使之具备被释放的条件。)
 696         函数使用场景:实现能够自动删除临时文件;
 697         注意点:当文件在打开状态时:open(),并没有关闭;unlink,当close的文件的时候,会自动删除。
 698         "例如: 
 699             int fd = open("san.txt", O_RDWR);
 700             unlink("san,txt");  //删除文件,当前状态不会删除
 701             write(fd, buf1, 102);
 702             lseek();
 703             read(fd, buf, 200);
 704             close(fd);            //此时文件 san.txt 才会自动删除
 705                       "
 706     12.chdir:修改当前进程的路径
 707             int chdir(const char *path);  path:当前进程的名字。
 708 
 709     13.getcwd:获取当前进程的工作路径。char* getcwd(char* buf, size_t size);
 710         参数:buf--缓冲区,存储获取到的工作路径。
 711               size--缓冲区的最大容量。
 712 
 713     14.mkdir--创建目录: int mkdir(const char* pathname, mode_t mode );
 714         参数: pathname: 创建的目录名。  mode: 目录权限(8进制)。给出的权限经过换算之后才会得到最终权限。(mode & ~umask & 0777)    
 715         
 716 
 717 
 718     
 719 
 720 "-----第六天-----------------------------------------------------------------------------"
 721 
 722 1.版本控制:svn/git;
 723 
 724 2.进程的概念:
 725     1)程序和进程;
 726         每个进程操作系统会为它分配 0-4G 的虚拟内存空间(32位操作系统); 其中0-3G为用户内存空间,进程可以对它进行读写操作; 3G - 4G 为系统内核空间,进程没有读写权限。
 727         进程只能读写用户空间,没有权限读写内核空间(kernel);
 728     2)内存页面的概念
 729         操作系统是按页来管理内存的;每个页有4096个字节。
 730         还可以设置页面属性:如char *str = "hello",字符串在只读数据段,此时只能读,不能写。
 731 
 732     3)进程的4种形态:0-3级; 0 级(内核态),最高; 3级(用户态);最低。
 733 
 734     2)并发:
 735     3)单道程序设计/多道程序设计
 736     4)CPU/MMU
 737     5)PCB:结构体;其中有一个指针指向文件描述符表;文件描述符表中存储1024个指针;已经打开的文件结构体。
 738         每个进程有属于自己的PCB(进程的状态描述符);但是多个进程只有一个内核区。
 739 3.环境变量控制
 740     1)echo $名称;    查看该名称的环境变量。
 741     2)查看系统的环境变量: env;
 742     3)获取环境变量的值:char *getenv(const char* name);    获取键name环境变量的值。
 743     4)设置环境变量:int    setenv(const char *name, const char *value, int rewrite);    将环境变量name的值设置为 value。
 744                     如果环境变量 name 已存在;则 rewrite 非0,覆盖原来的环境变量;rewrite为0,则不覆盖原来的环境变量。
 745     5)删除环境变量: void unsetenv(const char *name) 删除 name    的定义;
 746 
 747     6)进程里可以设置自己的环境变量,避免设置在终端,影响他人。
 748 4.环境控制原语:
 749     1)fork:
 750     2)wait/waitpid
 751     3)
 752 
 753 5. printenv.c
 754 
 755 6.进程的状态:4 种或 5 种,都对。 就绪, 运行, 睡眠, 停止。
 756 
 757 7.CPU的组成状态: 运算器,寄存器,控制器,译码器。
 758 
 759     操作系统完成进程调度。(cpu进行时钟周期运算)
 760     cpu的分时复用功能,使进程看起来像多进程。
 761     在单核 cpu 上,同一时间只能有一个进程处于运行状态。那么多核 cpu,就可以有多个进程处于运行状态。
 762 "8.子进程、父进程间的共享、不共享:"
 763         共享: 1.宿主目录; 2.进程工作目录; 3.信号处理方式;  4.环境变量;
 764                 5. 0-3G的用户空间("读时共享,写时复制。"“特别是:全局变量”)  6.文件描述符(打开文件的结构体) 7.mmap建立的映射区。
 765         
 766         不共享:1.进程ID, 2.进程运行时间  3.闹钟;   4.未决信号集   5.PCB
 767 
 768 8.进程原语:
 769     1)创建子进程。pid_t fork();    调用1次,返回两次;在父进程返回子进程的 PID, 在子进程返回0.
 770     2) fork()的工作进程:先调用 creat(),创建一个进程, 在调用 clone(),给子进程复制父进程的内容;
 771     3)pid = fork();    //此时父子进程就都出来了。
 772     4)pid_t getpid();  返回调用进程的PID号;
 773        pid_t getppid();    返回调用进程的父进程的PID号
 774        当在子进程中 getpid(),得到的值与 fork()父进程的返回值相等,都是这个子进程的id号
 775     5)父子进程:读时共享,写时复制。
 776     6)最大创建进程 个数;
 777 9.    uid_t getuid(void);        //返回实际用户id;
 778     uid_t geteuid(void);     //返回有效用户id;
 779     "注意: 文件实际用户与有效用户在创建该文件的家目录下时,如果没有修改,此时是相通的。
 780             当文件被拷贝到root目录时,   "    
 781     设置用户id; chmod 04755;  4 是设置用户ID。  完后效果为 -rwsr-xr-x;  s表示设置了用户ID。
 782             作用:当执行此文件时,执行者有效用户ID变为此文件的所有者。
 783             设置用户组ID: chmod 06777; 6 是设置用户ID和组ID; 效果为: -rwsrwsrwx;
 784 
 785 10. exec族函数的使用:
 786     1)功能: 去磁盘中加载另一个可执行程序,用它的代码段、数据段替换掉当前可执行程序的代码段、数据段;然后从后加载的这个程序退出,被替换的程序后续代码就不执行了。(换核不换壳,)
 787     2)一般情况下:与 fork() 函数联合使用;。  同时exec族函数不会创建新进程, 所以该程序的 ID 不会改变。
 788 
 789     3)函数的返回值: 成功没有返回值,运行完毕,自己退出。
 790                       失败返回 -1;
 791     4)exec族函数的规律: l(list):命令行参数列表。  p(path): 搜索file时使用path变量。
 792                           v(vector):使用命令行参数数组。  e(environment):使用环境变量的数组,不使用进程原有的环境变量,设置新加载程序的环境变量;
 793     
 794     5)int execl(const char *path, const char *argc, ...); 
 795     例如:execl("./bin/ls", "ls", "-l", "-a", NULL);    这几个参数的作用:其中第一个参数:"./bin/ls",命令的环境变量(即所在文件),不可以改变,必须完整正确;
 796         "ls":占位参数,可以随便写,一般写成该命令。(不能缺失,如果缺失了,就会把后面的命令参数变为占位参数,输出结果会出错。) "-l","-a"该命令的参数。   NULL:卫兵,执行到这里时命令结束。
 797 
 798 11. toupper():将小写字母变为大写。
 799 
 800 12.僵尸进程,孤儿进程:
 801     1)概念:
 802         僵尸进程:子进程终止,父进程未回收,子进程残留资源(PCB)存放于内核,造成僵尸进程。
 803         孤儿进程:父进程先于子进程终止,则子进程成为孤儿进程,然后被init进程领养。
 804 
 805     1)S:睡眠状态;  Z:僵尸状态;
 806     2)僵尸进程的产生原因:用户空间释放,内核空间的PCB没有释放,等着父进程回收。(它消耗的是内核当中的内存资源)
 807             即:子进程退出,父进程没有回收子进程资源(PCB),则子进程变为僵尸进程。
 808     3)子进程的PCB(在内核空间)没有释放,是留给父进程回收用的。只有父进程回收后,僵尸进程才会消失。
 809     4)杀死僵尸进程的办法是杀掉它的父进程。
 810 
 811   孤儿进程:父进程先于子进程结束,则子进程变为孤儿进程,子进程的变为1号进程init进程,由1号进程领养。
 812 
 813   "僵尸进程比孤儿进程更危险, 因为它不会自动关;,而孤儿进程会由 1 号进程领养,当它执行完毕后,会被 1 号进程回收"。
 814   做开发时主要避免的是僵尸进程的产生。用 wait(); waitpid(); 来避免僵尸进程的产生。
 815 
 816 
 817 13. pid_t wait(int* status);  (阻塞函数,等待回收子进程资源;如果没有子进程,返回-1);
 818     1)返回值:回收的子进程的ID号,    wait(NULL);不关心子进程如何死亡,直接回收。
 819                                     wait(&st); 用st来保存子进程死亡时的状态。
 820     
 821     2)    父进程的ID与它的进程组ID相同;子进程的组ID与父进程的ID相同。
 822         kill -9 -父进程的ID(即组进程的ID),这个进程组的所有进程都被杀死。"(注意 - 不能少)"
 823 
 824 14.pid_t waitpid(pid_t pid, int *status, int options);    (设置非阻塞。)
 825     第一个参数 pid的值类型:    1) < -1; 回收指定进程组内的任意子进程。(因为父进程与子进程属于统一进程组,父进程与孙子进程属于不同的进程组,但是他们有血缘关系。)
 826                                     -1;回收任意子进程(只要有血缘关系就行。)
 827                                     0; 回收 当前调用 waitpid 一个进程组的所有子进程。
 828                                     > 0; 回收指定ID的子进程。
 829     第二个参数:保存子进程的推出状态。
 830 
 831     第三个参数:WNOHANG:如果没有子进程退出,立即返回。(实现了非阻塞的 wait).
 832 
 833 15. 阻塞函数:非阻塞函数:在文件open(O_NONBLOCK)的时设置宏;读常规文件不会出现阻塞,当读伪文件时会出现。
 834         例如:阻塞读终端; 管道; 网络;
 835 
 836     设置非阻塞后:应设置轮询。
 837         
 838 
 839 "=========进程间的通信(IPC)==========================================================================================================="
 840 一。IPC方法:Linux环境下,多个进程间的通信,需要通过内核,在内核中开辟一块缓冲区(赋予他用户权限);进程1把数据从用户空间拷贝到内核缓冲区,
 841     进程2 把数据从内核缓冲区拷读走,内核提供的这种机制称为进程间的通信(IPC)。
 842 
 843     进程间的通信;四种方法:1.pipe管道 2.fifo有名管道,3.内存共享映射 4.Unix Domain Socket;
 844     管道(使用最简单);信号(开销最小);共享映射区(速度最快,效率最高); 本地套接字(最稳定);
 845 
 846 二。pipe管道:
 847 1.管道的特性:数据只能一个读,一个写,必须是一个方向。
 848             半双工:数据同一时刻只能有一个流向。即只能父进程写,子进程读; 或子进程写, 父进程读。
 849             全双工:数据同一时刻可以两个方向。
 850             单工:数据只能同一个方向流动。
 851             dup2(int oldfd, int newfd);        将第一个参数拷贝给第二个参数。
 852             1.其本质是一个伪文件(实为内核缓冲区)
 853             2.有两个文件描述符引用,一个读端,一个写端。
 854             3.规定数据从管道的写端流入,从读端流出。
 855   
 856   管道的原理:管道实为内核使用环形队列机制,借助内核缓冲区(4K)实现的。
 857 
 858   管道的局限性:1.数据不能自己写,自己读;
 859                   2.管道中的数据不能反复读取,一旦读走,管道中不再存在。
 860                   3.采用半双工通信方式,数据只能在单方向上流动。
 861                   4.只能在有血缘关系之间的进程使用管道。
 862                   5.只能进行单向通信,双向通信需要建立两个管道。
 863 
 864 2. pipe()创建管道,用于有血缘关系之间的通信。(采用环形队列实现的)
 865     管道使用半双工通信; 创建完管道完后,确定通信方向:父写子读,或子写父读。
 866     如果想创建多条管道,一定要先pipe(),再fork(),使子进程得以继承管道。
 867 
 868     使用管道时的四种注意情况:
 869     1) 写段关闭,读端读完管道里的内容时;再次读,返回0,相当于读到文件末尾EOF;
 870     2)写端未关闭,写端无数据, 读端读完管道里的数据时,再次读,阻塞。
 871     3)读端关闭, 写段写管道,产生SIGPIPE信号,写进程默认情况下会终止进程。
 872     4)读端未读管道数据,当写段写满管道后,在此写,阻塞。
 873     5)使用管道,无须open,但需手动close。
 874 
 875     管道的缓冲区大小:1.函数的方法:fpathconf(int fd, int name);  第一个参数为管道描述符;第二个参数为情况标识符。
 876                         2.命令: ulimit -a;    查看系统 软硬件限制。
 877 
 878 3.    总结:
 879     1.读管道:
 880         1.管道中有数据,read返回实际读到的字节数;
 881         2.管道中无数据,写端都被关闭,read返回 0,相当于读到文件末尾;
 882                         写端未关闭,read函数阻塞等待,(期待不久的将来会有数据到来,但此时会让出CPU资源)
 883     2.写管道:
 884         1.管道读端全部关闭;进程异常中止(返回一个终止信号);
 885         2.管道读端未关闭:
 886             1)管道已写满,write阻塞等待
 887 
 888             2)管道未写满,write将数据写入,返回实际写入的字节数;
 889 
 890 
 891 "==========================================================================="
 892 4.设置非阻塞管道的两种办法:
 893     1)fcntl函数设置非阻塞管道;
 894         int flg = fcntl(fd, F_GETFD);
 895         flg |= O_NONBLOCK;
 896         fcntl(fd, F_SETFL, flg);
 897     2)打开文件时直接设置非阻塞;
 898         int fd = open("/dev/tty", O_RDWR | O_NONBLOCK);
 899 当读取一个非阻塞文件,但是此时没有内容,会出错,错误码为EAGAIN;
 900 5. read() 函数的返回值四种情况;由于非阻塞设置的存在.
 901     1.返回值 > 0; 读到的字节数
 902     2.返回值 < 0; 读到文件末尾
 903     3.返回值 == -1;但是errno != EAGAIN  或 EWOULDBLOCK
 904         if( ret == -1) {
 905         if(errno ==  EAGAIN)
 906         {
 907             说明文件被设置为非阻塞方式读取,此时数据没有到达。
 908         }
 909         else 
 910         {
 911             失败;
 912         }
 913     }
 914     4.返回值 == -1, 但是errno == EAGAIN  或 EWOULDBLOCK;
 915         说明此时文件被设置为非阻塞方式读取,数据还没有到达。
 916 "========================================================================"
 917 三. fifo有名管道:解决无血缘关系的进程通信;
 918     1.介绍:
 919         FIFO文件在磁盘上没有数据块,仅仅用来标识内核中的一条管道。各进程可以打开这个文件进行read/write,实际上是在写内核通道,这样就实现了进程间的通信。
 920     1.创建fifo的方法:
 921         1)在终端创建一个有名管道;(不常用)用命令;"mkfifo 管道名";
 922         2)在代码运行时创建一个有名管道;(常用)用函数;int mkfifo(const char*pathname, mode_t mode);成功返回 0,失败-1;
 923 
 924     2.利用fifo实现非血缘关系进程间通信
 925         1.进程使用 “同一个fifo” 完成进程间通信。
 926         2.一个进程以只读方式打开read端,一个程序以只写打开写段。
 927         3.一根管道可以打开多个读端,多个写段。(一个写段多个读端; 或 一个读端多个写段);
 928     
 929     3.多种特殊情况:
 930         当一个写端多个读端时: 每个读端读取到的数据都不相同,因为从管道中读数据,读走之后,管道中的这个数据就不存在了;此时每个读端读到的所有内容合在一起为
 931                                 管道写端写入的数据
 932         当多个写端一个读端时, 每个写端写入的数据都会被这个读端读取出来;此时管道读到的数据为所有写端写入的数据。
 933                             
 934 四。mmap 内存映射:
 935 
 936         从磁盘映射到kernel区,同时kernel区这段内存(映射占用的)开放了用户区的权限;所以进程可以进行访问
 937 
 938 1.文件是应用于进程间通信
 939     1)父子进程之间
 940         可以用文件进行通信;
 941     2)非血缘关系进程间通信
 942         可以用文件进行通信;
 943     
 944 2.建立映射区,完成进程间通信             
 945     1)mmap函数;六个参数的不同意义
 946 
 947     2)注意事项
 948         1.open的时候可以创建一个新文件,但是用此文件创建映射区时,大小不能为0;(可以进行文件拓展后再用来创建映射)
 949             mmap使用的时候经常出现总线错误,通常是由于共享文件存储空间大小引起的。
 950             
 951         2.创建映射区的时候, 隐含着一次对文件的读操作;所以文件打开的方式只能为"读写;或读"。
 952           当 MAP_SHARED(对内存的修改将反应到磁盘空间)    时,要求 映射区的权限 <= 文件打开的权限。
 953           当 MAP_PRIVATE(对内存修改不会反应到磁盘空间)  时,此时则没有要求。(mmap中的权限是对内存的限制);
 954 
 955         3.映射区创建完后关闭文件按描述符,对映射区没有影响。
 956         4."最后一个参数,偏移量必须是4K的整数倍。"
 957         5.munmap函数传入的地址一定要是mmap的返回地址。坚决杜绝对mmap函数的返回值进行++操作
 958         6.mmap创建映射区出错的概率非常高,必须进行出错判断。
 959 
 960     3)父子进程间的mmap(用户空间独立,内核去各进程共享。)
 961         1.先mmap创建映射区
 962         2.再fork()共享映射区内存首地址
 963         3.指定MAP_SHARED;(必须是 SHARED);
 964 
 965     4)非血缘关系进程间通信
 966         1.使用“同一个文件”创建映射区
 967         2.两个进程,一个以read映射区,一个以write方式打开映射区;
 968         3.指定 MAP_SHARED;
 969         4)MAP_ANONYMOUS(匿名映射); 不可以用来通信
 970         5)/dev/zero(系统的伪文件); 不可以用来通信
 971     5)匿名映射
 972         linux 创建匿名映射:第四个参数添加 MAP_ANON或MAP__ANONYMOUS;(注意这个匿名宏,只有Linux操作系统可以使用)。
 973 
 974         UNIX(没有上述的匿名宏)。使用系统中的特殊文件(/dev/zero;(无限读,可以用来创建匿名映射))
 975 
 976 3.数据可以重复读取;(即多个读端读取一个写端,读取的内容相同,与fifo不同,取决于读取的数据)。
 977 
 978 
 979 
 980 "====================进程间的通信(IPC)================================================================================================"
 981 
 982 文件名最大为255字节(byte);
 983 "============================硬链接========================"
 984 创建一个硬链接:
 985     1.dentry(包含文件名,文件属性)
 986     2.一个文件包含(三大块):文件的名字; inode(指向文件属性) ;文件内容;
 987     3.
 988 "============================硬链接========================"
 989 
 990 strace 跟踪程序的运行时函数的系统调用;
 991 
 992 "===信号========================================================================================================================"
 993 一.信号的概念:
 994     1.本质:
 995         软中端;信号通过内核发送,内核处理的。
 996     2.特性:
 997         1)简单,2)不能携带大量信息; 3)满足某一条件
 998     3.信号机制:
 999         软中端;信号通过内核发送,内核处理的;通过软件的方法实现的,有较强的延迟性。
1000         
1001     3.四要素:
1002         1.信号编号, 2.信号名称, 3。默认处理动作,4.对应的事件。
1003         
1004         2.信号的默认处理动作(5种):
1005             1.终止进程(term);2.忽略(Ign:信号被处理丢弃);3.终止进程并产生core文件(core);
1006             4.Stop(暂停);  5.Cont (继续);
1007 
1008     4.名词称呼
1009         1.信号的状态:
1010             1.产生;
1011             2.未决:处于产生和递达中间,由于阻塞不能递达、处理。
1012             3.递达;信号递达后内核会立即处理。(也就是说,他俩经常绑定在一起,信号递达就代表这信号被处理。)
1013             4.处理:(信号必须递达)
1014                 1.忽略处理(信号已经处理,丢弃操作); 2.执行默认操作,  3.捕捉(不执行默认操作,来指定操作让其执行;)
1015             
1016         2.未决信号集:(pending)
1017             在PCB中,以位图的方式存在。
1018             记录信号是否被产生,并且被处理。 内核处理信号的依据。用户不能直接修改未决信号集。
1019 
1020         3.阻塞信号集/信号屏蔽字:(mask)
1021             在PCB中,以位图的方式存在。    mask影响pending;用户只可以通过mask来影响pending。
1022             记录信号是否被设置屏蔽。用户影响信号的依据。
1023 
1024         4. 1-31 号信号,常规信号(不支持排队); 34-64 号信号,实时信号(支持排队)
1025 
1026         5.特殊信号:(两个);
1027             9 号、 19 号信号;不允许捕捉,忽略,甚至不能设置屏蔽。
1028 
1029         6.产生段错误的方式(三种);
1030             1.访问非访问区域。0x1000   printf();            mmu---没有映射该虚拟内存
1031             2.对只读区域进行写操作。 char* p = hello; p[1] = 'H';      mmu---映射的虚拟内存对应的物理内存权限不足。 0 内核权限; 3 用户权限
1032             3.内存溢出;  char buf[10];  buf[10] = '10'; buf[100] = '28';  mmu---没有映射该虚拟内存。
1033 
1034 二.产生信号:
1035     1.常见的产生信号方法:
1036         1.按键产生:ctrl+c;
1037         2.系统调用:如kill
1038             kill(pid_t pid, int sig); 向指定进程或进程组发送指定信号;
1039                 第一个参数: pid > 0; 向指定进程发送指定信号;    pid == 0; 向调用kill函数的进程组的所有进程发送该信号
1040                             pid == -1; 发送信号给系统中有权限的所有进程;  pid < -1; 发送信号给指定的进程组|pid|;
1041                 第二个参数: 信号。(不同的平台环境下,信号的编号不同;但是信号的宏定义相同,所以一般使用宏名)。
1042 
1043             raise() 给当前进程发送指定信号;
1044             abort() 向当前进程发送SIGABRT信号;
1045         3.软件条件产生:如,定时器alarm;
1046             alarm:每个进程有且只有唯一一个定时器。
1047             返回值特殊:上次定时剩余的时间。定时(采用自然定时),与进程状态无关!!!,无论处于何种状态,都会计时。;
1048             取消闹钟: alarm(0);    实际执行时间 = 系统时间+用户时间+等待时间。
1049              定时的单位是:秒。
1050             setitimer(); 定时单位单位:微妙;
1051                 三个参数:
1052 
1053 
1054         4.硬件异常产生:如,非法内存访问(段错误);内存对齐出错(总线错误);除0(浮点数除外)。
1055 
1056     
1057         5.命令产生:如kill命令
1058 
1059 三。信号操作函数;
1060     1.信号集 set:            
1061         sigset_t   set;
1062 
1063         sigemptyset(&set)  清空集合  
1064 
1065         sigfillset(&set)  置1集合  
1066 
1067         sigaddset(&set, 待添加的信号名称) 添加信号到集合中
1068 
1069         sigdelset(&set, 待删除的信号名称) 删除信号到集合中
1070 
1071         sigismember(&set, 待判断的信号名称)判断信号是否在集合中  -- 》 1:在 0:不在; -1;错
1072 
1073     2.mask(信号屏蔽字/阻塞信号集)操作
1074         sigprocmask();用来屏蔽信号、解除信号;其本质是读取或修改进程的信号屏蔽字(PCB中);
1075         "注意:屏蔽信号只是将信号处理延后执行(延至解除屏蔽), 而忽略表示将信号丢弃。"
1076         参数:第一个参数 how 的取值:假设当前的信号屏蔽字为mask;
1077                 1.SIG_BLOCK:当how设置为此时,set表示要屏蔽的信号。相当于mask= mask|set;
1078                 2.SIG_UNBLOCK:当how 设置为此时,set 表示要解除的信号。相当于:mask = mask & ~set;
1079                 3.SIG_SETMASK; 当how 设置为此时,set = mask;
1080             第二个参数: set 传入参数:用来操作mask的set集合。
1081             第三个参数: oldset 传出参数。记录旧有的mask状态。
1082 
1083     3.pending操作
1084         int sigpending(sigset_t *set);传出参数
1085         参数:获取未决信号集
1086         返回值:存在返回 1; 不存在返回 0;
1087 
1088 四。信号捕捉
1089     1.signal:(注册信号的捕捉处理函数)
1090         参数:1.信号编号 2.捕捉后调用的执行函数(是一个函数指针。即回调函数);
1091         返回值:捕捉的函数句柄。
1092 
1093     2.sigaction: 1)信号捕捉函数执行期间,本信号被自动屏蔽(取决于:sa_flags) 2)信号捕捉函数执行期间,信号多次产生,只记录一次。
1094 
1095      函数原型:int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
1096          参数解析: 1)第一个参数:信号名称或编号;要捕捉的信号。
1097                     2)第二个参数:新处理动作传入,是一个结构体。
1098                            1)sa_handler: 1)函数指针,捕捉信号后执行的回调函数;2) SIG_IGN 表示忽略; 3)SIG_DFL 表示执行默认操作。
1099                         2)sa_mask; 信号屏蔽字/阻塞信号集;捕捉函数执行期间的屏蔽字,此中的信号在捕捉函数执行期间自动屏蔽。
1100                         3)sa_flags: 1) 0; 表示:信号捕捉函数执行期间,要捕捉的信号被自动屏蔽(即第一个参数)。
1101                                     2)SA_SIGINFO: 选用sa_sigaction来指定捕捉函数;
1102                                     3)SA_INTERRUPT: 系统调用被信号中断后,不重启;
1103                                     4)SA_REATART: 系统调用被信号中断后,自动重启。
1104                                     5)SA_NODEFER: 在捕捉函数执行期间不自动屏蔽捕捉的信号;
1105 
1106                        4) sa_sigaction: 函数指针, 三个参数,void (*sa_sigaction)(int, siginfo_t*, void*);指定带参数的信号捕捉函数。
1107                     3)第三个参数:旧处理动作传出;
1108             返回值: 0 表示成功, -1 表示失败,并设置errno ;
1109     3.信号捕捉特性:
1110         1)捕捉函数执行期间,屏蔽字由sa_mask指定。
1111         2)捕捉函数执行期间,被捕捉的信号自动屏蔽; 由sa_flags = 0 决定;
1112         3)"捕捉函数执行期间,常规信号不支持排队,多次产生只记录一次。"    
1113         常规信号为:1-31 信号; 实时信号为 34-64 信号。
1114         详细过程:进程正常运行时,默认PCB中有一个信号屏蔽字,假定为☆,它决定了进程自动屏蔽哪些信号。当注册了某个信号捕捉函数,捕捉到该信号以后,
1115            要调用该函数。而该函数有可能执行很长时间,在这期间所屏蔽的信号不由☆来指定。而是用sa_mask来指定。调用完信号处理函数,再恢复为☆。
1116     
1117     4.信号内核实现捕捉函数思想:
1118         信号捕捉函数是 回调函数; 由内核在信号产生、递达之后负责回调。
1119         调用结束应该返回内核空间,再返回用户空间。
1120 
1121     5.内核实现信号捕捉的一般过程
1122         1)回调捕捉函数; 2)调用结束先返回内核。
1123 
1124         “注意:此处加图片:”
1125 五。竟态条件(时序竟态)
1126     1.pause() 函数:主动造成进程挂起,等待信号唤醒。调用该系统调用的进程将处于阻塞状态(主动放弃CPU),知道有信号到达将其唤醒。
1127         int pause(void); 返回值:-1,并设置errno == EINTR;
1128         注意:
1129                 1) 唤醒pause()函数的信号不能执行默认操作、忽略、屏蔽。原因:
1130                     1)如果信号的默认动作是终止进程,则进程终止,pause函数不会有机会被调用。
1131                     2)如果信号默认动作是忽略、屏蔽,则进程继续挂起,pause不会调用。
1132                 2)如果信号的处理动作是捕捉,则【捕捉函数处理完后,pause被调用返回-1,并且errno == EINTR,表示被信号终端,即唤醒】;
1133                 3)综上所诉:能唤醒pause()函数的信号,只能是被捕捉的信号;
1134                 
1135     2.时序竟态:
1136         1.时序问题产生的原因: 1)系统负载严重; 2) 信号不可靠机制;
1137 
1138         2.解决时序问题:
1139             1)使用原子操作;将解除信号屏蔽与挂起等待合并成一个步骤。sigsuspend() 函数具有这个功能。在对时序要求严格的场合下都应用
1140                 sigsuspend 替换 pause。"(解除信号屏蔽+挂起等待信号)----》原子操作---》sigsuspend()函数"
1141                 int sigsuspend(const sigset_t *mask); 挂起等待信号。 
1142                     函数详解: 1)主动造成进程挂起,等待信号; 2)在这个函数调用期间屏蔽字由它的参数决定; 3)函数调用结束屏蔽字恢复为原值;4)原子操作
1143             2)提早预见,主动规避。这种错误,没法用gdb调试出来。
1144 
1145     3.全局变量的异步IO:
1146         存在的问题:
1147             1.多个进程对 同一 个全局变量进行写操作,存在问题。(如:信号回调函数中的全局变量和主函数中的全局变量为同1个,内核进程和主进程都对
1148                 全局变量进行了写操作,非常容易出问题)。
1149             2.写操作时没有任何 同步 机制。(加锁机制)
1150 
1151             “注意:此处有代码,父子进程数数,代码1,主进程和内核对同一个全局变量进行写操作,出问题;
1152                                              代码2,全局变量只由主进程进行一次赋值操作后,主进程和内核进程对它的只读操作,没有出问题”
1153 
1154     4.可重入、不可重入函数:
1155         注意:
1156             1)不可重入函数:函数内部含有全局变量、静态变量,使用malloc、free;
1157             "2)信号捕捉函数应设计为可重入函数;(与上面的相反的就是可重入函数)。"
1158             3)信号处理程序调用的可重入的系统调用函数;可参阅 man 7 signal;
1159             4)没有包含在上述列表中的系统调用函数大多数是不可重入的,其原因是:
1160                 a) 使用了静态数据结构,
1161                 b) 调用了malloc 或free
1162                 c) 是标准I/O函数 ;
1163 
1164 
1165 六。SIGCHLD信号 -------IGN(该信号的默认动作是忽略)
1166     1.该信号的产生:
1167         1)子进程终止;
1168         2)子进程接收到SIGSTOP信号停止时;
1169         3)子进程处在停止态,接收到SIGCONT后唤醒时;
1170         综上所述:只要子进程的状态发生变化,会对父进程发出SIGCHLD信号,默认处理的动作是忽略。
1171 
1172     2.借助 SIGCHLD 子进程回收。
1173         1)wait()--------阻塞等待子进程结束;
1174         2)waitpid()--------设置不阻塞(第二个参数WNOHANG)。如果这样设置,要设置轮询,才能将子进程回收。
1175         3)信号捕捉---子进程回收;
1176 
1177     3.注意: 
1178         1)子进程继承了父进程的 信号处理动作和 信号屏蔽字(mask),但并没有继承父进程的 未决信号集(sigpending)。
1179         2)注册信号捕捉函数的位置,和与其他进程发出信号的配合(必须在捕捉信号发出之前完成注册)。
1180         3)在捕捉函数中回收子进程(多个),一定要用循环,才可以做到,一次进入回收多个子进程;否则,会有子进程遗漏,未被回收,造成僵尸进程。
1181 
1182         “参考代码”
1183 
1184 七。信号传参:
1185     1.int sigqueue(pid_t pid, int sig, const union sigval value); 成功返回0; 失败-1;
1186         sigqueue 向指定进程发送信号的同时携带参数(即它的第三个参数);
1187         "注意:携带的数据。如果携带的是 地址:需注意,不同进程之间虚拟地址空间各自独立,将当前进程地址传递给另一进程没有实际意义"
1188     2.捕捉信号传参
1189         1)用该函数捕捉信号传递的数据: int sigaction(int signum, const struct sigaction* act, struct sigaction* oldact);    
1190         2)当注册信号捕捉函数,希望获得更多关于该信号的信息,不应使用 sa_handler, 而应使用 sa_sigaction。 但同时 sa_flags 必须指定SA_SIGINFO.
1191             siginfo_t 是一个成员十分丰富的结构体,携带者该信号的各种相关信息。
1192             
1193 八。中断系统调用
1194     1.慢速系统调用(造成当前系统永久阻塞的)
1195         1)如read, write, wait, pause, sigsuspend, 都是慢速系统调用 ;
1196         2)概念: 可能使进程永远阻塞的一类系统调用;如果再阻塞期间收到一个信号,该系统调用被中断,不再继续执行;也可以通过设置,使其中断后再重新启动。
1197         3)特点:慢速系统调用被中断的相关行为,实际上就是pause的行为: 如read()
1198             1) 信号不能被屏蔽, 执行默认动作, 忽略;
1199             2)信号的处理方式必须被捕捉;    
1200             3)中断后返回-1, 设置errno为 EINTR(表示:“被信号终端”);
1201 
1202         4)设置sa_flags 参数来决定它终端后的行为:
1203                 0           表示被捕捉的信号,在捕捉函数执行期间被自动屏蔽
1204                 SA_NODEFER    表示被捕捉的信号,在捕捉函数执行期间不被自动屏蔽
1205                 SA_INTERRURT 表示慢速系统调用,若被信号打断,不再重启
1206                 SA_RESTART  表示慢速系统调用,若被信号打断,重启 
1207                 SA_SIGINFO  表示捕捉函数是3参的sigaction,需指定为这个宏名
1208 
1209     2.其他系统调用:
1210         如Lgetpid(), fork();
1211 
1212 十。终端
1213     1.字符终端(虚拟终端),图形终端(伪终端),网络终端(伪终端)。
1214     2.线路规程(line discipline):
1215         像一个过滤器,对于某些特殊字符并不是让它直接通过,而是做特殊处理,比如ctrl+c,对应的字符并不会被用户程序read读到,而是被线路规程截获,
1216         解释称SIGINT信号发送给前台进程,通常会使该进程停止。线路规程应该过滤哪些字符和做哪些特殊处理是可以配置的。
1217     3.在终端设备(如键盘)上输入内容进入进程的顺序:
1218         终端设备--》终端设备驱动--》line discipline(线路规程过滤)---》系统调用(普通内容)---》用户进程
1219                                                                  ---》内核特殊字符(解释称信号)--》内核 前台进程。;
1220     4.一套伪终端由一个主设备(PTY Master)和一个从设备(PTY Slave)组成;主设备在概念上相当于键盘、显示器,只不过他不是一个真正的硬件而是一个内核模块;
1221         操作它的也不是用户,而是另外一个内核模块。        网络终端或图形终端窗口的shell进程以及它启动的其他进程都认为自己的控制终端是伪终端从设备。                
1222 
1223 十一。进程组        
1224     pid_t getpgid(pid_t pid);            返回指定进程组的ID
1225     pid_t getpgrp(void);                 返回调用函数进程组的ID
1226     int setpgid(pid_t pid, pid_t pgid);    设置某个进程的进程组ID; 成功返回0;失败-1;
1227     1.当父进程创建子进程时,默认子进程与父进程属于同一进程组。其进程组ID==父进程的ID。
1228     2.特性:
1229         1)进程组ID == 组长ID
1230         2)进程组的生命周期: 只要进程组中有一个进程存在,该进程组就存在,"与组长进程是否终止无关"。
1231                         进程组创建到最后一个进程离开(终止或转移到另一个进程组)。
1232     3.一个进程可以为自己或子进程设置进程组ID。
1233     
1234     4.修改一个进程的进程组ID需注意:
1235         1)如改变子进程为新的组,应在fork后,exec前。
1236         2)权限问题。非root用户只能改变自己和它创建的子进程的进程组。
1237 
1238 十二。会话。
1239     pid_t getsid(pid_t pid);获取该进程的会话ID(如果查看当前进程的会话ID,参数为0就行。自己测试吧)。成功:返回会话ID; 失败:返回-1,并设置errno .
1240     pid_t setpid(void); 创建一个会话,以自己的ID为新会话的ID,同时也会成为一个新的进程组。 成功:返回调用进程的ID;失败,-1;
1241     1.会话:进程组的集合。
1242     创建会话应注意:
1243         1.创建会话的进程不能是进程组组长;同时创建的进程变成新会话首进程(即组长)。
1244         2.创建完后该进程成为新进程组的组长进程。
1245         3.需要有root权限;(ubauntu不需要)
1246         4.新会话丢弃原有的控制终端,该会话没有控制终端(也就无法与用户进行交互)。
1247         5.该调用进程是组长进程,则出错返回。(与1 相同)。
1248         6.新建会话时,先调用fork,父进程退出;子进程创建会话(调用setsid()函数)。
1249         
1250     "注意:子进程不会随着成为新的进程组组长,而其父进程发生改变。"
1251 
1252 
1253 十三:守护进程:
1254     1.Daemon(精灵)进程,即守护进程。
1255         特征:  1.位于Linux后台,服务进程。
1256                 2.独立于控制终端(即没有控制终端),周期性的执行某任务,等待某事件
1257                 3.不受用户的注销、登录而终止;(注意:不是关机)。
1258                 4.通常采用以d结尾的名字。
1259                 
1260     2.创建守护进程的模型:
1261         1.创建子进程,父进程退出
1262             所有工作在子进程中进行,形式上脱离了控制终端。
1263         2.在子进程中创建新会话
1264             setsid()函数
1265             使子进程完全独立出来,脱离控制;
1266 
1267         3.改变当前目录为根目录;
1268             chdir()函数;
1269             目的:防止占用可卸载的文件系统。
1270             也可换成其他目录,只要该目录稳定,不会被卸载(当时卸载的优盘目录);
1271 
1272         4.重设文件掩码;
1273             umask()函数:
1274             目的: 防止继承的文件创建屏蔽字拒绝某些权限;
1275                    增加守护进程的灵活性;
1276 
1277         5.关闭文件描述符
1278             继承的打开不会用到,浪费系统资源,无法卸载。
1279 
1280         6.执行守护进程核心工作
1281 
1282         7.设置守护进程退出。
1283 
1284 "===信号========================================================================================================================"
1285 
1286 
1287 "===线程========================================================================================================================"
1288 
1289 一。线程概念
1290     1.什么是线程
1291         1)线程:LWP(light weight process):轻量级的进程,拥有独立的PCB,共享进程地址空间,Linux下线程是最小的执行单位。
1292         
1293     2.线程与进程的区别、联系
1294         进程独享地址空间;  线程共享。
1295         进程是最小的分配资源单位;  线程是最小的执行单位。
1296         各自有独立的PCB。
1297 
1298     3.内核实现线程的原理:
1299         1.创建线程与创建进程类似;调用底层函数clone();
1300         2.有不同的PCB,但线程共享进程地址空间---》PCB各线程持有相同页目录---》三级映射一致。
1301         3.以每个线程的LWP号, 作为内核分配执行单位的依据。
1302         4.线程的ID != LWP;
1303         5.线程是寄存器与栈的集合。
1304 
1305     3.线程之间共享、不共享那些东西
1306         共享:
1307             1)文件描述符; 2)信号的处理方式; 3)用户ID和组ID;
1308             4)工作目录位置; 5).text/.data/.bss/.ordata/heap-----》用户空间(0-3G ,栈空间除外) ;
1309         不共享:    
1310             1)线程ID; 2)用户stack(栈空间);   3)内核stack(栈空间);
1311             4)信号屏蔽字;        5)errno变量;      6) 线程优先级
1312 
1313     4.线程的优缺点
1314         优点: 
1315             1)线程间通信可以通过----全局变量 。(进程间不共享全局变量 --- mmap 映射)
1316             2)开销小,节省空间
1317             3)程序的并发性提高。
1318 
1319         缺点:
1320             1)第三方库函数pthread,稳定性差;
1321             2)gdb调试工具支持不好。
1322             3)信号支持好(一般不要在线程中使用信号)。
1323 
1324 
1325 二。线程的控制原语:【重点】
1326     1.创建线程:
1327         int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);
1328         参数一:    传出参数,创建的线程ID;
1329         参数二:    线程属性,一般采用默认的(用 NULL)。
1330         参数三:    子线程的操作函数。
1331         参数四:    子线程操作函数的参数。
1332         返回值: 成功:0 ; 失败:错误号;
1333 
1334     2.获取线程的ID
1335         pthread_t pthread_self(void);
1336         参数:无u;
1337         返回值:    成功:线程ID号。  失败:无。
1338 
1339     3.将当前线程退出:
1340         void pthread_exit(void *retval);
1341         参数一:线程退出值(void*类型); NULL:无退出值。
1342         返回值:无;
1343         对比:
1344             return :     返回到调用者那里去;
1345             exit() :        进将当前进程退出;
1346             pthread_exit();    将当前线程退出;
1347 
1348     4.阻塞等待子线程退出,获取退出状态。
1349         int pthread_join(pthread_t thread, void** retval);
1350         参数一:      回收的线程ID;
1351         参数二:    被回收线程退出值。(NULL 表示不关心线程退出值)
1352         返回值:    成功:0;  失败:错误号。
1353 
1354     5.实现线程分离:
1355         int pthread_detach(pthread_t thread);
1356         参数一:待分离的线程ID;
1357         返回值:成功:0; 失败:错误号。
1358 
1359     6.杀死(取消)线程。
1360         int pthread_cancel(pthread_t thread); 
1361         参数一:欲杀死的线程ID;
1362         返回值:成功:0; 失败:错误号。
1363         注意:调用该函数时,欲杀死的线程必须调用系统调用函数(如:write,printf);以使线程进入内核,到达 记录点/取消点。
1364             或者调用检查函数(。。test)等会查一下;
1365         终止线程的方式:return;
1366                         pthread_exit();
1367                         pthread_cancel();
1368 
1369     7.比较两个线程
1370         int pthread_equal(pthread_t t1, pthread_t t2);
1371         参数一、二:比较的两个线程ID号。
1372 
1373     4.线程、进程控制原语对比;
1374             线程                               进程
1375         pthread_creadte();                         fork();
1376         pthread_self();                            getpid()---getppid()---getpgid()---getsid();
1377         pthread_join();                            wait()---waitpid();
1378         pthread_exit();                            exit()----return;
1379         pthread_cancel();                        kill();
1380         pthread_detach();---分离
1381         pthread_equal();----比较
1382 
1383 三。NPTL
1384     pthread库版本;
1385     查看:getconf GNU_LIBPATHREAD_VERSION
1386     gcc编译指定 ---lpthread 选项。
1387 
1388 四。线程的属性【扩展】
1389     可在创建线程之前设置修改其默认属性:
1390     pthread_attr_t 结构体内容:1)线程分离状态; 2)线程栈空间大小; 3)线程警戒区大小;  4)线程栈低地址;。。。。。
1391 
1392     1.属性初始化
1393         pthread_attr_init();
1394         参数:attr:(传出参数),待初始化的结构体。
1395         返回值:成功;0 ; 失败:错误号;
1396 
1397     2.销毁自设属性;
1398         pthread_attr_destroy(); 使用时机: 线程创建完毕。
1399 
1400     3.线程分离状态:
1401         1) int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate); 获取线程分离状态。
1402             参数一:attr:线程属性结构体指针
1403             参数二:detachstate(传出参数): PTHREAD_CREATE_DETACHED(分离);PTHREAD_CREATE_JOINABLE(分离);
1404             返回值:成功;0; 失败:错误号;
1405         2)int pthread_attr_getdetachstate(pthread_attr_t *attr, int *detachstate);    设置线程分离状态。
1406 
1407     4.线程栈地址:
1408         int pthread_attr_getstack(pthread_attr_t *attr, void **stackaddr, size_t *stacksize);    获取线程栈地址
1409         参数一:attr:        线程属性结构体指针
1410         参数二:stackaddr:(传出)    获取线程栈地址
1411         参数三:stacksize:(传出)    获取线程栈大小。
1412         返回值:成功;0;失败:错误号。
1413         int pthread_attr_setstack(pthread_attr_t *attr,void *stackaddr, size_t stacksize); 设置线程栈地址
1414 
1415     5.线程栈大小
1416            int pthread_attr_getstacksize(pthread_attr_t *attr, size_t *stacksize);获取线程栈大小
1417 
1418         int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize); 设置线程栈大小
1419         参数一:attr:        线程属性结构体指针
1420         参数二:stacksize:    设置线程栈大小
1421         返回值:成功;0;失败:错误号。
1422 
1423 
1424 五。线程使用注意事项
1425     1.退出线程应使用 pthread_exit();
1426     2.避免僵尸线程的产生:
1427         1)pthread_join(); 回收子线程。
1428         2)pthread_detach();分离线程。
1429         3)指定分离属性,再pthread_create();创建线程。
1430     3.线程共享进程地址空间:malloc和mmap内存可被其他线程释放。
1431     4.多线程中应避免使用fork(),除非马上exec;多线程fork()后,产生的进程中只含有调用fork函数的线程,其他线程均退出。
1432     5.线程主函数中,应该避免返回局部变量地址值。
1433     6.避免多线程和信号混用。
1434 
1435 
1436 
1437 "===线程同步、进程同步========================================================================================================================"
1438 一。同步、线程同步概念
1439     1.生活中的同步为: 同时起步,协调一致。
1440     2.线程中的同步为: 协同步调,指定访问数据的先后顺序。
1441     3.所有多个控制流,共同操作同一共享数据的场景,都需要同步机制。
1442     4.数据混乱的原因:(产生与时间有关的错误)
1443         1)资源共享(独享资源则不会)
1444         2)调度随机
1445         3)线程间缺乏必要的同步机制
1446             其中前两者不可改变,这是多线程并发得以实行的必要条件,那么就需要在线程间加上同步机制。
1447 
1448 二。线程同步的方法
1449     1.互斥量(互斥锁:mutex)------》建议锁(协同锁)
1450         1)互斥量特征
1451             1)建议锁,不具有强制性。
1452             2)访问共享数据前,加锁,访问结束后立即解锁。
1453             3)线程不按规则访问数据依然可以成功(即不加锁访问);但数据会混乱。
1454 
1455         2)一系列操作函数
1456             1)初始化互斥锁
1457                 1)动态初始化:int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);
1458                     pthread_mutex_t 互斥锁的类型,本质是一个结构体,只有 0 和 1 两个值。
1459                     参数一:互斥锁, 
1460                     参数二:互斥锁的类型(进行修改,可以在进程间使用。)
1461                     restrict:关键字;只用于限制指针:告诉编译器,所有修改该指针指向内存中的操作,只能通过本指针完成;
1462                                         不能通过本指针之外的其他变量和指针操作。
1463                 2)静态初始化:pthead_mutex_t muetx = PTHREAD_MUTEX_INITIALIZER;
1464 
1465             2)加锁:
1466                 int pthread_mutex_lock(pthread_mutex_t *mutex);
1467                 可理解为将 mutex--;
1468                 说明:尝试加锁,如果加锁不成功,线程阻塞,阻塞到持有该互斥量的其他线程解锁为止。
1469             3) 解锁:
1470                 int pthread_mutex_unlock(pthread_mutex_t *mutex);
1471                 可理解为将 mutex++;
1472                 说明:主动解锁,同时将阻塞在该锁上的所有线程全部唤醒;至于哪个线程先被唤醒,取决于优先级、调度。默认:先阻塞、先唤醒。
1473             4)    尝试加锁
1474                 int pthread_mutex_trylock(pthread_mutex_t *mutex);
1475                 说明: lock加锁失败会阻塞,等待所释放;  trylock:加锁失败直接返回错误号,不阻塞;
1476             5)销毁互斥锁
1477                 int pthread_mutex_destroy(pthread_mutex_t *mutex);
1478                 说明:程序结束时,要销毁使用的互斥锁。
1479         "在访问共享资源前加锁,访问结束后立即解锁,锁的 粒度 越小越好。"
1480 
1481     2.死锁--------------------不是线程同步的一种方式,而是一种现象。
1482         1)死锁一。(对同一个互斥量加锁两次。)
1483             连续加锁,阻塞等待。
1484             示例:
1485     pthread_mutex_t mutex;                        //定义一个全局互斥锁变量。
1486     void *do_sth(void* arg)
1487     {
1488         srand((unsigned int) time(NULL));
1489         while(1)
1490         {
1491             pthread_mutex_lock(&mutex);            //子线程加锁
1492             printf("hello ");
1493             sleep(rand()%3);
1494             printf("world\n");
1495             pthread_mutex_unlock(&mutex);        //子线程解锁
1496         }
1497 
1498         pthread_exit(NULL);                            //结束子线程
1499     }
1500     int main(void)
1501     {
1502         pthread_t ptid;
1503         srand((unsigned int) time(NULL));                //随机数的种子
1504         pthread_mutex_init(&mutex, NULL);                //互斥锁动态初始化
1505 
1506         pthread_create(&ptid, NULL, do_sth, NULL);        //创建子线程
1507         while(1)
1508         {
1509             pthread_mutex_lock(&mutex);        //主控线程第一次对数据加锁
1510 
1511             printf("HELLO ");
1512             sleep(rand()%3);                //这个睡眠额度作用是:即使此时CPU空出来,但由于另一个线程没有拿到锁,还是无法进行输出。
1513             printf("WORLD\n");   
1514             pthread_mutex_lock(&mutex);     //死锁:对同一互斥量连续加锁两次/多次;造成线程阻塞。
1515             pthread_mutex_unlock(&mutex);   //数据操作完后立即解锁,使锁的粒度最小。
1516         }
1517         pthread_mutex_destroy(&mutex);        //程序结束,销毁使用的互斥锁。
1518         pthread_join(ptid, NULL);            //回收子线程。
1519          pthread_exit(NULL);                    //主控线程退出
1520     } 
1521 
1522         2)死锁二。(多个共享数据,多把锁。持有锁A的线程1请求锁B,持有锁B的线程2请求锁A)
1523                     更极端的情况:震荡。
1524                 解决方法:    1.当无法获取所有锁的时候,放弃已经掌握的锁
1525                               2.规定 不同线程 获取 共享数据 的顺序(即规定获取锁的顺序)。
1526 
1527     //两组共享的数据及其配套锁
1528     typedef struct recond_m
1529     {
1530         int data_m;
1531         pthread_mutex_t mutex_m;
1532     }recond_m;
1533 
1534     typedef struct recond_n
1535     {
1536         int data_n;
1537         pthread_mutex_t mutex_n;
1538     }recond_n;
1539 
1540     recond_m data_1;                                    //定义的两个全局共享变量
1541     recond_n data_2;    
1542     void *do_sth(void* arg)                                //子线程执行逻辑
1543     {
1544         srand((unsigned int) time(NULL));                //随机数的种子
1545         while(1)
1546         {
1547             //加锁顺序: 先 data1, 再data2;
1548             pthread_mutex_lock(&data_1.mutex_m);
1549             data_1.data_m = 9;                            //对全局变量修改
1550             printf("zi xian data_m:%d\n ",data_1.data_m);
1551             pthread_mutex_lock(&data_2.mutex_n);        
1552             data_2.data_n = 9;
1553             printf("zi xian data_n:%d\n ",data_2.data_n);
1554 
1555             pthread_mutex_unlock(&data_2.mutex_n);        //解锁两个共享数据
1556             pthread_mutex_unlock(&data_1.mutex_m);
1557         }
1558 
1559         pthread_exit(NULL);
1560     }
1561 
1562     int main(void)
1563     {
1564         pthread_t ptid;
1565         srand((unsigned int) time(NULL));
1566         //动态初始化两把锁
1567         pthread_mutex_init(&data_1.mutex_m, NULL);
1568         pthread_mutex_init(&data_2.mutex_n, NULL);
1569 
1570         pthread_create(&ptid, NULL, do_sth, NULL);            //创建子线程
1571         while(1)
1572         {
1573             //主控线程 : 先锁 data2; 再锁data1;
1574             pthread_mutex_lock(&data_2.mutex_n);
1575             data_2.data_n = 6;
1576             printf("zhu kong data_n:%d\n ",data_2.data_n);
1577             sleep(1);                                        //空出CPU资源, 子线程加锁。
1578             pthread_mutex_lock(&data_1.mutex_m);
1579             data_1.data_m = 9;
1580             printf("zhu kong data_m:%d\n ",data_1.data_m);
1581 
1582             pthread_mutex_unlock(&data_1.mutex_m);
1583             pthread_mutex_unlock(&data_2.mutex_n);
1584             
1585         }
1586 
1587         pthread_mutex_destroy(&data_1.mutex_m);
1588         pthread_mutex_destroy(&data_2.mutex_n);
1589         pthread_join(ptid, NULL);
1590         pthread_exit(NULL);
1591     }
1592 
1593     2.读写锁(读写锁只有一把)
1594         1)读写锁特性
1595             1.写独占,读共享。
1596             2.写锁的优先级高于读锁。
1597             "详解:三种情况"
1598                 1)写模式加锁:解锁前,所有对该锁加锁的进程都会被阻塞。
1599                 2)读模式加锁,解锁前,如果其他所有线程以读模式加锁会成功,以写模式加锁会失败。
1600                 3)读模式加锁,解锁前,其它线程中既有写模式加锁,也有读模式加锁,所有线程阻塞;解锁后,优先满足写模式的加锁。
1601 
1602         2)一系列操作函数
1603             就不赘述了;和上述一样。
1604         3)使用场景:
1605             适合于对数据结构读的次数远大于写的次数。
1606 
1607         3)读写锁优点:
1608             当读线程 多于 写线程时,可以提高数据的访问效率。
1609 
1610     3.条件变量
1611         1)条件变量的特性
1612             1.条件变量本身不是锁,但是依然可以造成线程阻塞;通常与互斥锁配合使用。
1613             
1614         2)一系列操作函数(拿出其中的三个特殊函数)
1615             int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex); 
1616             说明:阻塞等待一个条件变量。隐喻着在此函数调用前,数据已完成加锁操作;pthread_mutex_lock(&mutex)。
1617             该函数有三步:
1618                 1)阻塞等待条件 cond 满足;
1619                 2) 条件未满足时,解锁已经获得的锁。
1620                 3)当条件满足时,被唤醒,接触阻塞再次尝试加锁互斥量。为一个原子操作。
1621 
1622             int pthread_cond_timedwait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex, const struct timespec *restrict abstime);
1623             说明:指定时间后尝试调用该函数。符合条件则运行;不符合条件,不阻塞,返回错误号。
1624             参数三:绝对时间。
1625 
1626             int pthread_cond_signal(pthread_cond_t *cond);
1627             唤醒至少一个阻塞在条件变量上的线程。(实际上也就只唤醒一个,但是man手册中,没有这样写)
1628             int pthread_cond_broadcast(pthread_cond_t *cond);
1629             唤醒全部阻塞在该条件变量上的线程。
1630 
1631         3)生产者、消费者模型
1632                 生产者线程:
1633                 消费者线程:
1634                 公共区(共享资源):
1635                 对公共区添加条件变量,互斥锁,对数据加以保护。
1636         
1637         4)条件变量的优点:
1638             相较于直接使用互斥量,使用它只有当条件满足时,各线程才会竞争,提高了程序间的效率。
1639 
1640     4.信号量 -------》本质:结构体。
1641         1)特性:
1642                 1.进化版的互斥锁。。(1-> N);
1643                 "2.信号量的初值,决定了占用信号量的线程的个数。    "
1644         2)一系列操作函数
1645             1)int sem_init(sem_t *sem, int pshared, unsigned int value);
1646                 说明:初始化一个信号量
1647                 参数一:信号量的指针。
1648                 参数二:pshared 取0用于线程间,取非0 用于进程间.
1649                 "参数三:信号量的初值。"
1650             2)int sem_destroy(sem_t *sem);
1651                 说明:销毁一个信号量
1652             3)int sem_wait(sem_t *sem);        (类比pthread_mutex_lock());
1653                 说明:信号量加锁--。
1654                 详细:信号量大于0,则信号量--;  信号量等于0,则线程阻塞等待。
1655             4) int sem_post(sem_t *sem);        (类比pthread_mutex_unlock());
1656                 说明:信号量解锁++;
1657                 详细:信号量小于最大值,则信号量++;信号量等于最大值,则线程阻塞等待。
1658             5)int sem_trywait(sem_t *sem);
1659                 说明:信号量尝试加锁,不阻塞。
1660             6)int sem_timedwait(sem_t *sem, const struct timespec *abc_timeout);
1661                 说明:指定时间后尝试对信号量加锁,成功则运行,不成功,返回错误号,不阻塞。
1662                 参数二:绝对时间。
1663                 例子:定时1秒; time_t cur = time(NULL);    //获取当前时间。
1664                                 struct timespec t_time;        //定义结构体变量
1665                                 t_time.tv_sec = cur + 1;    //定时1秒
1666                                 t_time.tv_nsec = 0;
1667                                 sem_timedwait(&sem, &t_time);
1668                                 
1669         3)生产者、消费者模型
1670 
1671 三。进程间同步
1672     1.信号量
1673         1.
1674     2.互斥量(mutex)
1675         1.进程间也可以使用互斥锁,来达到同步的目的。但是在初始化之前,应该修改一下其属性。
1676         2.属性修改函数:
1677             pthread_mutexattr_t mattr 类型;        用于定义mutex锁的 属性。
1678             int pthread_mutexattr_init(pthread_mutexattr_t *mattr)  函数;    初始化一个mutex属性对象;
1679             int pthread_mutexattr_destroy(pthread_mutexattr_t *mattr)    函数;     销毁mutex属性对象,(非销毁锁);
1680             int pthread_mutexattr_setpshared(pthread_mutexattr_t *attr, int pshared);    修改mutex属性。
1681             参数二:pshared取值:
1682                 线程锁:PTHREAD_PROCESS_PRIVATE (mutex 的默认属性即为线性锁,进程间私有)
1683                 进程锁:PTHREAD_PROCESS_SHARED 
1684 
1685     3.文件锁
1686         int fcntl(int fildes, int cmd, ...);函数;
1687         参数一:文件描述符;
1688         参数二:
1689                 F_SETLK(struct flock*) 设置文件锁 (trylock);;
1690                 F_SETLKW(struct flock*)设置文件锁 (lock)W -->wait;
1691                 F_GETLK(struct flock*) 获取文件锁  ;
1692 
1693         参数三:struct flock 结构体内容;
1694                 1)l_type:  锁的类型: F_RDLCK(读锁);  F_WRLCK(写锁);  F_UNLCK(解锁);
1695                 2)l_whence:偏移位置:SEEK_SET;  SEEK_CUR;  SEEK_END;
1696                 3) l_start: 起始偏移:
1697                 4) l_len:    长度 ; 0 表示整个文件加锁。
1698                 5)l_pid;    持有该锁的进程ID;  仅限F_GETLK中使用。 
1699 
1700 
1701 "===线程========================================================================================================================"
1702 
1703 
1704 "===网络编程========================================================================================================================"
1705 
1706 
1707 "===网络编程之理论基础========================================================================================================================"
1708 
1709 一。协议概念
1710     1.协议:
1711 
1712     2.典型协议:
1713         传输层:     常见协议:TCP / UDP;
1714         应用层:     常见协议:HTTP; FTP;
1715         网络层:     常见协议:IP; ICMP; IGMP;
1716         网络接口层:常见协议:ARP; RARP;
1717 
1718         ARP协议:正向地址解析协议,通过已知的IP,寻找对应主机的MAC地址。
1719         RARP协议:反向地址转换协议,通过MAC地址确定IP地址。
1720         FTP :文件传输协议
1721         TCP :传输控制协议;是一种面向连接的、可靠的、基于字节流的传输层通信协议。
1722         UDP:用户控制协议,是OSI参考模型中一种无连接的传输层协议,提供面向事物的简单不可靠信息传送服务。
1723         IP:因特网互联协议
1724         HTTP:超文本传输协议,是互联网上应用最广泛的一种网络协议。
1725         ICMP:Internet控制报文协议,是TCP/Ip协议族的一个子协议,用于IP主机和路由器之间传递消息。
1726         IGMP:Internet组管理协议,运行在主机和组播路由器之间。
1727 
1728 二。B/S ; C/S 模型结构【即网络应用层设计模式】
1729     1.C/S模型:(客户机/服务器模式)
1730         如:实际应用:QQ; 
1731         两个数据端:客户端(client)/服务器(server); 
1732         该模型的优点:
1733                 1.缓存数据到本机,提高通信效率。
1734                 2.协议的选择灵活。-------自定义协议,或者对现有协议进行裁剪。
1735         该模型的缺点:
1736                 1.对用户而言,安全性较低(因为在用户端安装了应用程序(即客户端))。
1737                 2.开发量大。
1738                 3.受操作平台限制。
1739         使用场景:
1740                 1.当需要自定义协议
1741                 2.对数据要求高。
1742 
1743     2.B/S模型:(浏览器/服务器模式)
1744         借助浏览器完成通信,用户不需要在主机上安装应用程序。如(当年的偷菜游戏,即网页游戏。)
1745         该模型的优点:
1746                 1.不受操作平台限制;
1747                 2.开发量小
1748 
1749         该模型的缺点:
1750                 1.因为数据不能缓存到本地,所以大型的网络游戏不能使用。
1751                 2.协议选择相对来说不太灵活。(浏览器的开发要求必须满足)。
1752 
1753         使用场景:
1754                 1.对数据要求不太高,
1755                 2.跨平台方便的
1756 
1757 
1758 三。分层模型"【重点, 必须掌握】"
1759     1.OSI 七层模型
1760         物数网传会表应:【物理层、数据链路层、网络层、传输层、会话层、表示层、应用层。】
1761 
1762     2.TCP/IP 4层模型
1763         网网传应:【网络接口层(由物理层和数据链路层组成,也有人叫做数据链路层)、网络层、传输层 、应用层】。
1764         4层模型与7层模型,互相对应。
1765         网络接口层协议:ARP协议和RARP协议。        网络层协议:IP协议、ICMP协议、IGMP协议。
1766         传输层协议:TCP/UDP协议(主要是这两种);   应用层协议:HTTP协议、 FTP协议。
1767         TCP 是一种面向连接的、可靠的协议,有点像打电话。也就是说TCP传输的双方首先要建立联接,之后由TCP协议保证数据收发的可靠性,数据包丢失重发。
1768         UDP是无连接的传输协议,不保证可靠性,有点像寄信。使用UDP协议的应用程序需要自己完成丢包重发、消息排序等工作。
1769 
1770 四。协议格式
1771     1.数据包的封装【网络环境中数据通信过程】。
1772         1)(((((数据)应用层)传输层)网络层)数据链路层); 
1773         数据包装完后,才可以在网络上进行传输;
1774 
1775         2)传输层及其以下由内核控制,应用层由用户控制。不同主机之间,数据在网络中的发送,应用层数据通过协议栈发到网络上时,每层协议都要加上一个数据首部,称为封装。
1776 
1777     2.以太网帧格式:
1778         ARP数据报格式;  借助 IP----》获取mac地址。
1779         目的地址,源地址,类型, 数据, CPC(校验码)。
1780         其中 源地址和目的地址是 网卡的硬件地址(即MAC地址),长度是48位;
1781 
1782         2)以太网帧中数据长度最小为46字节,最大为1500字节。
1783 
1784 
1785     2.IP段格式
1786         4位首部长度, 4位版本号, 16位总长度, 8位TTL(记录数据包的跳数), 32位源IP, 32位目的IP。
1787         
1788 
1789     3.TCP数据包格式:(面向连接的,复杂)
1790         16位源端口号, 16位目的端口号。 4位首部长度,6位标志位; 16位窗口大小; 32位序号; 32位确认序号。
1791 
1792     4.UDP数据包格式     (无连接的)
1793         16位源端口号; 16位目的端口号。  最大包括:2^16 = 65535;
1794         端口号:在一台主机上唯一标识一个进程。    != pid; 应用与网络
1795         IP地址:在网络中唯一标识一台主机。
1796         IP地址+端口号; 在网络环境中唯一标识一个进程。------》socket
1797 
1798         HTTP 服务默认 TCP 协议的 80 端口; 
1799         FTP 服务默认 TCP协议的 21 端口;
1800         TFTP 协议默认 TCP协议的 69 端口。  
1801         
1802     5.NET映射机制:映射表
1803         1.私有IP:仅在局域网有效。        (允许重复)
1804         2.公网IP:在整个网络环境可见。    (不允许重复,即冲突)
1805         3.路由器内部,NAT映射表。借助私有IP和端口号 -------- 共有IP和标号。
1806 
1807     6.打洞机制:
1808         借助 公网服务器, 将两台私有IP的主机,直接相连,完成数据的直接传递。
1809 
1810 五。TCP 的3次握手建立连接、4次握手断开连接。"【重点】"
1811     三次握手建立连接:
1812         主动发起连接一方,发送请求:SYN(请求建立连接);  
1813         被动接受链接一方,回复:ACK(应答),SYN();  
1814         主动发起链接一方,回复:ACK(应答,建立连接成功);
1815 
1816     四次握手断开连接:
1817         主动关闭连接一方1: FIN
1818         被动关闭连接一方1: ACK
1819         主动关闭连接一方2: FIN
1820         被动关闭连接一方2: ACK
1821 
1822 mss(最大报文长度): 与下面的MTU大小相关。
1823     
1824 六。MTU概念
1825     MTU:最大传输单元。
1826         在以太网帧格式中为 1500 字节。
1827     (MTU - 数据报头)= mss ;
1828     
1829 七。滑动窗口
1830     给对端提供一个发送数据的总上限值。---------》进行流量控制
1831     最大:65535;因为:在报头中占据16位。
1832 
1833 八:TCP协议格式:
1834     16位源端口, 16位目的端口,32位序列号, 32位确认序列号, 16位窗口大小, 标志位(SYN, ACK, FIN); 
1835     需要记住使用的就上面这些;
1836     
1837 九。TCP状态转换图:"【重点】"
1838 
1839 
1840 
1841 十:2MSL
1842         1)查看:net 
1843 
1844 
1845 十一。端口复用:
1846 
1847 十二:半关闭:
1848         1)主动关闭链接一方,处于FIN_WAIT_2.  4次握手---完成两次。
1849         2)
1850 "===UDP======================================"
1851 1.UDP 协议格式
1852     主要针对不稳定的网络层,做传输行为。  TCP--完全弥补 97% ;UDP---完全不弥补。
1853     TCP 和UDP 优缺点:
1854         网络中TCP一次传输的多组数据路径是相同的(如果网络中的路由器没有发生变化);
1855         网络中UDP一次传输的多组数据路径是不同的(导致:)。
1856         
1857         TCP:面向连接的可靠数据传输。---类比打电话
1858             优点:  1.数据传输可靠;;
1859                     2.稳定:数据内容稳定,流量稳定(mss, 滑动窗口),速率稳定(即线路稳定,在网络中经过相同的路由)。
1860             缺点:  1.效率低,开销大。
1861                     2.速度慢,(因为每次数据传输过去,需要对方回复)
1862             
1863             应用场景:重要文件、大文件。
1864 
1865         UDP:无连接的不可靠报文传递。-----类比发短信            
1866             优点:  1.速度快
1867                     2.实时性高,效率高。
1868             缺点:  1.数据传输不可靠。
1869                     2.稳定性差(每次传输的路径不一样)。
1870 
1871             应用场景:对实时性要求较高:视频聊天、视频电话、分屏广播。
1872 
1873         腾讯:开始:TCP ---后来TCP+UDP(做判断,看适合用哪种);----后来UDP+应用层校验(不丢失数据包)--(这种方式需要自行开发应用层协议。)
1874 
1875 2.UDP C/S 服务器模型
1876     比较差异
1877     TCP:  三次握手建立连接---socket();bind(); accept();read();小写转大写;write();-----service端
1878                             -------socket(); connect();write(); read();
1879     UDP:    无需建立链接---socket(); bind(); recvfrom();
1880 
1881         UDP默认情况下支持 多路IO---高并发服务器。
1882         1.为了使支持情况更好,需要应用层自己封装校验;
1883         2.扩大接收端缓存区大小。使用 setsockopt();
1884 
1885     注意:send(), recv() 只能用于TCP链接的C/S模型。
1886     
1887 3.UDP 广播
1888 
1889 4.UDP 组播
1890 
1891 "上课所用的分屏软件的设计思想:"
1892 server端:
1893     1)服务器端:截屏模块,截取本地屏幕数据。得到图片,(保证人眼的观看,需要1秒24帧)几M。
1894     2)降帧12帧;保证数据发送效率。
1895     3)选用适当的算法进行压缩。 几十KB
1896     4)为进一步提高效率,只将有变化、改动、坐标区域,截取,压缩,发送。
1897 
1898 client端:
1899     1)每台接收的主机 有个客户端,使用和服务器端相同的端口。
1900     2)接收到数据后以相同的算法,逆方式解压缩。
1901 
1902 5.本地套接字:
1903     IPC的一种方法:
1904 
1905 6.常用的开源库(网络编程) 
1906     1.libevent
1907     2.libv
1908 7.服务器压力测试开源库
1909 
1910 8.使用软件:
1911     wareshark;
1912 
1913 "===UDP======================================"
1914 
1915 
1916 
1917 
1918 "===网络编程之理论基础========================================================================================================================"
1919 
1920 
1921 "===网络编程之 socket(套接字) 编程========================================================================================================================"
1922     在网络通信中,socket通信时一定是成对出现的。 
1923     使用的时候必须要指定:IP和端口号 ;socket = IP+port(端口号);
1924 
1925 一。理论基础:
1926     1.网络字节序
1927         大端法:高位存低地址。
1928         小端法:高存高。(x86结构,即个人电脑;微软的主推)
1929 
1930         网络环境中:--------》网络字节序:大端法。
1931         本地环境中:--------》本机字节序:小端法。
1932         所以网络中传输的数据需要进行字节序转换。
1933         "一套操作函数:";
1934         uint32_t htonl(uint32_t hostlong); ----本地--转--网络-- 32; --->IP 地址
1935 
1936            uint16_t htons(uint16_t hostshort); ----本地--转--网络-- 16; --->port号(端口号)
1937 
1938            uint32_t ntohl(uint32_t netlong);  ----网络--转--本地--32;    
1939 
1940            uint16_t ntohs(uint16_t netshort);----网络--转--本地--16;
1941 
1942     2.IP地址转换
1943         因为本地的IP地址为字符串型,适合人类查看。
1944         网络上的IP为 unsigned int 型。
1945         str IP <-----> 网络字节序IP
1946         int inet_pton(int af, const char *src, void *det );
1947             af:      ipv4:AF_INET;
1948                   ipv6:AF_INET6;
1949 
1950             src:  网路字节序的IP地址;
1951 
1952             det:  用来储存转化后的IP地址。
1953 
1954 
1955         const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);
1956 
1957             af:  ipv4: AF_INET;
1958 
1959                  ipv6: AF_INET6;
1960 
1961             src:网络字节序的IP地址。
1962 
1963             dst:用来存储转化后的IP地址。
1964 
1965             size: dst的大小。
1966             
1967 
1968     3.socketaddr地址结构
1969         定义变量: struct sockaddr_in  saddr;
1970 
1971         初始化:
1972 
1973                struct sockaddr_in {
1974                sa_family_t    sin_family; /* address family: AF_INET */
1975                in_port_t      sin_port;   /* port in network byte order */
1976                struct in_addr sin_addr;   /* internet address */
1977             };    
1978 
1979 二。C/S模型实现的 TCP 客户端服务器程序"【重点, 必须掌握】"
1980     socket----套接字:
1981         本质:内核缓冲区(发送,接收。)
1982         "进行通信的时候,是成对出现的。双向全双工"
1983 
1984     1.创建一个套接字:
1985            SOCK_DGRAM --------报式协议        UDP 协议
1986     protocol:0 流式默认: TCP
1987                 报式默认: UDP
1988     返回值:成功则指向新创建的socket的文件描述符; 失败:返回-1,设置errno.
1989 
1990     2.int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);绑定服务器的:IP, port(端口号);到套接字上。
1991         参一:sockfd :socket文件描述符。
1992         参二:构造出IP地址+端口号;(sockaddr_in 类型)
1993         参三:地址的长度。
1994 
1995     3. int listen(int sockfd, int backlog); 设置同时向服务器发起三次连接的客户端数量(注意:不是客户端链接服务器的数量)  -- 10    ;
1996         参一:socket函数的返回值(即socket文件描述符)
1997         参二:数量上限。
1998         注意:这个函数的作用是:指定连接数的,而不是等待客户端连接的。
1999 
2000     4.int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);  阻塞等待客户端发起连接请求。
2001         参一:socket函数的返回值(即socket文件描述符)
2002         参二:addr:传出参数。返回客户端的地址结构(IP、port)。
2003         参三:addrlen:传入、传出参数。
2004         返回值:返回一个新的fd,指向客户端的套接字。
2005 
2006     5.int connect(int sockfd, const struct aockaddr *addr, socklen_t addrlen);
2007         参一:socket函数的返回值(即socket文件描述符)
2008         参二:传入参数,指定服务器端地址信息,含IP地址和端口号。
2009         参三:传入参数:地址的大小。
2010         返回值:成功;0;失败:-12011 
2012 
2013 三。服务器、客户端封装错误处理模块。
2014     
2015     封装思想:
2016         将系统调用进行 浅封装;
2017         简化程序逻辑 ------首字母大写-------可以直接从代码编写页面进入 man page。
2018 
2019 
2020     read返回值:
2021 
2022         > 0:   返回实际读到的字节数
2023 
2024         == 0:  到达文件末尾。或者管道对端关闭。或者套接字对端关闭。 
2025 
2026         < 0 (-1):
2027 
2028             1. errno == EINTR  ( ECONNABORTED 网络中)
2029 
2030                 慢速系统调用,被信号中断。 应该设置重启。
2031 
2032             2. errno == EAGIAN (或 EWOULDBLOCKED) 
2033 
2034                 待读对象设置非阻塞读,并且此刻没有数据到达。 应该设置重新读取。
2035 
2036             3. error
2037                 perror();  exit();
2038     readn
2039 
2040     readline
2041 
2042 
2043 四。多进程并发服务器
2044     1. 流程: socket() bind()  listen() accept() read ()  write()  close();
2045         详解: 1.创建套接字;  2.定义地址变量,给他赋值IP、port; 3.监听客户端连接请求。  4.在循环阻塞等待链接客户端,一旦有客户端连接成功,就创建子进程。
2046                 5.子进程负责与客户端通信; 6.父进程 继续阻塞等待客户端的链接; 同时注册信号,回收子进程。
2047 
2048     2. 子进程用来跟客户端通信。―― fork() 
2049 
2050     3. 父进程。 监听客户端连接请求。 注册信号捕捉函数,回收子进程。
2051 
2052     4. while (1 ) {
2053 
2054         fd = accept()
2055 
2056         fork();  
2057        }
2058 
2059 
2060 
2061 五。多线程并发服务器
2062     
2063     1. socket() bind()  listen() accept() read ()  write()  close();
2064         详解:1.创建套接字; 2.定义地址变量,给他赋值IP、port; 3。监听客户端连接请求;  4.在循环中,主控线程阻塞等待客户端的链接,一旦有客户端连接成功,就创建子线程,同时分离或专门用一个子线程回收剩余的子线程。
2065                 5.主控线程负责 继续阻塞等待客户端连接, 6.子线程负责与客户端通信。
2066 
2067     2. 子线程用来跟客户端通信。―― pthread_create();
2068 
2069     3. 主线程。  监听客户端连接请求。 子线程1 :pthread_join(); pthread_detach();---修改线程。
2070 
2071     4. 客户端通信---小写--大写: 子线程的主控函数。
2072 
2073 六:多路I/O转接
2074 
2075     .select【*2076         1)一组函数
2077 
2078         2)监听、转换,实现方法、思想
2079 
2080         3)建立流程: socket()  setsockopt()  bing()  listen()  fd_set集合的赋值  select()  FD_ISSET()
2081             accept()   符合条件的描述符通信。
2082 
2083     2.poll
2084         1)一组函数
2085         2)监听、转换,实现方法、思想
2086         3)区别poll、select比较,优缺点;
2087 
2088     3.epoll【**2089         1)一组函数
2090         2)监听、转换,实现方法、思想
2091         3)区别poll、select、epoll比较,优缺点;;
2092 
2093 七。epoll多路IO转接模型
2094     1.设置突破1024文件描述符限制
2095     2.epoll操作函数
2096         epoll_create();
2097         epoll_ctl();
2098         epoll_wait();
2099     3.epoll实现多路IO转接模型;
2100 八.epoll
2101 
2102 
2103 "===网络编程之 socket 编程========================================================================================================================"
2104 
2105 
2106 "===网络编程========================================================================================================================"

 

 1
 AF_INET 和 PF_INET 的区别:

 
  在写网络程序的时候,建立TCP socket: 2 sock = socket(PF_INET, SOCK_STREAM, 0); 3 然后在绑定本地地址或连接远程地址时需要初始化sockaddr_in结构,其中指定address family时一般设置为AF_INET,即使用IP。 4 相关头文件中的定义:AF = Address Family 5 PF = Protocol Family 6 AF_INET = PF_INET 7 在windows中的Winsock2.h中, 8 #define AF_INET 0 9 #define PF_INET AF_INET 10 所以在windows中AF_INET与PF_INET完全一样. 11 12 而在Unix/Linux系统中,在不同的版本中这两者有微小差别.对于BSD,是AF,对于POSIX是PF. 13 理论上建立socket时是指定协议,应该用PF_xxxx,设置地址时应该用AF_xxxx。当然AF_INET和PF_INET的值是相同的,混用也不会有太大的问题。也就是说你socket时候用PF_xxxx,设置的时候用AF_xxxx也是没关系的,这点随便找个TCPIP例子就可以验证出来了。如下,不论是AF_INET还是PF_INET都是可行的,只不过这样子的话,有点不符合规范。 14 [cpp] view plaincopy 15 /* 服务器端开始建立socket描述符 */ 16 / if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1) 17 if((sockfd=socket(PF_INET,SOCK_STREAM,0))==-1) 18 { 19 fprintf(stderr,"Socket error:%s\n\a",strerror(errno)); 20 exit(1); 21 } 22 23 /* 服务器端填充 sockaddr结构 */ 24 bzero(&server_addr,sizeof(struct sockaddr_in)); 25 server_addr.sin_family=AF_INET; 26 //server_addr.sin_family=PF_INET; 27 server_addr.sin_addr.s_addr=htonl(INADDR_ANY); 28 server_addr.sin_port=htons(portnumber); 29 30 31 在函数socketpair与socket的domain参数中有AF_UNIX,AF_LOCAL,AF_INET,PF_UNIX,PF_LOCAL,PF_INET. 32 这几个参数有AF_UNIX=AF_LOCAL, PF_UNIX=PF_LOCAL, AF_LOCAL=PF_LOCAL, AF_INET=PF_INET. 33 但是对于socketpair与socket的domain参数,使用PF_LOCAL系列, 34 而在初始化套接口地址结构时,则使用AF_LOCAL. 35 例如: 36 z = socket(PF_LOCAL, SOCK_STREAM, 0); 37 adr_unix.sin_family = AF_LOCAL;

 

posted on 2016-08-27 23:46  路之遥_其漫漫  阅读(2854)  评论(0编辑  收藏  举报

导航