Linux复习

linux期末复习

本文中一些$a这样的shell变量引用会被mathjex翻译成公式,请分辨。

  1. 基本的命令
  2. 如何安装应用程序,快捷键等(vim,linux本身的快捷键)
  3. shell脚本
  4. 编译(gcc,makefile)
  5. shell命令的c语言实现
  6. 多进程与线程的基本概念与编程

课程内容回顾

  • 各类Linux下的命令;
  • Shell脚本;
  • 基于命令的文字处理,尤其是正则表达式;
  • Linux下的C语言编程环境,gcc及make等;
  • Linux下如何搭建网站;
  • Linux下常用命令的实现ls, cat, cp以及pwd等;
  • 进程及线程等;

考试题型

  • 填空题(30分:2分*15);
  • 判断题(10分:1分*10);
  • 简答题(28分:4分*7);
  • 代码阅读题(16分);填写空白代码行;
  • 编程题(16分:8分*2);

考试范围和重点

每周实验的内容;

不考的内容:
1、正则表达式;
2、字符串处理命令sed和awk;
3、curl和wget;
4、Samba服务器、apache、mysql和php等的安装;
5、多线程的线程间同步;

重点考察内容

  • Ubuntu下的常见操作,比如安装程序,快捷键等;
  • 常用的命令和shell脚本;
  • Linux下的编译环境,比如gcc和Makefile;
  • 常用shell命令的C语言实现,比如cp, cat, ls以及pwd;
  • 多线程与进程的基本概念及接口;

Linux下的各种命令

参考word(书本和word版本的linux复习提纲),重点注意(软链接,硬链接),(rm,tail,head,cat,grep,cd,ls,mkdir。rmdir,pwd,ps,……),(命令之间的管道的应用),(chmod),(进入root),(group用户组),man命令,crontab命令!!!。

1. 切换用户

1
2
su username
su #切换到root目录

2.Group

1
2
groupadd 选项 用户组
groupdel 用户组 # 删除

可以使用的选项有:

  • -g GID 指定新用户组的组标识号(GID)。
  • -o 一般与-g选项同时使用,表示新用户组的GID可以与系统已有用户组的GID相同。
1
2
useradd 选项 用户名 # 新增
userdel 选项 用户名 # 删除
  • 选项:
    • -c comment 指定一段注释性描述。
    • -d 目录 指定用户主目录,如果此目录不存在,则同时使用-m选项,可以创建主目录。
    • -g 用户组 指定用户所属的用户组。
    • -G 用户组,用户组 指定用户所属的附加组。
    • -s Shell文件 指定用户的登录Shell。
    • -u 用户号 指定用户的用户号,如果同时有-o选项,则可以重复使用其他用户的标识号。

Shell脚本

下面的大部分内容引用自shell菜鸟教程,很多的内容都省略了,想看完全版的去菜鸟教程,这里只是考试内容。

1.shell脚本基础

bash,zsh,csh ……,ubuntu内置的默认shell脚本是bash。

1
2
#! /bin/bash
echo "Hello World !"

#! 是一个约定的标记,它告诉系统这个脚本需要什么解释器来执行,即使用哪一种 Shell。

2.shell脚本的运行方式

1、作为可执行程序

cd 到相应目录

1
2
chmod +x ./test.sh  #使脚本具有执行权限
./test.sh #执行脚本

一定要写成 ./test.sh,而不是 test.sh,运行其它二进制的程序也一样,直接写 test.sh,linux 系统会去 PATH 里寻找有没有叫 test.sh 的。要用 ./test.sh 告诉系统说,就在当前目录找。

2、作为解释器参数

这种运行方式是,直接运行解释器,其参数就是 shell 脚本的文件名,如:

1
2
/bin/sh test.sh
/bin/php test.php

这种方式运行的脚本,不需要在第一行指定解释器信息,写了也没用。

3.shell变量

注意,变量名和等号之间不能有空格

1
2
3
4
5
6
your_name="Jason"
除了显式地直接赋值,还可以用语句给变量赋值,如:
for file in `ls /etc`

for file in $(ls /etc)
以上语句将 /etc 下目录的文件名循环出来。

已定义的变量,可以被重新定义

1
2
3
4
your_name="tom"
echo $your_name
your_name="alibaba"
echo $your_name
  • 这样写是合法的,但注意,第二次赋值的时候不能写 $ your_name="alibaba",使用变量的时候才加美元符($)。

只读变量

1
2
myUrl="https://www.google.com"
readonly myUrl

删除变量

1
unset variable_name

