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 2)查看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等于 10 时 17 行打断点,注意:断点不能设在 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;失败:-1。 2011 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 1.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;
路之遥