Linux Note
C 语言之解析局部变量返回 一般的来说,函数是可以返回局部变量的。 局部变量的作用域只在函数内部,在函数返回后,局部变量的内存已经释放了。因此,如果函数返回的是局部变量的值,不涉及地址,程序不会出错。但是如果返回的是局部变量的地址(指针)的话,程序运行后会出错。因为函数只是把指针复制后返回了,但是指针指向的内容已经被释放了,这样指针指向的内容就是不可预料的内容,调用就会出错。
准确的来说,**函数不能通过返回指向栈内存的指针(注意这里指的是栈,返回指向堆内存的指针是可以的)**。
1. Linux 基础命令
stat命令 用于显示文件的状态信息。stat命令的输出信息比 ls 命令的输出信息要更详细。
1.0 创建用户 创建用户命令两条 :
用户删除命令 :
两个用户创建命令之间的区别 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 # 使用 adduser $ adduser apple 正在添加用户"apple"... 正在添加新组"apple" (1007)... 正在添加新用户"apple" (1007) 到组"apple"... 创建主目录"/home/apple"... 正在从"/etc/skel"复制文件... 输入新的 UNIX 密码: 重新输入新的 UNIX 密码: passwd:已成功更新密码 正在改变 apple 的用户信息 请输入新值,或直接敲回车键以使用默认值 全名 []: 房间号码 []: 工作电话 []: 家庭电话 []: 其它 []: 这些信息是否正确? [Y/n] y # 这样在创建用户名时,就创建了用户的主目录以及密码。 # 默认情况下: # adduser在创建用户时会主动调用 /etc/adduser.conf; # 在创建用户主目录时默认在/home下,而且创建为 /home/用户名 # 如果主目录已经存在,就不再创建,但是此主目录虽然作为新用户的主目录,而且默认登录时会进入这个目录下,但是这个目录并不是属于新用户,当使用userdel删除新用户时,并不会删除这个主目录,因为这个主目录在创建前已经存在且并不属于这个用户。 # 为用户指定shell版本为:/bin/bash
因此 adduser 常用参数选项为:
--home
: 指定创建主目录的路径,默认是在/home目录下创建用户名同名的目录,这里可以指定;如果主目录同名目录存在,则不再创建,仅在登录时进入主目录。
--quiet
: 即只打印警告和错误信息,忽略其他信息。
--debug
: 定位错误信息。
--conf
: 在创建用户时使用指定的configuration文件。
--force-badname
: 默认在创建用户时会进行/etc/adduser.conf中的正则表达式检查用户名是否合法,如果想使用弱检查,则使用这个选项,如果不想检查,可以将/etc/adduser.conf中相关选项屏蔽。
1 2 # 使用 useradd # 注意: 在使用useradd命令创建新用户时,不会为用户创建主目录,不会为用户指定shell版本,不会为用户创建密码。
为用户指定参数的 useradd 命令,常用命令行选项:
1 2 3 4 5 6 7 8 # 解释: # -d “/home/tt" :就是指定/home/tt为主目录 # -m 就是如果/home/tt不存在就强制创建 # -s 就是指定shell版本 $ sudo useradd -d " /home/tt" -m -s " /bin/bash" tt # 修改 tt 密码: $ sudo passwd tt
删除用户命令
userdel
只删除用户:
连同用户主目录一块删除:
相关文件 :
/etc/passwd - 使 用 者 帐 号 资 讯,可以查看用户信息
/etc/shadow - 使 用 者 帐 号 资 讯 加 密
/etc/group - 群 组 资 讯
/etc/default/useradd - 定 义 资 讯
/etc/login.defs - 系 统 广 义 设 定
/etc/skel - 内 含 定 义 档 的 目 录
为组用户增加 root 权限 :
1 2 3 4 5 # 修改 /etc/sudoers 文件,找到下面一行,在 root 下面添加一行,如下所示: # root ALL=(ALL) ALL tommy ALL=(ALL) ALL # 修改完毕,现在可以用 tommy 帐号登录,然后用命令 sudo – ,即可获得 root 权限进行操作。
1.1 ln 软硬链接 硬链接
硬链接说白了是一个指针,指向文件索引节点,系统并不为它重新分配inode。可以用: ln 命令来建立硬链接。
尽管硬链接节省空间,也是Linux系统整合文件系统的传统方式,但是存在一下不足之处:
(1)不可以在不同文件系统的文件间建立链接
(2)只有超级用户才可以为目录创建硬链接。
软链接(符号链接)
软链接克服了硬链接的不足,没有任何文件系统的限制,任何用户可以创建指向目录的符号链接。因而现在更为广泛使用,它具有更大的灵活性,甚至可以跨越不同机器、不同网络对文件进行链接。 建立软链接,只要在 ln 后面加上选项 –s
1 2 $ ln -s abc cde $ ln abc cde
删除链接
1 2 $ rm -rf symbolic_name$ unlink symbolic_name
1.2 find grep xargs 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 # 列出当前目录及子目录下所有文件和文件夹 $ find . # 在/home目录下查找以.txt结尾的文件名 但忽略大小写 $ find /home -iname "*.txt" # 当前目录及子目录下查找所有以 .txt 和 .pdf 结尾的文件 $ find . \( -name "*.txt" -o -name "*.pdf" \) $ find . -name "*.txt" -o -name "*.pdf" # 匹配文件路径或者文件 $ find /usr/ -path "*local*" # 基于正则表达式匹配文件路径 $ find . -regex ".*\(\.txt\|\.pdf\)$" # 同上,但忽略大小写 $ find . -iregex ".*\(\.txt\|\.pdf\)$" # 找出/home下 不是 以 .txt 结尾的文件 $ find /home ! -name "*.txt" # 根据文件类型进行搜索 # 类型参数列表: # f 普通文件 # l 符号连接 # d 目录 # c 字符设备 # b 块设备 # s 套接字 # p Fifo $ find . -type 类型参数 # 基于目录深度搜索 向下最大深度限制为3 $ find . -maxdepth 3 -type f # 搜索出深度距离当前目录至少2个子目录的所有文件 $ find . -mindepth 2 -type f # 根据文件时间戳进行搜索 # UNIX/Linux文件系统每个文件都有三种时间戳: # - 访问时间(-atime/天,-amin/分钟):用户最近一次访问时间。 # - 修改时间(-mtime/天,-mmin/分钟):文件最后一次修改时间。 # - 变化时间(-ctime/天,-cmin/分钟):文件数据元(例如权限等)最后一次修改时间。 $ find . -type f 时间戳 # 搜索最近七天内被访问过的所有文件 $ find . -type f -atime -7 # 搜索恰好在七天前被访问过的所有文件 $ find . -type f -atime 7 # 搜索超过七天内被访问过的所有文件 $ find . -type f -atime +7 # 搜索访问时间超过10分钟的所有文件 $ find . -type f -amin +10 # 找出比 file.log 修改时间更长的所有文件 $ find . -type f -newer file.log # 根据文件大小进行匹配 # 文件大小单元: # b —— 块(512字节) # c —— 字节 # w —— 字(2字节) # k —— 千字节 # M —— 兆字节 # G —— 吉字节 $ find . -type f -size 文件大小单元 # 搜索大于10KB的文件 $ find . -type f -size +10k # 搜索小于10KB的文件 $ find . -type f -size -10k # 搜索等于10KB的文件 $ find . -type f -size 10k # 删除匹配文件 删除当前目录下所有.txt文件 $ find . -type f -name "*.txt" -delete # 根据文件权限/所有权进行匹配 当前目录下搜索出权限为777的文件 $ find . -type f -perm 777 # 找出当前目录下权限不是644的php文件 $ find . -type f -name "*.php" ! -perm 644 # 找出当前目录用户tom拥有的所有文件 $ find . -type f -user tom # 找出当前目录用户组sunk拥有的所有文件 $ find . -type f -group sunk # 借助-exec 选项与其他命令结合使用 # 找出当前目录下所有root的文件,并把所有权更改为用户tom # {} 用于与 -exec 选项结合使用来匹配所有文件,然后会被替换为相应的文件名。 $ find .-type f -user root -exec chown tom {} \; # 找出自己家目录下所有的.txt文件并删除 # -ok 和 -exec 行为一样,不过它会给出提示,是否执行相应的操作。 $ find $HOME /. -name "*.txt" -ok rm {} \; # 查找当前目录下所有.txt文件并把他们拼接起来写入到all.txt文件中 $ find . -type f -name "*.txt" -exec cat {} \;> all.txt # 将30天前的.log 文件移动到old目录中 $ find . -type f -mtime +30 -name "*.log" -exec cp {} old \; # 找出当前目录下所有.txt文件并以 “File:文件名” 的形式打印出来 $ find . -type f -name "*.txt" -exec printf "File: %s\n" {} \; # 因为单行命令中 -exec 参数中无法使用多个命令,以下方法可以实现在 -exec 之后接受多条命令 $ -exec ./text.sh {} \; # 搜索但跳出指定的目录 # 查找当前目录或者子目录下所有.txt文件,但是跳过子目录sk $ find . -path "./sk" -prune -o -name "*.txt" -print # find其他技巧收集 # 要列出所有长度为零的文件 $ find . -empty # -exec 接收 find 传过来的所有内容,容易造成溢出 # xargs find 的好伴侣,xargs 将 find 命令查找的结果分成若干模块输出给后面的指令
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 # grep 内容过滤 # 在文件中搜索一个单词,命令会返回一个包含“match_pattern”的文本行: $ grep match_pattern file_name $ grep "match_pattern" file_name # 输出除之外的所有行 -v 选项: $ grep -v "match_pattern" file_name # 标记匹配颜色 --color=auto 选项: $ grep "match_pattern" file_name --color=auto # 使用正则表达式 -E 选项: $ grep -E "[1-9]+" $ egrep "[1-9]+" # 只输出文件中匹配到的部分 -o 选项: $ echo this is a test line. | grep -o -E "[a-z]+\." line. $ echo this is a test line. | egrep -o "[a-z]+\." line. # 统计文件或者文本中包含匹配字符串的行数 -c 选项: $ grep -c "text" file_name # 输出包含匹配字符串的行数 -n 选项: $ grep "text" -n file_name $ cat file_name | grep "text" -n# 搜索多个文件并查找匹配文本在哪些文件中: $ grep -l "text" file1 file2 file3... # 在多级目录中对文本进行递归搜索: $ grep "text" . -r -n # 忽略匹配样式中的字符大小写: $ echo "hello world" | grep -i "HELLO" # 选项 -e 制动多个匹配样式: $ echo this is a text line | grep -e "is" -e "line" -ois line # 也可以使用-f选项来匹配多个样式,在样式文件中逐行写出需要匹配的字符。 cat patfile aaa bbb $ echo aaa bbb ccc ddd eee | grep -f patfile -o# 在grep搜索结果中包括或者排除指定文件: # 只在目录中所有的.php和.html文件中递归搜索字符"main()" $ grep "main()" . -r --include *.{php,html} # 在搜索结果中排除所有README文件 $ grep "main()" . -r --exclude "README" # 在搜索结果中排除filelist文件列表里的文件 $ grep "main()" . -r --exclude-from filelist # 使用0值字节后缀的grep与xargs: # 测试文件: $ echo "aaa" > file1$ echo "bbb" > file2$ echo "aaa" > file3$ grep "aaa" file* -lZ | xargs -0 rm # 执行后会删除 file1 和 file3,grep 输出用 -Z 选项来指定以 0 值字节作为终结符文件名(\0),xargs -0 读取输入并用 0 值字节终结符分隔文件名,然后删除匹配文件,-Z 通常和 -l 结合使用。 # grep静默输出: # 不会输出任何信息,如果命令运行成功返回0,失败则返回非0值。一般用于条件测试。 $ grep -q "test" filename # 打印出匹配文本之前或者之后的行: # 显示匹配某个结果 之后的3行,使用 -A 选项: $ seq 10 | grep "5" -A 3# 显示匹配某个结果 之前的3行,使用 -B 选项: $ seq 10 | grep "5" -B 3# 显示匹配某个结果的 前三行和后三行,使用 -C 选项: $ seq 10 | grep "5" -C 3
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 # xargs # 参数: -a file 从文件中读入作为sdtin -e flag ,注意有的时候可能会是-E,flag必须是一个以空格分隔的标志,当xargs分析到含有flag这个标志的时候就停止。 -p 当每次执行一个argument的时候询问一次用户。 -n num 后面加次数,表示命令在执行的时候一次用的argument的个数,默认是用所有的。 -t 表示先打印命令,然后再执行。 -i 或者是-I,这得看linux支持了,将xargs的每项名称,一般是一行一行赋值给 {},可以用 {} 代替。 -r no-run-if-empty 当xargs的输入为空的时候则停止xargs,不用再去执行了。 -s num 命令行的最大字符数,指的是 xargs 后面那个命令的最大命令行字符数。 -L num 从标准输入一次读取 num 行送给 command 命令。 -l 同 -L。 -d delim 分隔符,默认的xargs分隔符是回车,argument的分隔符是空格,这里修改的是xargs的分隔符。 -x exit的意思,主要是配合-s使用。。 -P 修改最大的进程数,默认是1,为0时候为as many as it can ,这个例子我没有想到,应该平时都用不到的吧。
1.3 VIM 设置 ~/.bashrc
添加 set -o vi – 可以直接使用 vim 的各种快捷键
VIM 快捷键:
1.4 GCC gcc 工作流程
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 # 预处理 头文件展开 宏替换 $ gcc -E hello.c hello.i # 生成汇编代码 $ gcc -S hello.i hello.s # 将汇编编译成二进制文件 $ gcc -c hell0.s hello.o # 链接 $ gcc hello.o a.out
gcc 参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 # 指定编译输出的名字 $ gcc main.c -o main # 通过 -Wall 参数启用所有警告 $ gcc -Wall main.c -o main # 使用 -E 参数只产生预处理输出 $ gcc -E main.c > main.i # 使用 -S 参数只产生汇编代码 $ gcc -S main.c > main.s # 使用 -C 参数只产生编译的代码 $ gcc -C main.c # 上面的代码产生main.o, 包含机器级别的代码或者编译的代码。 # 使用-save-temps参数产生所有的中间步骤的文件 $ gcc -save-temps main.c $ ls a.out main.c main.i main.o main.s # 使用 -l 参数链接共享库 $ gcc -Wall main.c -o main -lCPPfile # 使用 -fPIC 产生位置无关的代码 # 当产生共享库的时候,应该创建位置无关的代码,这会让共享库使用任意的地址而不是固定的地址,要实现这个功能,需要使用-fPIC参数。 # 下面的例子产生libCfile.so动态库。 $ gcc -c -Wall -Werror -fPIC Cfile.c $ gcc -shared -o libCfile.so Cfile.o # 产生共享库的时候使用了-fPIC 参数。 # 注意 -shared 产生共享库。 # 使用 -V 打印所有的执行命令 $ gcc -Wall -v main.c -o main Using built-in specs. COLLECT_GCC=gcc COLLECT_LTO_WRAPPER=/usr/lib/gcc/i686-linux-gnu/4.6/lto-wrapper ... # 使用 -D 参数可以使用编译时的宏 $ gcc -Wall -D MY_MACRO main.c -o main # 使用 -Werror 将警告升级为错误 $ gcc -Wall -Werror main.c -o main # 使用 @ 参数从文件中读取参数 # gcc参数可以从文件中读取,通过@后跟文件名的方式提供, 多个参数可以使用空格区隔。 $ cat opt_file -Wall -omain $ gcc main.c @opt_file # 使用参数 -I 指定头文件的文件夹 $ gcc -I/home/codeman/include input-file.c # -I 取消前一个参数功能,一般用在 -Idir 之后。 # 使用参数-std指定支持的c++/c的标准 $ gcc -std=c++11 hello-world.cpp # 使用 -static 生成静态链接的文件 静态编译文件(把动态库的函数和其它依赖都编译进最终文件) $ gcc main.c -static -o main -lpthread # 相反的使用 -shared 使用动态库链接。 # 使用 -g 用于 gdb 调试 $ gcc main.c -static -o main -g # -lstdc++ 指定 gcc 以 c++ 方式编译 $ gcc main.cpp -lstdc++ -o main # -O 优化选项, 1-3 越高优先级越高 $ gcc main.cpp -lstdc++ -o main -O1 # 使用 -M 生成文件关联的信息 $ gcc -M main.c main.o: main.c /usr/include/stdc-predef.h /usr/include/stdio.h \ /usr/include/features.h /usr/include/sys/cdefs.h \ /usr/include/bits/wordsize.h /usr/include/gnu/stubs.h \ /usr/include/gnu/stubs-64.h \ /usr/lib/gcc/x86_64-redhat-linux/4.8.5/include/stddef.h \ /usr/include/bits/types.h /usr/include/bits/typesizes.h \ /usr/include/libio.h /usr/include/_G_config.h /usr/include/wchar.h \ /usr/lib/gcc/x86_64-redhat-linux/4.8.5/include/stdarg.h \ /usr/include/bits/stdio_lim.h /usr/include/bits/sys_errlist.h
1.5 库文件制作 静态库制作和使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 # 步骤 # 1. 编译为 .o 文件 # 2. 将 .o 文件打包:ar rcs libmycalc.a file1.0 file2.o ... # 3. 将头文件与库一起发布 # 查看库信息 $ nm libmycalc.a # 使用 # 编译时 需要加静态库名(记得路径),-I 包含头文件 $ gcc main.c -o app -I include/ -L lib/ -lmycalc # 优点: # 1. 执行快 # 2. 发布应用时不需要发布库 # 缺点: # 1. 执行程序体积比较大 # 2. 库变更时需要重新编译应用
动态库制作和使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 # 步骤 # 1. 编译与位置无关的代码,生成 .o 关键参数 -fPIC # 2. 将 .o 文件打包, 关键参数 -shared # 3. 将库与头文件一起发布 $ gcc -shared -o libcalc.so *.o # 使用 # -L 指定动态库路径 -I 指定库名 $ gcc main.c -o app -I include/ -L lib/ -lcalc # ldd 查看库依赖 $ ldd libcalc.so # 优点: # 1. 执行程序体积小 # 2. 库变更时,一般不需要重新发布动态库 # 缺点: # 1. 执行时需要加载动态库,相对而言,比静态库慢 # 2. 发布应用时需要同时发布动态库 # 解决不能加载动态库的问题 # 1. 拷贝到 /lib 下。 一般不允许 # 2. 将库路径增加到环境变量 LD_LIBRARY_PATH 中,不是特别推荐 # 3. 配置 /etc/ld.so.conf 文件,增加 当前项目库路径,执行 sudo ldconfig -v
1.6 Makefile makefile 的三要素:
写法:
1 2 app: main.c add.c sub.c div.c mul.c gcc -o app -I./include / main.c add.c sub.c div.c mul.c
如果更改其中一个文件,所有的源码都重新编译
可以考虑编译过程分解,先生成 .o
文件,然后使用 .o
文件编程结果
规则是递归的,依赖文件如果比目标文件新,则重新生成目标文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 ObjFiles=main.o add.o sub.o div.o mul.o app: $(ObjFiles) gcc -o app -I./include/ $(ObjFiles) main.o: main.c gcc -c main.c -I./include/ add.o: add.c gcc -c add.c -I./include/ sub.o: sub.c gcc -c sub.c -I./include/ div.o: div.c gcc -c div.c -I./include/ mul.o: mul.c gcc -c mul.c -I./include/
makefile 的隐含规则:默认处理第一个目标
1 2 3 4 5 6 7 8 9 # get all .c files SrcFiles=$(wildcard *.c) # all .c files --> .o file ObjFiles=$(patsubst %.c,%.o,$(SrcFiles)) test: echo $(SrcFiles) echo $(ObjFiles)
makefile 变量:
$@
代表目标
$^
代表全部依赖
$<
第一个依赖
$?
第一个变化的依赖
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 # get all .c files SrcFiles=$(wildcard *.c) # all .c files --> .o file ObjFiles=$(patsubst %.c,%.o,$(SrcFiles)) all:app # 目标文件用法 $(Var) app: $(ObjFiles) gcc -o $@ -I./include/ $(ObjFiles) # 模式匹配规则, $@ $< 这样的变量,只能在规则中出现 % .o:%.c gcc -c $< -I./include/ -o $@ # @ 在规则前代表不输出该条规则的命令 # - 规则前的“-”,代表该条规则报错,仍然继续执行 # .PHONY 定义伪目标,防止有歧义 .PHONY:clean all clean: -@rm -f *.o -@rm -f app
make -f makefile1
指定makefile文件进行编译
1 2 3 4 5 6 7 8 9 10 SrcFiles=$(wildcard *.c) TargetFiles=$(patsubst %.c,%,$(SrcFiles) ) all:$(TargetFiles) %:%.c gcc -o $@ %^ clean: rm -f $(TargetFiles)
1.7 gdb 调试
gdb 调试入门,大牛写的高质量指南
gdb 调试利器
启动gdb:gdb app
在gdb启动程序:
r(un) – 启动 可以带参数启动
start – 启动 - 停留在main函数,分步调试
n(ent) – 下一条指令
s(tep) – 下一条指令,可以进入函数内部,库函数不能进
q(uit) – 退出 gdb
b(reak) num – 指定行号,函数, 文件:行号 设置断点
b 行号 – 主函数所在文件的行
b 函数名
b 文件名:行号
l(ist) 文件:行号 – 查看代码
info b – 查看断点信息
d(el) num – 删除断点
c – continue 跳到下一个断点
p(rint) – 打印参数,或者变量值
ptype 变量 – 查看变量类型
set – 设置变量的值
set argc=4
set argv[1]=“12”
set argv[2] = “7”
display argc – 跟踪显示参数或者变量的变化
info display
undisplay num
b num if xx == xx – 条件断点
gdb跟踪core
设置生成 core :ulimit -c unlimited
取消生成 core: ulimit -c 0
设置 core 文件格式:/proc/sys/kernel/core_pattern
文件不能 vi,可以用后面的套路:echo “/corefile/core-%e-%p-%t” > core_pattern
core 文件如何使用:
2. 系统api与库函数的关系
3. Linux 系统编程 ulimit -a
查看所有资源的上限
env
查看环境变量
echo $PATH
打印指定的环境变量
char *getenv()
获取环境变量
创建一个进程 :
pid_t fork(void)
返回值:
获得pid,进程 id,获得当前进程
pid_t getpid(void)
获得当前进程父进程的 id
pid_t getppid(void)
ps ajx
查看父进程和子进程相关信息
进程共享 :
父子进程之间在fork后,有哪些相同和不同:
父子相同处:
全局变量
data、text、栈、堆、环境变量
用户 ID
宿主目录、进程工作目录、信号处理方式…
父子不同处:
进程 ID
父进程 ID
进程运行时间
闹钟(定时器)
未决信号集
似乎,子进程复制了父进程 0-3G 用户空间内容,以及父进程的 PCB, 但 pid 不同。真的每 fork 一个子进程都要将父进程的 0-3G 地址空间完全拷贝一份,然后在映射至屋里内存吗?当然不是,父子进程间遵循读时共享写时复制 。这样设计,无论子进程执行父进程的逻辑还是执行自己的逻辑都能节省内存开销。
孤儿进程与僵尸进程 :
回收子进程,知道子进程的死亡原因,作用:
pid_t wait(int *status)
子进程的死亡原因:
正常死亡 WIFEXITED
如果 WIFEXITED 为真,使用 WEXITSTATUS 得到退出的状态
非正常死亡 WIFSIGNALED
如果 WIFSIGNALED 为真,使用 WTERMSIG 得到信号
pid_t waitpid(pid_t pid, int *status, int options)
pid
< -1
组ID
-1
回收任意
0
回收和调用进程组 ID 相同组内的子进程
>0
回收指定的 pid
option
0 与 wait 相同,也会阻塞
WNOHANG 如果当前没有子进程退出,会立刻返回
返回值
如果设置了 WNOHANG ,那么如果没有子进程退出,返回 0
失败返回 -1 (没有子进程)
3.1 IPC 概念 IPC : 进程间通信,通过内核提供的缓存区进行数据交换的机制
IPC 通信的方式有几种:
pipe 管道 – 最简单
fifo 有名管道
mmap 文件映射共享IO – 速度最快
本地 socket 最稳定
信号 携带信息量最小
共享内存
消息队列
读管道 :
写端全部关闭 – read 读到 0, 想当于读到文件末尾
写端没有全部关闭
有数据 – read 读到数据
没有数据 – read 阻塞 fcntl 函数可以更改非阻塞
写管道 :
读端全部关闭 – ? 产生一个信号 SIGPIPE,程序异常终止
读端未全部关闭
管道已满 – write 阻塞 – 如果要显示现象,读端一直不读,写端狂写。
管道未满 – write 正常写入
管道缓冲区大小
可以使用 ulimit –a
命令来查看当前系统中创建管道文件所对应的内核缓冲区大小。通常为:
1 pipe size (512 bytes, -p) 8
也可以使用 fpathconf
函数,借助参数选项来查看。使用该宏应引入头文件<unistd.h>
long fpathconf(int fd, int name);
管道的优劣
优点:
缺点:
只能单向通信,双向通信需建立两个管道。
只能用于父子、兄弟进程(有共同祖先)间通信。该问题后来使用fifo有名管道解决。
FIFO通信
FIFO 有名管道,实现无血缘关系进程通信
创建一个管道的伪文件
mkfifo myfifo 命令创建
也可以使用函数 int mkfifo(const char *pathname, mode_t mode);
内核会针对 fifo 文件开辟一个缓存区,操作 fifo 文件,可以操作缓存区,实现进程间通信 – 实际上就是文件读写
mmap映射共享区
1 void *mmap (void *adrr, size_t length, int prot, int flags, int fd, off_t offset) ;
返回:
成功:返回创建的映射区首地址;
失败:MAP_FAILED宏
参数:
addr: 建立映射区的首地址,由Linux内核指定。使用时,直接传递NULL
length: 欲创建映射区的大小
prot: 映射区权限 PROT_READ、PROT_WRITE、PROT_READ|PROT_WRITE
flags: 标志位参数(常用于设定更新物理区域、设置共享、创建匿名映射区)
MAP_SHARED: 会将映射区所做的操作反映到物理设备(磁盘)上。(共享的)
MAP_PRIVATE: 映射区所做的修改不会反映到物理设备。(私有的)
fd: 用来建立映射区的文件描述符
offset: 映射文件的偏移(4k的整数倍)
释放映射区
1 int munmap (void *addr, size_t length) ;
匿名映射
通过使用我们发现,使用映射区来完成文件读写操作十分方便,父子进程间通信也较容易。但缺陷是,每次创建映射区一定要依赖一个文件才能实现。通常为了建立映射区要open一个temp文件,创建好了再unlink、close掉,比较麻烦。 可以直接使用匿名映射来代替。其实Linux系统给我们提供了创建匿名映射区的方法,无需依赖一个文件即可创建映射区。同样需要借助标志位参数flags来指定。
使用 MAP_ANONYMOUS
(或 MAP_ANON
), 如:
1 2 int *p = mmap(NULL , 4 , PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1 , 0 );
需注意的是,MAP_ANONYMOUS和MAP_ANON这两个宏是Linux操作系统特有的宏。在类Unix系统中如无该宏定义,可使用如下两步来完成匿名映射区的建立。
1 2 3 4 5 6 fd = open("/dev/zero" , O_RDWR); p = mmap(NULL , size, PROT_READ|PROT_WRITE, MMAP_SHARED, fd, 0 );
信号的概念
信号的特点
信号的机制
进程 B 发送给进程 A ,内核产生信号,内核处理
信号的产生
按键产生
函数调用 kill、raise、abort
定时器 alarm、setitimer
命令产生 kill
硬件异常、段错误、浮点型错误、总线错误、SIGPIPE
信号的状态
信号的默认处理方式
信号的 4 要素
3.2 进程和线程
创建一个会话需要注意以下 5 点注意事项:
调用进程不能是进程组组长,该进程编程新会话首进程(session header)
该进程成为一个新进程组的组长进程
新会话丢弃原有的控制终端,该会话没有控制终端
该调用进程是组长进程,则出错返回
建立会话时,先调用fork,父进程终止,子进程调用 setsid
守护进程:
Daemon 进程,是 Linux 中的后台服务进程,通常独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。一般采用以 d 结尾的名字。
创建守护进程,最关键的一步是调用 setsid 函数创建一个新的 session 。并成为 session leader。
创建守护进程模型 :
创建子进程,父进程退出
在子进程中创建新会话
setsid() 函数
使子进程完全能独立出来,脱离控制
改变当前目录为根目录
chdir() 函数
防止占用可卸载的文件系统
也可以换成其他路径
重设文件权限掩码
umask() 函数
防止继承的文件创建屏蔽字拒绝某些权限
增加守护进程灵活性
关闭文件描述符
开始执行守护进程核心工作
守护进程退出处理程序模型
会话:进程组的更高一级,多个进程组对应一个会话
进程组:多个进程在同一个组,第一个进程默认是进程组的组长
创建会话的时候,组长不可以创建,必须是组员创建。
创建会话的步骤:创建子进程,父进程终止,子进程当会长
守护进程的步骤:
创建子进程 fork
父进程退出
子进程当会长 setsid
切换工作目录 $HOME
设置掩码 umask
关闭文件描述符,为了避免浪费资源
执行核心逻辑
退出
1 2 int pthread_create (pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg) ;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 #include <stdio.h> #include <unistd.h> #include <fcntl.h> #include <string.h> #include <stdlib.h> #include <time.h> #include <signal.h> int main (int argc, char *argv[]) { char strFileName[256 ] = {0 }; while (1 ) { memset (strFileName, 0x00 , sizeof (strFileName)); sprintf (strFileName, "%s/log/Mr.Miaow.%ld" , getenv("HOME" ), time(NULL )); int fd = open(strFileName, O_RDWR | O_CREAT, 0666 ); if (fd < 0 ) { perror("open err" ); exit (1 ); } close(fd); sleep(5 ); } return 0 ; }
扩展了解:
通过 nohup 指令也可以达到守护进程创建的效果
nohup cmd [> 1.log] &
nohup 指令会让 cmd 收不到 SIGHUP 信号
& 代表后台运行
线程是最小的执行单位,进程是最小的系统资源分配单位
查看 LWP 号:ps -Lf pid
查看指定线程的 lwp 号
线程非共享资源
线程 ID
处理器现场和栈指针(内核栈)
独立的栈空间(用户空间栈)
errno 变量
信号屏蔽字
调度优先级
线程优缺点:
1 2 3 4 5 6 7 8 9 10 11 alias echomake=`cat ~/bin/makefile.template >> makefile` $ cat ~/bin/makefile.template# create by Mr.Miaow `date +%Y%m%d` SrcFiles=$(wildcard *.c) TargetFiles=$(patsubst %.c,%,$(SrcFiles)) all:$(TargetFiles) % :%.c gcc -o $@ %< -lpthread -g clean: -rm -f $(TargetFiles)
线程退出注意事项 :
在线程中使用pthread_exit
在线程中使用 return (主控线程return 代表退出进程)
exit 代表退出整个进程
线程回收函数:
1 int pthread_join (pthread_t thread, void **retval) ;
杀死线程:
1 int pthread_cancel (pthread_t thread) ;
被pthread_cancel 杀死的线程,退出状态为 PTHREAD_CANCELED
线程分离:
1 int pthread_detach (pthread_t thread) ;
此时不需要 pthread_join回收资源
线程 ID 在进程内部是唯一的
进程属性控制 :
初始化线程属性
1 int pthread_attr_init (pthread_attr_t *attr) ;
销毁线程属性
1 int pthread_attr_destroy (pthread_attr_t *attr) ;
设置属性分离态
1 2 3 4 5 6 7 int pthread_attr_setdetachstate (pthread_attr_t *attr, int detachstate) ;# attr init 初始化的属性 # detachstate # - PTHREAD_CREATE_DETACHED 线程分离 # - PTHREAD_CREATE_JOINABLE 允许回收 int pthread_attr_getdetachstate (const pthread_attr_t *attr, int *detachstate) ;
查看线程库版本:
1 $ getconf GNU_LIBPTHREAD_VERSION
创建多少个线程?
线程同步 :
解决同步的问题:加锁
mutex 互斥量 :
1 2 3 4 5 6 7 8 9 pthread_mutex_t fastmutex = PTHREAD_MUTEX_INITIALIZER; pthread_mutex_t recmutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;pthread_mutex_t errchkmutex = PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP;int pthread_mutex_init (pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr) ;int pthread_mutex_lock (pthread_mutex_t *mutex) ;int pthread_mutex_trylock (pthread_mutex_t *mutex) ;int pthread_mutex_unlock (pthread_mutex_t *mutex) ;int pthread_mutex_destroy (pthread_mutex_t *mutex) ;
读写锁的特点:读共享,写独占,写优先级高
读写说任然是一把锁,有不同状态:
1 2 3 4 5 6 7 8 9 int pthread_rwlock_init (pthread_rwlock_t *restrict rwlock, const pthread_rwlockattr_t *restrict attr) ;int pthread_rwlock_destroy (pthread_rwlock_t *rwlock) ;pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER;int pthread_rwlock_rdlock (pthread_rwlock_t *rwlock) ;int pthread_rwlock_tryrdlock (pthread_rwlock_t *rwlock) ;int pthread_rwlock_wrlock (pthread_rwlock_t *rwlock) ;int pthread_rwlock_unlock (pthread_rwlock_t *rwlock) ;
条件变量 (生产者消费者模型):
1 2 3 4 5 6 7 8 9 10 11 pthread_cond_t cond = PTHREAD_COND_INITIALIZER;int pthread_cond_init (pthread_cond_t *cond, pthread_condattr_t *cond_attr) ;int pthread_cond_signal (pthread_cond_t *cond) ;int pthread_cond_broadcast (pthread_cond_t *cond) ;int pthread_cond_wait (pthread_cond_t *cond, pthread_mutex_t *mutex) ;int pthread_cond_timedwait (pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime) ;int pthread_cond_destroy (pthread_cond_t *cond) ;
信号量 加强版的互斥锁 :
信号量是进化版的互斥量,允许多个线程访问共享资源
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 #include <semaphore.h> int sem_init (sem_t *sem, int pshared, unsigned int value) ;# pshared # - 0 代表线程信号量 # - 非0 代表进程信号量 # value 定义信号量的个数 int sem_wait (sem_t *sem) ;int sem_post (sem_t *sem) ;int sem_trywait (sem_t *sem) ;int sem_timedwait (sem_t *sem, const struct timespec *abs_timeout) ;int sem_destroy (sem_t *sem) ;
文件锁 :
1 2 3 4 #include <unistd.h> #include <fcntl.h> int fcntl (int fd, int cmd, ... ) ;