1.linux版本
内核版本 1991,芬兰大学生 www.kernel.org
发行版本 公司对内核版本进行软件/操作集成,更方便使用 选最稳定的版本
2.linux基本命令
目录:
cd 切换目录
pwd 显示当前路径
mkdir 创建目录
rmdir 删除目录
文件:
cp 拷贝文件
mv 粘贴/重命名文件
vi 打开文件编译器
more filename 查看文件内容(每次显示固定行)
cat filename 查看文件内容(一次性显示完)
head -n 查看文件前n行代码
tail -n 查看文件后n行代码
find filename 查看当前目录是否该文件
file filename 查看文件类型
chmod 修改文件权限
grep | 文本搜索
sed 替换文件中某些字符串的值
awk 把文件逐行的读入,以空格为默认分隔符将每行切片,切开的部分再进行各种分析处理。每接收文件的一行,然后执行相应的命令,来处理文本
进程:
ps 查看运行态的进程
ps -ef 查看运行态进程的所有信息
kill 挂起进程/结束进程
jobs 显示Linux中的任务列表及任务状态,包括后台运行的任务。
top 实时显示系统中各个进程的资源占用状况(CPU利用率/内存占用率)
管理:
useradd username 添加新用户
userdel username 删除用户
passwd name 修改用户密码
su username 切换用户
shutdown -h now现在关机 (root权限)
shutdown -r now现在重启 (root权限)
init 0 关机 (root权限)
init6 重启 (root权限)
halt关机 (普通用户)
reboot重启 (普通用户)
mail查看邮箱
网络:
netstat 显示网络连接、路由表和网络接口信息,可以让用户得知有哪些网络连接正在运作,可以查看进程占用的端口号 (root权限下使用)
lsof 显示系统打开的文件
打印了一堆我也不知道是什么的东东……
tcpdump 对网络上的数据包截获
其他常用命令:
ldd 查看程序用到的共享库
nm exename 查看exe文件中的偏移量
3.linux系统的目录结构
/boot linux内核启动的核心文件
/etc 配置文件
/bin 系统使用的命令
/lib 库文件
/home 用户的家目录,所有用户都在该目录下有以自己用户名命名的家目录
/mnt 用户临时挂载文件的挂载点
/proc 虚拟目录,存进程的信息,关机后消失
/dev linux的外部设备
/root 系统管理员的家目录
4.文件类型
-普通文件
d目录文件
p管道文件
l链接文件
c/b设备文件
s套接字文件
5.系统运行级别
0关机
1单用户模式,用于维护系统
2不带网络的多用户模式
3完整的多用户模式
4保留,用户可以自己定义
5图形化界面的多用户
6重启系统
-
6.vi文本编辑器
vi 文件名 打开文件
i 进入编辑文件
-
退出编辑模式
q 退出文本编辑器
!q 强制退出文本编译器
wq 保存退出
!wq 强制退出并保存修改内容 - :set nu 加行号
-
:set nonu 去行号
nyy 拷贝n行
ndd 剪切n行
p 粘贴
ndd删除n行
u撤销
7.修改文件权限 chmod
u创建者 g主用户 o其他用户
r读权限4 w写权限2 x执行权限1
chmod u+x file 给该文件的创建者加可执行权限
chomd 0666 file 将文件的权限改为创建者/主用户/其他用户可读可写
8.g++和gcc有什么区别?
对于下面一段代码,分别创建test.c main.cpp
#include <stdio.h>
int Add(int a, int b)
{
return a+b;
}
int main()
{
printf("main:%x, add:%x\n", main, add);
return 0;
}
<1>用gcc编译test.c,生成testc可执行文件
可以看到,gcc可以编译C语言代码,Add函数的符号名是按照编译C语言的方式处理的
<2>用g++编译test.c生成testc++可执行文件
可以看到,g++可以编译C语言代码,Add函数的符号名是按照编译C++的方式处理的
通过ls -l,查看文件详细信息,发现testc++比testc的文件大!
<3>用gcc编译main.cpp,生成mainc可执行文件
链接出错了!gcc默认是不能编译C++文件的,gcc不能自动链接C++的标准库stdc++,如果要用gcc编译C++文件,要手动链接++标准库。
手动链接C++标准库,再来一次
链接成功!
可以看到,gcc默认不能编译C++代码,因为gcc不能自动链接C++标准库文件,手动链接库文件后,gcc按照C++方式处理程序。
<4>用g++编译main.cpp生成mainc++可执行文件
可以看到,g++可以编译C++代码,按照C++方式处理程序。
通过ls -l,查看文件详细信息,发现mainc比mainc的文件稍微大一点!
现在编译代码时用gcc,更确切的说他应该是一个驱动程序。它会根据代码的后缀名来判断调用C编译器还是C++编译器 (g++)。如果代码是后缀是.c,gcc会调用C编译器,去链接C标准库。如果代码后缀是.cpp,gcc会调用C++编译器,链接C++标准库。
9.makefile文件
makefile简介:makefile是工程管理工具,按照代码的编译顺序逆向写makefile文件,编译时使用make命令编译makefile文件,方便编译。
注意:依赖关系和编译命令,用”换行和Tab键隔开”。
10.gdb常用调试命令
Linux系统上,默认的开发环境是realease版本,没有调试信息。编译时使用-g参数添加调试信息。如:gcc -o main main.c -g
进入调试:gdb main
查看代码:l
添加断点:b
查看断点信息:info break
删除断点:delete 断点号 (delete 1 删除断点号为1的断点)
运行程序:r
查看变量值:p (查看变量入口地址:p 函数名)
持续显示变量a的值:display a
下一步:n
继续运行,到下一个断点停止:c (continue)
进入函数:s
退出函数:finish
查看函数调用堆栈:bt
跟踪子进程:set follow-fork-make child
退出调试:q
11.core文件
在一个程序崩溃时,它会在指定目录(一般为当前目录)下生成一个core文件,主要是用来调试的。
core文件仅仅是一个内存映象(同时加上调试信息)。
core的默认大小是0,所以默认不产生core文件,因为默认是release版本。
core文件是在程序崩溃时才产生的!默认的core文件大小是0,此时即使程序崩溃,也不生成core文件。
查看core文件大小:ulimit -c
更改core文件大小:ulimit -c size
编译链接程序
运行程序
查看core文件:ls
进入core调试:gdb 文件名
开始调试:file ./test
退出调试:q
12.strace 跟踪程序运行中调用的系统调用
13.ltrace 跟踪库函数
14.库
库:预先编译好方法的集合
静态链接库(.a)和动态链接库(共享库.so)的区别?
静态链接库和动态链接库最大的区别是加载方法的时间不同。
静态链接库在编译时,将用到的方法加载进来,包含在可执行文件中(静态运行库中一个目标文件中只包含一个函数,因为加载静态库时候要将包含该函数的库方法都加载进来,太浪费空间)。
动态链接库在编译时对用到的方法做”需要用”标记,运行时将用到的方法加载到内存中。
对同一段程序,采用静态链接生成的exe文件比动态链接的exe文件大一些。
ldd 查看程序需要用到的共享库
15.进程
task_struct结构体中有两个指针,分别指向当前进程的生父和养父。初始值都是它的父进程。
<1>僵死进程
如果子进程先与父进程结束(子进程送SIGCHILD信号给父进程,父进程默认忽略该信号),父进程未获取子进程的状态信息,在未被init进程发现之前,子进程所处的状态是僵死态,此时的子进程就是僵死进程。僵死进程的生父指针和养父指针都是其父进程。当被init进程发现时,内核将其养父指针改为init进程,init负责清楚其占用的进程表,释放其资源。
如果在子进程时僵死状态时,父进程结束了,此时的僵死进程就变成了孤儿进程。内核将其生父指针改为init指针,init负责清楚其占用的进程表,释放其资源。子进程也结束了。
<2>孤儿进程
如果父进程先与子进程结束,此时子进程是孤儿进程,内核将其养父指针该为init进程,孤儿进程被init进程收养,init负责清楚其占用的进程表,释放其资源。子进程结束。
<3>fork
#include <stdio.h>
int main()
{
printf("A");
write(1,"B",1);
fork();
sleep(3);
return 0;
}
结果是:BAA
B立即打印出来的,A在缓冲区中,fork()后,缓冲区的数据也被拷贝了一份,sleep时间到,程序结束时,刷新缓冲区,每个进程缓冲区中的A都被打印出来。
#include<stdio.h>
int main()
{
int i = 0;
for(; i<2; ++i)
{
fork();
printf("A\n");
}
return 0;
}
结果是:AAAAAA
#include<stdio.h>
int main()
{
int i = 0;
for(; i<2; ++i)
{
fork();
printf("A");
}
return 0;
}
结果是:AAAAAAAA
16.进程间通信
(1)管道
<1>性质:管道在内存中存取数据,磁盘上只保留管道的信息,故管道的大小恒为0。
只有在读、写端同时打开时,管道才能被正确打开,否则open一直阻塞。
代码测试管道大小
运行结果为65536(64K)
使用ulimit -a命令查看管道默认大小
因此,Linux系统上,管道默认大小为64K。
<2>无名管道
无名管道只能在父子进程间进行通信,pipe(int pipefd[2])创建出管道后,利用fork复制一个子进程。在子进程中写/读,在父进程中读/写。fd[0]读,fd[1]写
<3>有名管道
创建管道命令:mkfifo filename
程序运行过程中创建管道:
#include <sys/types.h>
int mkfifo(const char *filename,mode_t mode);//管道名称,权限
<4>有名管道和无名管道的区别
无名管道:
–只能在有亲缘关系的进程(多为父子进程)间进行数据通信;
–无名管道是基于进程存在的,只能在程序运行时被创建,每次使用都要创建管道。
有名管道:
–可以在互不相关的进程之间实现通信;
–可以使用命令创建管道,通过管道所在的路径名定位到该管道,两个进程间通信的时候直接使用该管道,不用再创建。在文件系统中可以查看到该管道文件,但大小恒为0;
<5>管道的实现
管道总是从头指针head写,从尾指针tail读。写/读完成后,指针移动到合适位置。当头指针走到缓冲区的末尾时,又从另一端开始写(类似于循环,采取FIFO)
(2)信号量
作用:同步线程
手段:通过PV操作,控制程序的推进速度
临界资源:同一时间只允许有限个进程访问的资源
临界区:访问临界资源的代码段
原子操作:执行过程不能被打断
信号量是大于0的正整数值,信号量>1时,代表可以访问资源的进程数量;信号量为1/0,二元信号量。
P操作:(获取资源操作)原子-1
V操作:(释放资源操作)原子+1
(3)共享内存
共享内存:可以被映射到不同进程的内存空间,没有第三方空间
注意:要控制多个进程对同一共享内存的访问之间的同步(使用二元信号量/记录锁)
共享内存在栈的下方,紧靠着栈
mmap将一个文件的若干部分映射到进程地址空间(映射段与文件相关联)
(4)消息队列
消息队列是在两个不相关进程间传递数据的一种简单、高效方式,她独立于发送进程、接受进程而存在。
int msgget(key_t key, int msgflg);
因为消息队列独立于进程而存在,为了区别不同的消息队列,需要以key值标记消息队列,这样两个不相关进程可以通过事先约定的key值通过消息队列进行消息收发。例如进程A向key消息队列发送消息,进程B从Key消息队列读取消息。
16.线程
线程:程序内部中的一条执行路径。
(1)线程同步
<1>信号量
sen_t sem;
#include <semaphore.h>
int sem_init(sem_t *sem, int pshared, unsigned int value);
int sem_post(sen_t *sem);
int sem_wait(sem_t *sem);
int sem_destroy(sem_t *sem);
<2>互斥锁 互斥锁用于加锁互斥
pthread_mutex_t mutex;
#include <semaphore.h>
int pthrerad_mutex_init(pthread_t *mutex, NULL);
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);
int pthread_destroy(pthread_mutex_t *mutex);
<3>条件变量 条件变量专注于等待
当达到某个条件时,执行某操作。通过设置条件达到同步进程。
17.并发运行与并行
并发运行
在单处理器多道程序设计系统中,在操作系统的管理下,所有正在运行的进程轮流使用CPU,每个进程允许占用CPU的时间非常短(比如10毫秒),这样用户根本感觉不出来CPU是在轮流为多个进程服务,就好像所有的进程都在不间断地运行一样,但实际上在任何一个时间内有且仅有一个进程占有CPU。
由于程序交替出现的顺序不同,所以每次结果几乎都不相同,很难发生预期的结果。
对临界区加锁,对进程同步。
并行
在多处理器多道程序设计系统中,在同一个时间段内,可以同时处理和处理器数目相同的程序,有时间上的重叠。