变量类型(应该不会考):hear_no_evil:

  • 1) 局部变量 局部变量在脚本或命令中定义,仅在当前shell实例中有效,其他shell启动的程序不能访问局部变量。
  • 2) 环境变量 所有的程序,包括shell启动的程序,都能访问环境变量,有些程序需要环境变量来保证其正常运行。必要的时候shell脚本也可以定义环境变量。
  • 3) shell变量 shell变量是由shell程序设置的特殊变量。shell变量中有一部分是环境变量,有一部分是局部变量,这些变量保证了shell的正常运行

3.1 shell字符串

字符串可以用单引号,也可以用双引号,也可以不用引号。

1
str='this is a string'

单引号字符串的限制

1.单引号里的任何字符都会原样输出,单引号字符串中的变量是无效的;
2.单引号字串中不能出现单独一个的单引号(对单引号使用转义符后也不行),但可成对出现,作为字符串拼接使用。

双引号的优点

  1. 双引号里可以有变量
  2. 双引号里可以出现转义字符

拼接字符串

1
2
3
4
5
6
7
8
9
your_name="runoob"
# 使用双引号拼接
greeting="hello, "$your_name" !"
greeting_1="hello, ${your_name} !"
echo $greeting $greeting_1
# 使用单引号拼接
greeting_2='hello, '$your_name' !'
greeting_3='hello, ${your_name} !'
echo $greeting_2 $greeting_3

输出结果为

1
2
hello, runoob ! hello, runoob !
hello, runoob ! hello, ${your_name} !

字符串长度

1
2
string="abcd"
echo ${#string} #输出 4

查找子字符串

查找字符 i 或 o 的位置(哪个字母先出现就计算哪个):

1
2
string="runoob is a great site"
echo `expr index "$string" io` # 输出 4

注意: 以上脚本中 `是反引号,而不是单引号',不要看错了哦。

3.2 shell 数组

1
2
3
4
5
6
7
8
9
10
11
数组名=(值1 值2 ... 值n)
array_name=(value0 value1 value2 value3)
array_name=(
value0
value1
value2
value3
)
array_name[0]=value0
array_name[1]=value1
array_name[n]=valuen

3.3 shell 注释

以 # 开头的行就是注释,会被解释器忽略。

多行注释

1
2
3
4
5
:<<EOF
注释内容...
注释内容...
注释内容...
EOF

4.shell传递参数

我们可以在执行 Shell 脚本时,向脚本传递参数,脚本内获取参数的格式为:$n。n 代表一个数字,1 为执行脚本的第一个参数,2 为执行脚本的第二个参数,以此类推……

例如:

1
2
3
4
5
echo "Shell 传递参数实例!";
echo "执行的文件名:$0";
echo "第一个参数为:$1";
echo "第二个参数为:$2";
echo "第三个参数为:$3";

输出如下:

1
2
3
4
5
6
7
$ chmod +x test.sh 
$ ./test.sh 1 2 3
Shell 传递参数实例!
执行的文件名:./test.sh
第一个参数为:1
第二个参数为:2
第三个参数为:3

特殊参数:

参数处理 说明
$# 传递到脚本的参数个数
$? 显示最后命令的退出状态。0表示没有错误,其他任何值表明有错误。
$* 以一个单字符串显示所有向脚本传递的参数。如”$*”用「”」括起来的情况、以”$1 $2 … $n”的形式输出所有参数。

5.shell基本运算符

1
2
val=`expr 2 + 2`
echo "两数之和为 : $val"

表达式和运算符之间要有空格,例如 2+2 是不对的,必须写成 2 + 2,这与我们熟悉的大多数编程语言不一样。

运算符 说明 举例
+ 加法 expr $a + $b 结果为 30。
- 减法 expr $a - $b 结果为 -10。
* 乘法 expr $a \* $b 结果为 200。
/ 除法 expr $b / $a 结果为 2。
% 取余 expr $b % $a 结果为 0。
= 赋值 a=$b 将把变量 b 的值赋给 a。
== 相等。用于比较两个数字,相同则返回 true。 [ $a == $b ] 返回 false。
!= 不相等。用于比较两个数字,不相同则返回 true。 [ $a != $b ] 返回 true。

注意:条件表达式要放在方括号之间,并且要有空格,例如: [$a==$b] 是错误的,必须写成 **[ $a == $b ]**。

乘号(*)前边必须加反斜杠()才能实现乘法运算

数字类型关系运算符

运算符 说明 举例
-eq 检测两个数是否相等,相等返回 true。 [ $a -eq $b ] 返回 false。
-ne 检测两个数是否不相等,不相等返回 true。 [ $a -ne $b ] 返回 true。
-gt 检测左边的数是否大于右边的,如果是,则返回 true。 [ $a -gt $b ] 返回 false。
-lt 检测左边的数是否小于右边的,如果是,则返回 true。 [ $a -lt $b ] 返回 true。
-ge 检测左边的数是否大于等于右边的,如果是,则返回 true。 [ $a -ge $b ] 返回 false。
-le 检测左边的数是否小于等于右边的,如果是,则返回 true。 [ $a -le $b ] 返回 true。

布尔运算符

运算符 说明 举例
! 非运算,表达式为 true 则返回 false,否则返回 true。 [ ! false ] 返回 true。
-o 或运算,有一个表达式为 true 则返回 true。 [ $a -lt 20 -o $b -gt 100 ] 返回 true。
-a 与运算,两个表达式都为 true 才返回 true。 [ $a -lt 20 -a $b -gt 100 ] 返回 false。

字符串运算符

运算符 说明 举例
= 检测两个字符串是否相等,相等返回 true。 [ $a = $b ] 返回 false。
!= 检测两个字符串是否不相等,不相等返回 true。 [ $a != $b ] 返回 true。
-z 检测字符串长度是否为0,为0返回 true。 [ -z $a ] 返回 false。
-n 检测字符串长度是否不为 0,不为 0 返回 true。 [ -n “$a” ] 返回 true。
$ 检测字符串是否为空,不为空返回 true。 [ $a ] 返回 true。

6.Shell echo命令

1
2
3
#!/bin/sh
read name
echo "$name It is a test"

重定向

1
echo "It is a test" > myfile

显示命令执行结果

1
echo `date` 

7.Shell printf

1
printf  format-string  [arguments...]
1
2
3
4
printf "%-10s %-8s %-4s\n" 姓名 性别 体重kg  
printf "%-10s %-8s %-4.2f\n" 郭靖 男 66.1234
printf "%-10s %-8s %-4.2f\n" 杨过 男 48.6543
printf "%-10s %-8s %-4.2f\n" 郭芙 女 47.9876

转义

序列 说明
\a 警告字符,通常为ASCII的BEL字符
\b 后退
\c 抑制(不显示)输出结果中任何结尾的换行字符(只在%b格式指示符控制下的参数字符串中有效),而且,任何留在参数里的字符、任何接下来的参数以及任何留在格式字符串中的字符,都被忽略
\f 换页(formfeed)
\n 换行
\r 回车(Carriage return)
\t 水平制表符
\v 垂直制表符
\ 一个字面上的反斜杠字符
\ddd 表示1到3位数八进制值的字符。仅在格式字符串中有效
\0ddd 表示1到3位的八进制值字符

8.流程控制(书上写的够明白了)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
a=10
b=20
if [ $a == $b ]
then
echo "a 等于 b"
elif [ $a -gt $b ]
then
echo "a 大于 b"
elif [ $a -lt $b ]
then
echo "a 小于 b"
else
echo "没有符合的条件"
fi
1
2
3
4
5
6
7
8
9
10
for loop in 1 2 3 4 5
do
echo "The value is: $loop"
done

for str in This is a string
do
echo $str
done

1
2
3
4
5
6
7
8
9
10
11
12
13
int=1
while(( $int<=5 ))
do
echo $int
let "int++"
done

echo '按下 <CTRL-D> 退出'
echo -n '输入你最喜欢的网站名: '
while read FILM
do
echo "是的!$FILM 是一个好网站"
done
1
2
3
4
5
6
7
a=0

until [ ! $a -lt 10 ]
do
echo $a
a=`expr $a + 1`
done
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
echo '输入 1 到 4 之间的数字:'
echo '你输入的数字为:'
read aNum
case $aNum in
1) echo '你选择了 1'
;;
2) echo '你选择了 2'
;;
3) echo '你选择了 3'
;;
4) echo '你选择了 4'
;;
*) echo '你没有输入 1 到 4 之间的数字'
;;
esac

#-----------------------------------------
site="runoob"

case "$site" in
"runoob") echo "菜鸟教程"
;;
"google") echo "Google 搜索"
;;
"taobao") echo "淘宝网"
;;
esac

9.Shell 输入/输出重定向

命令 说明
command > file 将输出重定向到 file。
command < file 将输入重定向到 file。
command >> file 将输出以追加的方式重定向到 file。
n > file 将文件描述符为 n 的文件重定向到 file。
n >> file 将文件描述符为 n 的文件以追加的方式重定向到 file。
n >& m 将输出文件 m 和 n 合并。
n <& m 将输入文件 m 和 n 合并。
<< tag 将开始标记 tag 和结束标记 tag 之间的内容作为输入。

如何安装应用程序,快捷键等

1.换源

1
2
sudo cp /etc/apt/sources.list /etc/apt/sources.list.bak 备份原来的sorce文件
sudo gedit /etc/apt/sources.list 修改sources.list文件 or sudo vim /etc/apt/sources.list

修改完这个文件后

1
sudo apt-get update

2.安装软件

1
sudo apt-get install [app]

3.移除软件

1
sudo apt-get remove [app]

4.apt or apt-get

apt与apt-get可以实现大部分相同的功能,可以视作两个不同的软件,但是apt-get支持更为底层的操作,其操作更细。

5.快捷键

5.1 linux, terminal

Ctrl + Alt + T打开终端。

Tab-自动补全

  • 当以xxx开头的命令或文件名仅有1个时,按下Tab键 1次,将会自动补全该命令或文件名;
  • 当以xxx开头的命令或文件名不止1个时,按1次Tab键是没反应的,再按1次Tab键,下方将出现以xxx开头的命令或文件名。

在终端&控制台下copy and paste:

  • 粘贴:Ctrl + Shift + cCtrl + Insert
  • 复制:Ctrl + Shift + vShift + Insert

在终端中搜索内容

Ctrl + Shift + f

命令行历史

  • 上键、Ctrl + p:查看上一个使用过的命令
  • 下键、Ctrl + n:查看下一个使用过的命令
  • Ctrl + r:搜索历史命令。输入若干字符,开始向上搜索包含该字符的命令,继续按Ctrl+r,搜索上一条匹配的命令

清屏

Ctrl + l

横线的数量

  • 一横:表示参数是字符形式,如ls -a
  • 两横:表示参数是单词形式,如cp --help

5.2 vim

移动光标:gg第一行,G最后一行,XG第X行;

删除一行:dd 删除单词:dw

复制一行:yy

粘贴一行:p

退出编辑器

按键 功能 说明
:w 将缓冲区写入文件,即保存修改
:wq 保存修改并退出
:x 保存修改并退出
:q 退出,如果对缓冲区进行过修改,则会提示
:q! 强制退出,放弃修改

显示行数

:set nu

光标移动

按键 功能 说明
h,j,k,l 上,下,左,右
ctrl-e 移动页面
ctrl-f 上翻一页
ctrl-b 下翻一页
ctrl-u 上翻半页
ctrl-d 下翻半页
w 跳到下一个字首,按标点或单词分割 接上表
W 跳到下一个字首,长跳,如end-of-line被认为是一个字
e 跳到下一个字尾
E 跳到下一个字尾,长跳
b 跳到上一个字
B 跳到上一个字,长跳
0 跳至行首,不管有无缩进,就是跳到第0个字符
^ 跳至行首的第一个字符
$ 跳至行尾 常用
gg 跳至文首 常用
G 调至文尾 常用
5gg/5G 调至第5行
gd 跳至当前光标所在的变量的声明处
fx 在当前行中找x字符,找到了就跳转至
; 重复上一个f命令,而不用重复的输入fx
* 查找光标所在处的单词,向下查找
# 查找光标所在处的单词,向上查找

删除复制

按esc后,然后ggvG或者ggVG 全选(高亮显示)
按esc后,然后ggyG 全部复制
按esc后,然后dG 全部删除

基础的 gcc and makefile 操作

1. gcc

1.1 单文件编译

gcc test.c -o test

1.2 多文件编译

1
2
3
gcc -c test1.c -o test1.o
gcc -c test2.c -o test2.o
gcc test1.o test2.o -o test

2.Makefile

数据结构作业中用到了makefile

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
maincc:main.o compress.o pack.o  hfman.o
g++ pack.o compress.o main.o hfman.o -o compress_win
main.o:main.cpp
g++ -c main.cpp
pack.o:pack.cpp
g++ -c pack.cpp
compress:compress.cpp
g++ -c compress.cpp
hfman:hfman.cpp
g++ -c hfman.cpp

#maincc:main.o compress.o pack.o debugg.o\
g++ pack.o compress.o main.o debugg.o -o compress_debug_co\
main.o:main.cpp\
g++ -c main.cpp\
pack.o:pack.cpp\
g++ -c pack.cpp\
compress:compress.cpp\
g++ -c compress.cpp\
debugg:debugg.cpp\
g++ -c debugg.cpp\

注意注释编写规范。

进程与线程

1.进程与线程区别

引用:链接

第一个被创造出来的进程是0号进程,这个进程在操作系统层面是不可见的,但它存在着。0号进程完成了操作系统的功能加载与初期设定,然后它创造了1号进程(init),这个1号进程就是操作系统的“耶稣”。1号进程是上帝派来管理整个操作系统的,所以在用pstree查看进程树可知,1号进程位于树根。再之后,系统的很多管理程序都以进程身份被1号进程创造出来,还创造了与人类沟通的桥梁——shell。从那之后,人类可以跟操作系统进行交流,可以编写程序,可以执行任务。。。

无论是进程还是线程,对于程序员而言,都是用来实现多任务并发的技术手段。二者都可以独立调度,因此在多任务环境下,功能上并无差异。并且二者都具有各自的实体,是系统独立管理的对象个体。所以在系统层面,都可以通过技术手段实现二者的控制。而且二者所具有的状态都非常相似。而且,在多任务程序中,子进程(子线程)的调度一般与父进程(父线程)平等竞争。

进程是资源分配的基本单位,线程是调度的基本单位。

这句经典名言已流传数十年,各种操作系统教材都可见此描述。确实如此,这就是二者的显著区别。读者请注意“基本”二字。相信有读者看到前半句的时候就在心里思考,“进程岂不是不能调度?”,非也!进程和线程都可以被调度,否则多进程程序该如何运行呢!

只是,线程是更小的可以调度的单位,也就是说,只要达到线程的水平就可以被调度了,进程自然可以被调度。它强调的是分配资源时的对象必须是进程,不会给一个线程单独分配系统管理的资源。若要运行一个任务,想要获得资源,最起码得有进程,其他子任务可以以线程身份运行,资源共享就行了。

简而言之,进程的个体间是完全独立的,而线程间是彼此依存的。多进程环境中,任何一个进程的终止,不会影响到其他进程。而多线程环境中,父线程终止,全部子线程被迫终止(没有了资源)。而任何一个子线程终止一般不会影响其他线程,除非子线程执行了exit()系统调用。任何一个子线程执行exit(),全部线程同时灭亡。

其实,也没有人写出只有线程而没有进程的程序。多线程程序中至少有一个主线程,而这个主线程其实就是有main函数的进程。它是整个程序的进程,所有线程都是它的子线程。我们通常把具有多线程的主进程称之为主线程。

进程的备份关系森严,在父进程没有结束前,所有的子进程都尊从父子关系,也就是说A创建了B,则A与B是父子关系,B又创建了C,则B与C也是父子关系,A与C构成爷孙关系,也就是说C是A的孙子进程。在系统上使用pstree命令打印进程树,可以清晰看到备份关系。

多线程间的关系没有那么严格,不管是父线程还是子线程创建了新的线程,都是共享父线程的资源,所以,都可以说是父线程的子线程,也就是只存在一个父线程,其余线程都是父线程的子线程。

基本命令

1.chmod

chmod

# 权限 rwx 二进制
7 读 + 写 + 执行 rwx 111
6 读 + 写 rw- 110
5 读 + 执行 r-x 101
4 只读 r– 100
3 写 + 执行 -wx 011
2 只写 -w- 010
1 只执行 –x 001
0 000

2.软连接和硬链接

2.1 硬链接

硬链接是通过索引节点进行的链接。在Linux中,多个文件指向同一个索引节点是允许的,像这样的链接就是硬链接。硬链接只能在同一文件系统中的文件之间进行链接,不能对目录进行创建。如果删除硬链接对应的源文件,则硬链接文件仍然存在,而且保存了原有的内容,这样可以起到防止因为误操作而错误删除文件的作用。由于硬链接是有着相同 inode 号仅文件名不同的文件,因此,删除一个硬链接文件并不影响其他有相同 inode 号的文件。

1
2
link oldfile newfile 
ln oldfile newfile

2.2 软链接

软链接(也叫符号链接)与硬链接不同,文件用户数据块中存放的内容是另一文件的路径名的指向。软链接就是一个普通文件,只是数据块内容有点特殊。软链接可对文件或目录创建。

软链接主要应用于以下两个方面:一是方便管理,例如可以把一个复杂路径下的文件链接到一个简单路径下方便用户访问;另一方面就是解决文件系统磁盘空间不足的情况。例如某个文件文件系统空间已经用完了,但是现在必须在该文件系统下创建一个新的目录并存储大量的文件,那么可以把另一个剩余空间较多的文件系统中的目录链接到该文件系统中,这样就可以很好的解决空间不足问题。删除软链接并不影响被指向的文件,但若被指向的原文件被删除,则相关软连接就变成了死链接。

1
2
ln -s old.file soft.link
ln -s old.dir soft.link.dir

实验

1. crontab

crontab -e打开设置文件,crontab可能并没有在ubuntu上安装,使用sudo apt-get install 来安装。

在crontab -e后打开的文件里修改。

修改的规则如下:

1
f1 f2 f3 f4 f5 program
  • 其中 f1 是表示分钟,f2 表示小时,f3 表示一个月份中的第几日,f4 表示月份,f5 表示一个星期中的第几天。program 表示要执行的程序。
  • 当 f1 为 * 时表示每分钟都要执行 program,f2 为 * 时表示每小时都要执行程序,其馀类推
  • 当 f1 为 a-b 时表示从第 a 分钟到第 b 分钟这段时间内要执行,f2 为 a-b 时表示从第 a 到第 b 小时都要执行,其馀类推
  • 当 f1 为 */n 时表示每 n 分钟个时间间隔执行一次,f2 为 */n 表示每 n 小时个时间间隔执行一次,其馀类推
  • 当 f1 为 a, b, c,… 时表示第 a, b, c,… 分钟要执行,f2 为 a, b, c,… 时表示第 a, b, c…个小时要执行,其馀类推
1
2
3
4
5
6
7
8
*    *    *    *    *
- - - - -
| | | | |
| | | | +----- 星期中星期几 (0 - 6) (星期天 为0)
| | | +---------- 月份 (1 - 12)
| | +--------------- 一个月中的第几天 (1 - 31)
| +-------------------- 小时 (0 - 23)
+------------------------- 分钟 (0 - 59)
1
*/3 * * * * date >> /home/wangchenghua/hello.txt

2.exec

  • execve函数是真正意义上的系统调用

  • 其他为经过包装的库函数,最终调用的还是execve函数。

  • 带“l”表示以列表的形式传参数

  • 带“v”表示以数组的形式传参数

  • 带“e”表示将环境变量传递给函数

  • 带“p”表示第一个参数filename不用输入完整的路径,只要给出命令名即可,它会在环境变量PATH中查找命令

该函数定义在<unistd.h>

1
2
3
4
5
6
7
8
9
10
11
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
int main()
{
char * argv[] = {"wensen.sh", 0}; // 数组最后一位需要为0
execvp("/home/wensen/workspace/test/wensen.sh", argv);
return 0;
}
————————————————
原文链接:https://blog.csdn.net/u011857683/article/details/81160059

需要注意的是,执行的时候,传入的参数的第一个必须是这个被执行文件的文件名,最后的一位数组必须是0;

与一般情况不同,exec函数族的函数执行成功后不会返回,因为调用进程的实体,包括代码段,数据段和堆栈等都已经被新的内容取代,只留下进程ID等一些表面上的信息仍保持原样,颇有些神似”三十六计”中的”金蝉脱壳”。看上去还是旧的躯壳,却已经注入了新的灵魂。只有调用失败了,它们才会返回一个-1,从原程序的调用点接着往下执行。

3.进程

进程由程序产生,是动态的,是一个运行着的、要占用系统运行资源的程序。简而言之,进程就是程序的一次运行过程。系统给每一个进程都分配了一个唯一的进程标识符(进程号,简称PID)。

进程相关的参数

PID:进程号(Process ID),用于唯一标识进程。

PPID:父进程号(Parent PID),创建某进程的上一个进程的进程号。

USER/UID:启动某个进程的用户ID和该用户所属组的ID。

STAT:进程状态,颐和进程可能处于多种状态,如运行、等待、停止、睡眠、僵死等。

PRIORITY:进程的优先级,数字越大表示优先级越低。

NICE:进程的谦让度,表示进程对CPU时间要求的迫切程度。

资源占用:包括CPU、内存等资源的占用信息。

进程的类型

  • 交互进程:在Shell下通过执行程序所产生的进程,可在前台或后台运行。

  • 批处理进程:一个进程序列。

  • 守护进程:又称监控进程,是指那些在后台运行,并且没有控制终端的进程,通常可以随着操作系统的启动而运行,也可将其称为服务

在terminal中使用ps命令查看,也可以使用第三方的htop查看

进程的基本状态

  1. 就绪状态
  2. 执行状态
  3. 阻塞状态

进程控制块

  1. 为了描述和控制进程的运行,系统为每个进程定义了一个数据结构,该数据结构被称为进程控制块PCB。
  2. PCB是进程存在的唯一标志.

进程启动

  1. 前台方式启动进程: 打开系统终端,在终端窗口的命令行提示符后输入Linux命令并按回车键,就以前台方式启动了一个进程。
  2. 后台方式启动进程: 在终端下,以后台方式启动进程,需要在执行的命令后面添加一个“&”符号

进程优先级改变

查看目前进程的优先级: ps –l

改变进程优先级的命令——nice命令

【功能】在启动进程时指定请求进程执行优先级

【格式】nice [选项] 命令

【选项】常用的一个选项是“-n”,n值即为NI的值,n值的范围为-20—19。n值越小优先级越高。即,-20代表最高的NI优先级,19代表最低的NI优先级。如果不加该选项,默认NI值为10。

【说明】默认情况下,只有root用户才能提高请求进程的优先级,普通用户只能降低请求进程的优先级

renice命令

【功能】在进程执行时改变NI的值。

【格式】renice [+/-n] [-g 命令名…] [-p 进程标识码…] [-u 进程所有者…]

【说明】可以通过命令名、进程标识码、进程所有者名指定要改变的进程的NI值

kill命令

【功能】终止进程

【格式】kill [-信号] PID

【说明】kill命令用来终止进程,实际是向指定进程发送特定的信号。从而使该进程根据这个信号执行特定的动作。信号可以用信号名称,也可以使用信号码。

pstree命令

【功能】显示进程家族树的信息

【格式】pstree [选项] [进程PID/用户名]

fork命令: 一个进程调用fork来复制自己。创建子进程后,父、子进程执行同一个程序,子进程继承父进程的资源。调用fork()函数后,系统将创建一个与当前进程相同的新的进程。它与原有的进程具有相同的数据、连接关系和从同一处执行的连续性。

1
2
3
4
5
6
7
8
9
10
int main()
{
printf("my pid is %d\n", getpid() );
fork();
fork();
fork();
printf("my pid is %d\n", getpid() );
}

问题:程序会有几行输出?

分辨子进程还是父进程

1
2
3
4
5
6
7
8
9
10
11
12
13
int main()
{
int fork_rv;
printf("Before: my pid is %d\n", getpid());
fork_rv = fork(); /* create new process */

if ( fork_rv == -1 ) /* check for error */
perror("fork");
else if ( fork_rv == 0 )
printf("I am the child. my pid=%d\n", getpid());
else
printf("I am the parent. my child is %d\n", fork_rv);
}

父进程如何等待子进程的退出

Wait做的两件事情:

  • 暂停调用它的进程直到子进程结束
  • 取得子进程结束时传给exit的值

进程一旦调用了wait,就立即阻塞自己,由wait自动分析是否当前进程的某个子进程已经退出,如果让它找到了这样一个已经变成僵尸的子进程,wait就会收集这个子进程的信息,并把它彻底销毁后返回;如果没有找到这样一个子进程,wait就会一直阻塞在这里,直到有一个出现为止。

僵尸进程

注意:在一个进程调用了exit之后,该进程并非马上就消失掉,而是留下一个称为僵尸进程(Zombie)的数据结构。在Linux进程的5种状态中,僵尸进程是非常特殊的一种,它已经放弃了几乎所有内存空间,没有任何可执行代码,也不能被调度,仅仅在进程列表中保留一个位置,记载该进程的退出状态等信息供其他进程收集,除此之外,僵尸进程不再占有任何内存空间。

如何结束僵尸进程?

  • 父进程中使用wait函数,收尸。
  • 把父进程杀掉。父进程死后,僵尸进程成为”孤儿进程”,过继给1号进程init,init始终会负责清理僵尸进程.它产生的所有僵尸进程也跟着消失.

wait()

进程一旦调用了wait,就立即阻塞自己,由wait自动分析是否当前进程的某个子进程已经退出,如果让它找到了这样一个已经变成僵尸的子进程,wait就会收集这个子进程的信息,并把它彻底销毁后返回;如果没有找到这样一个子进程,wait就会一直阻塞在这里,直到有一个出现为止。

参数status用来保存被收集进程退出时的一些状态,它是一个指向int类型的指针。但如果我们对这个子进程是如何死掉的毫不在意,只想把这个僵尸进程消灭掉,(事实上绝大多数情况下,我们都会这样想),我们就可以设定这个参数为NULL,就象下面这样:pid = wait(NULL); 如果成功,wait会返回被收集的子进程的进程ID,如果调用进程没有子进程,调用就会失败,此时wait返回-1

4.线程(重点)

线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。

(1)线程采用了多个线程可共享资源的设计思想。在多进程情况下,每个进程都有自己独立的地址空间,在多线程情况下,同一进程内的线程共享进程的地址空间。线程和进程的最大区别在于线程完全共享相同的地址空间,运行在同一地址上

(2)由于进程地址空间独立而线程共享地址空间,所以从一个线程切换到另一线程所花费的代价比进程低。

(3)进程本身的信息在内存中占用的空间比线程大。因此,线程更能充分地利用内存。线程可以看作是在进程内部执行的指定序列。

(4)线程间的通信比进程间的通信更加方便和省时。进程间的数据空间相互独立,彼此通信要以专门的通信方式进行,通信时必须经过操作系统,而同一进程的多个线程共享数据空间,一个线程的数据可以直接提供给其他线程使用,不必进过操作系统。

1
2
3
4
5
6
7
8
int pthread_create(
pthread_t *restrict tidp, //新创建的线程ID指向的内存单元。
const pthread_attr_t *restrict attr, //线程属性,默认为NULL
void *(*start_rtn)(void *), //新创建的线程从start_rtn函数的地址开始运行
void *restrict arg //默认为NULL。若上述函数需要参数,将参数放入结构中并将地址作为arg传入。
);
————————————————
原文链接:https://blog.csdn.net/wushuomin/article/details/80051295
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include  <pthread.h> // vital
#include <stdio.h>
#icnlude <stdlib.h>
void print_msg(char *m){
int i;
for(i=0 ; i<NUM ; i++){
printf("%s", m);
fflush(stdout);
sleep(1);
}
}
int main()
{
pthread_t t1, t2; /* two threads */

void *print_msg(void *);

pthread_create(&t1, NULL, print_msg, (void *)"hello");
pthread_create(&t2, NULL, print_msg, (void *)"world\n");
pthread_join(t1, NULL);
pthread_join(t2, NULL);
}

pthread_join使得调用线程挂起直至有thread参数指定的线程终止。

多个线程在一个单独的进程中运行,共享全局变量,因此线程间可以通过设置和读取全局变量来进行通信。

对共享内存的访问是线程的一个既有用又极其危险的特性

pthread_mutex_lock

pthread_mutex_unlock

1
2
3
4
5
pthread_mutex_t counter_lock = PTHREAD_MUTEX_INITIALIZER;

pthread_mutex_lock(&counter_lock);
total_words++;
pthread_mutex_unlock(&counter_lock);

5.pwd

1、如何判断是否到达目录树的顶点?

在unix文件系统的根目录中“.”和“..”指向同一个i-节点时,就以认为到达树的顶端。

2、如何正确显示目录名?

递归地调用并显示,或者使用栈数据结构;

命令pwd的实现流程

  1. 得到”.”的i-节点号,称其为n(使用stat);
  2. chdir ..,切换到上一级目录( 使用chdir);
  3. 找到i-节点号n所对应的链接名称(使用opendir, readdir, closedir),重复直到树的顶端;
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
#include<stdio.h>
#include<sys/stat.h>
#include<sys/types.h>
#include<dirent.h>
#include<stdlib.h>
#include<string.h>

#define SIZE 128

ino_t get_inode(char *dirname);
void get_work_dir(ino_t inode_num);
void inode_to_dirname(ino_t inode_num, char *buf, int buflen);

int main(void){
get_work_dir(get_inode("."));
printf("\n");
return 0;

}

ino_t get_inode(char *dirname){
struct stat info;
if (stat(dirname, &info) == -1){
perror("dirname");
exit(1);
}

return info.st_ino;
}

void get_work_dir(ino_t inode_num){
ino_t parent_inode;
char buf[SIZE];
if (get_inode("..") != inode_num){
chdir("..");
inode_to_dirname(inode_num, buf, SIZE);
parent_inode = get_inode(".");
get_work_dir(parent_inode);
printf("/%s", buf);
}
}

void inode_to_dirname(ino_t inode_num, char *buf,int buflen){
DIR *dir_ptr;
struct dirent *dire;
if ((dir_ptr = opendir(".")) == NULL){
perror(".");
exit(1);
}

while ((dire = readdir(dir_ptr)) != NULL){
if (dire->d_ino == inode_num){
strncpy(buf, dire->d_name, buflen);
buf[strlen(buf)] = '\0';
closedir(dir_ptr);
return ;
}
}
fprintf(stderr, "error looking for inode number %d\n", (int)inode_num);
exit(1);
}

6.ls

在Linux中文件和目录都被组织目录树,每个节点或者是文件或者是目录,U盘等也是挂载到某个特定的目录,所以ls只要考虑这两种情况,不需要去考虑分区。

目录是一种特殊的文件。

  1. 打开当前目录的对应的dirp文件;

DIR* dir_ptr = opendir(".");

  1. 读取当前目录的dir_ptr结构中的文件名,并打印;

direntp = readdir(dir_ptr)

printf("%s\n", direntrp->name);

  1. 最后关闭打开的dirp文件;

问题

1、怎么样把mode:100644转化为”-rw-r–r–”?

2、user: 1000怎么样转化为tfzhang?

3、group: 1000怎么样转化为tfzhang?

4、变换modtime?

与操作提取数字位

通过每位与操作,将每组的rwx位提取出来;比如要提取右起第3位,只要将数值100644与4这个数进行与操作;

7.cat

复习不完了,:cry: .

考完了,还算顺利,注意下各种函数的参数位置,着重复习线程与进程还有pwd,cat,ls的实现,没了,挂不了。

作者

WangCH

发布于

2021-06-24

更新于

2021-06-27

许可协议

评论