Linux基础知识

常用命令篇

tar归档与压缩

  1. tar的作用是打包文件,即将多个文件合成一个;(.tar)<br /> 而后再对该程序进行压缩,比如gzipbzip2等等;(.tar.gz)<br /> **它本身是不具备压缩功能,是调用其他的压缩程序实现的;(如,-z命令其实就是为了调用gzip压缩)**<br /> [https://zhidao.baidu.com/question/1833802576161577100.html](https://zhidao.baidu.com/question/1833802576161577100.html)

Git命令

Git是一个开源的分布式版本控制系统,主要用来实现代码版本的管理。
最常用的四种命令:add、commit、reset、checkout。

三分区

本地Git的工作模式分为三个分区:working directory(工作目录,肉眼可见)、stage/index area(暂存区)、commit history(提交历史)。
它们三者之间的状态转移关系如下:

常用命令

git add=git stage(别名),将修改存至暂存区;
git check out.或者指定文件,可以恢复到stage的状态;
PS:check out命令只会将文件修改为stage的状态,但是却不会删除在这之间创建的文件;
git commit -m“描述”
将stage区的文件添加到history区,如果只是小改动不想另建一个commit,可以使用git commit —amend将如今的修改和之前的修改合并。
git reset,可以实现将history中的版本还原到stage区;
git add 而后git commit 或者直接git add -a 可以直接将work directory提交到history区。
git checkout HEAD .就可以从history恢复之前的状态到work directory;
ps:git checkout somename 即可回到某个阶段的分支或状态
使用^,可以移动到checkout main^中main的上一个节点;
git branch -f name1 name2 可以将分支强制指向另外一个节点;

学习网站

在线学习网站—闯关:
https://learngitbranching.js.org/?locale=zh_CN
在线教程网站:
https://www.runoob.com/git/git-tutorial.html

Cmake

参考教程:
https://blog.csdn.net/zhuiyunzhugang/article/details/88142908
一些语法解释:
https://blog.csdn.net/zhanghm1995/article/details/80902807
该教程一共有多个Demo,依次介绍了工程由简到繁地全过程。
源代码保存在OwnProject/CmakeLearning中

Cmake有什么用

不同平台上有各种Make工具,如GNU Make,Qt的Qmake,微软的MSMake等,它们之间的makefile格式不同,这给跨平台编译制造了麻烦。Cmake的最突出的特点就是“一次编写,任意运行”,它不依赖于平台,而是由运行时的平台来选择生成Makefile文件。

Demo

1.3.2.1 Demo1
单cpp的构建;
cmake_minimum_required():所需的 CMake 的最低版本; project():参数值是 Demo1,该命令表示项目的名称是 Demo1 。 add_executable(): 源文件编译成可执行文件。
1.3.2.2 Demo2
main.cpp加上其他的源文件 且在同一目录;
# 查找目录下的所有源文件 # 并将名称保存到 DIR_SRCS 变量 aux_source_directory(. DIR_SRCS)
1.3.2.3 Demo3
多个目录,多个源文件;这就涉及多个Cmakelists了;
# 添加 math 子目录# 这样math目录中的CMakeLists也会进行处理;(处理时会进入子目录运行Cmake) add_subdirectory(math) # 添加链接库:Demo去链接MathFunctions target_link_libraries(Demo MathFunctions) # 生成链接库 add_library (MathFunctions ${DIR_LIB_SRCS})
1.3.2.4 Demo4
增加了自定义编译选项;
# 加入一个配置头文件,用于处理 CMake 对源码的设置(config.h由config.h.in在Cmake时生成,在其中定义了变量USE_MYMATH,利用ccmake可视化编辑) configure_file ( “${PROJECT_SOURCE_DIR}/config.h.in” “${PROJECT_BINARY_DIR}/config.h” ) # 是否使用自己的 MathFunctions 库 option (USE_MYMATH “Use provided math implementation” ON) # 是否加入 MathFunctions 库 if (USE_MYMATH) include_directories (“${PROJECT_SOURCE_DIR}/math”) add_subdirectory (math) set (EXTRA_LIBS ${EXTRA_LIBS} MathFunctions) endif (USE_MYMATH) # 注意cmake中的if和endif结尾
1.3.2.5 Demo5
安装和测试功能,即make install和make test;
在math/CMakeLists.txt中添加 # 指定 MathFunctions 库的安装路径 install (TARGETS MathFunctions DESTINATION bin)//库目录安装到 /usr/local/bin install (FILES MathFunctions.h DESTINATION include)//头文件安装到/usr/local/include PS:安装路径可以通过修改CMAKE_INSTALL_PREFIX来更改 1、cmake -DCMAKE_INSTALL_PREFIX=/usr .. 2、SET(CMAKE_INSTALL_PREFIX < install_path >)(加在projectname之后)
make之后运行make install即可。
make test其实就是在CMakeList之后添加一些测试用例,暂时省略。
打开gdb调试,在Debug模式下打开:
set(CMAKE_BUILD_TYPE “Debug”) set(CMAKE_CXX_FLAGS_DEBUG “$ENV{CXXFLAGS} -O0 -Wall -g -ggdb”) set(CMAKE_CXX_FLAGS_RELEASE “$ENV{CXXFLAGS} -O3 -Wall”)
1.3.2.6 Demo6
环境检查:查看当前环境中是否已有某参数或库;
1.3.2.7 Demo7
添加版本号:
set (Demo_VERSION_MAJOR 1) set (Demo_VERSION_MINOR 0) 而后在confi.h.in中添加宏定义 // the configured options and settings for Tutorial #define Demo_VERSION_MAJOR @Demo_VERSION_MAJOR@ #define Demo_VERSION_MINOR @Demo_VERSION_MINOR@
1.3.2.8 Demo8
生成安装包,需要用到 CPack,专门来打包源文件;
具体的操作和过程请结合文件以及教程观看。
PS:可以利用各种工具将不同平台的项目迁移到cmake,比如am2make、qmake converter等;

Make和CMake的关系

写程序大体步骤为:
1.用编辑器编写源代码,如.c文件。
2.用编译器编译代码生成目标文件,如.o。
3.用链接器连接目标代码生成可执行文件,如.exe。
但如果源文件太多,一个一个编译时就会特别麻烦,于是人们想到,为什么不设计一种类似批处理的程序,来批处理编译源文件呢,于是就有了make工具,它是一个自动化编译工具,你可以使用一条命令实现完全编译。但是你需要编写一个规则文件,make依据它来批处理编译,这个文件就是makefile,所以编写makefile文件也是一个程序员所必备的技能。

对于一个大工程,编写makefile实在是件复杂的事,于是人们又想,为什么不设计一个工具,读入所有源文件之后,自动生成makefile呢,于是就出现了cmake工具,它能够输出各种各样的makefile或者project文件,从而帮助程序员减轻负担。但是随之而来也就是编写cmakelist文件,它是cmake所依据的规则。所以在编程的世界里没有捷径可走,还是要脚踏实地的。

Grep与管道

grep全称是Global Regular Expression Print,表示全局正则表达式版本,它的使用权限是所有用户。
它可以与cat、wc等命令用|连接,这表示管道命令,它们之间是父子进程的关系
Linux grep 命令用于查找文件里符合条件的字符串
grep 指令用于查找内容包含指定的范本样式的文件,如果发现某文件的内容符合所指定的范本样式,预设 grep 指令会把含有范本样式的那一列显示出来。若不指定任何文件名称,或是所给予的文件名为 -,则 grep 指令会从标准输入设备读取数据

find命令

Linux find 命令用来在指定目录下查找文件。任何位于参数之前的字符串都将被视为欲查找的目录名。

Chmod与所有权、所有组

Linux 文件调用权限分为三级:文件所有者(谁创建了文件)、用户组(跟所有者一个组的用户)、其他用户,chmod可以修改用户对文件的权限。

PS:可用whoami或id -un查询当前用户;id -gn查询当前用户组;
chown改变文件所有者;
chgrp改变文件所在组;

mmap共享内存

https://blog.csdn.net/baidu_39486224/article/details/84449784

其他常用命令(什锦/链接即可)

last:输出系统最近登录的用户;
wc:根据-m、-c等打印出文件的字符数、字节数。

编程架构篇

信号量

在unix编程中,Signal信号量属于异步处理异常事件的机制之一。
常见的信号量如下:

以SIGCHLD为例,该信号是当子进程终止时发送给父进程的,默认忽略。
PS:信号处理函数存在的问题在于:待处理信号被阻塞、待处理信号不会排队等待(只有第一个有用)、系统调用可以被中断。详见下述例子:
https://blog.csdn.net/github_33873969/article/details/77744382

常用函数

pid_t wait(int *status)
分析是否有子进程退出,成功则返回子进程ID,否则-1;

pid_t waitpid(三个参数)
与前者的区别在于指定pid,多了两个参数,更灵活。
详见:
http://blog.chinaunix.net/uid-25365622-id-3045460.html

setsid()
其作用就是赋予子进程一个新的session,实现子进程与父进程(控制终端、原进程组、原会话)脱离。主要用来创建守护进程。
eg:pid_t pid = fork(); //fork a process
if (pid < 0) exit(0); //fork error
if (pid > 0) exit(0); //father process exit
setsid(); [1] //creat a new session for a process

umask(int n)
设置当前进程组的最大目录权限,如果是umaks(0)则默认具有777权限。(rwx,4+2+1=7,777即是该用户、所在组、其他人都具有最高权限

chdir(char*)
用于改变工作目录;

popen(charcommand,char type)
其内部实现是调用fork()产生一个子进程,执行一个shell以运行command命令,并读取或写入标准输出/输入
详见:
https://www.jb51.net/article/140783.htm

char fgets(char str, int n, FILE *stream)
从指定的流 stream 读取一行,并把它存储在 str 所指向的字符串内。当读取 (n-1) 个字符(因为最后一个字符是空字符‘/0’)时,或者读取到换行符时,或者到达文件末尾时,它会停止。

char strtok(char str, const char *delim)
分解字符串 str 为一组字符串,delim 为分隔符。返回第一个被分割的子串。
https://www.runoob.com/cprogramming/c-function-strtok.html

execl(char path,char argv,···)
执行对应路径的文件,并传递后续的参数(最后一个必为NULL)。

int kill(pid_t pid, int sig)
给对应pid的进程发送一个SIGNAL(sig表示),对应进程接收之后不关心,成功发送返回0。

fork()
返回值,负数表示失败;0表示为子进程;正数表示返回给父进程子进程的ID。

getpid()
获取当前进程的pid。

vfork

1、保证子进程先运行,在它调用 exec(进程替换) 或 exit(退出进程) 父进程才可能被调度运行。如果子进程没有调用 exec, exit, 程序则会导致死锁,程序是有问题的程序,没有意义
2、子进程共享父进程的地址空间,fork虽然子进程虽然和父进程拥有同样的资源,但不是共享地址空间的
注意:return是函数的返回,返回后释放堆栈资源,exit是进程的结束,系统级别的,直接退出整个进程。
1. fork ():子进程拷贝父进程的数据段,代码段
vfork ( ):子进程与父进程共享数据段
2. fork ()父子进程的执行次序不确定
vfork 保证子进程先运行,在调用exec 或exit 之前与父进程数据是共享的,在它调用exec
或exit 之后父进程才可能被调度运行。
3. vfork ()保证子进程先运行,在她调用exec 或exit 之后父进程才可能被调度运行。如果在调用这两个函数之前子进程依赖于父进程的进一步动作,则会导致死锁。

文件系统

Linux系统文件系统结构大致如下:

/root:超级用户的家目录;
/bin:binary,存放可执行文件或链接,常用命令如cat、cp等;
/sbin:system binary,存放的命令可对系统配置进行查看或更改(更改需要sudo);
/boot:存放系统启动所需的文件,如grub;
/dev:存放系统中所有的设备文件(Linux中任何东西都是文件!),如磁盘、键盘、鼠标等;
/etc:主要存放一些软件或系统的配置信息;
/lib:library,存放bin可执行文件的一些依赖库;
/media:存放自动挂载的设备文件,如移动硬盘、网络设备、或者U盘;
/mnt:存放手动挂载的设备文件;
/opt:无明确使用,但一般用作软件的安装路径;
/proc:process,存放正在运行的进程的状态信息
/run和/sys:存储某些程序和系统的运行信息,如显卡亮度;
/srv:service,如果是linux服务器,主要用来存放Web服务器或FTP服务器的资源文件;
/tmp:系统或软件的临时文件保存路径;
/usr:用户自行安装的一些依赖或应用;
/var:主要存储日志信息(log);

硬链接与软链接?

软链接最好理解,就是一个“指针”类似于windows的快捷方式,一旦源文件出问题,就会报错;
硬链接相当于建立了一个副本,实际只有一个文件,两者互相影响,只有删掉两者该文件才彻底消失(狡兔三窟

IO复用的原理以及几种函数(epoll、poll、select)的理解?

IO复用是Linux中的IO模型之一,IO复用就是进程预先告诉内核需要监视的IO条件,使得内核一旦发现进程指定的一个或多个IO条件就绪,就通过进程处理,从而不会在单个IO上阻塞了。Linux中,提供了select、poll、epoll三种接口函数来实现IO复用。
Select
select(默认水平触发)的缺点:
1、select支持的文件描述符太小,默认1024
2、select采用轮询的方式扫描文件描述符,文件描述符数量越多,性能越差;
3、每次调用select都需要将fd集合从用户态拷贝到内核态,且要在内核遍历所有传递进来的fd,这个开销在fd很多时,开销也很大。
Poll
与select相比,poll使用链表保存文件描述符,没有了监视文件数量的限制,但其他三个缺点依然存在。
Epoll
epoll使用一个文件描述符管理多个描述符,将用户关系的文件描述符的事件存放到内核的一个事件表中,这样在用户空间和内核空间的copy只需一次。上面所说的select缺点在epoll上不复存在,
Epoll是事件触发的,不是轮询查询的。没有最大的并发连接限制。
网上总结:epoll是Linux下多路复用IO接口select/poll的增强版本,①它能显著提高程序在大量并发连接中只有少量活跃的情况下的系统CPU利用率,因为它会复用文件描述符集合来传递结果而不用迫使开发者每次等待事件之前都必须重新准备要被侦听的文件描述符集合,②另一点原因就是获取事件的时候,它无须遍历整个被侦听的描述符集,只要遍历那些被内核IO事件异步唤醒而加入Ready队列的描述符集合就行了。③epoll除了提供select/poll那种IO事件的电平触发 (Level Triggered)外,还提供了边沿触发(Edge Triggered),这就使得用户空间程序有可能缓存IO状态,减少epoll_wait/epoll_pwait的调用,提高应用程序效率。
优秀博客:https://www.cppfans.org/1417.html

epoll 的 LT 和 ET 模式的理解:

epoll对文件描述符的操作有两种模式:LT(level trigger水平触发)和ET(edge trigger边沿触发)
区别:
LT模式:是默认/缺省的工作方式,同时支持 block和no_block socket。这种工作方式下,内核会通知你一个fd是否就绪,然后才可以对这个就绪的fd进行I/O操作。就算你没有任何操作,系统还是会继续提示fd已经就绪,不过这种工作方式出错会比较小,传统的select/poll就是这种工作方式的代表。
ET(edge-triggered) 是高速工作方式,仅支持no_block socket,这种工作方式下,当fd从未就绪变为就绪时,内核会通知fd已经就绪,并且内核认为你知道该fd已经就绪,不会再次通知了,除非因为某些操作导致fd就绪状态发生变化。如果一直不对这个fd进行I/O操作,导致fd变为未就绪时,内核同样不会发送更多的通知,因为only once。所以这种方式下,出错率比较高,需要增加一些检测程序。
面试官:
epoll内核用什么数据结构?
RBTree与双向队列
epoll怎么处理信号?管道。
epoll怎么监听事件?epoll_wait()

三者的细分?

(1)select==>时间复杂度O(n)
它仅仅知道了,有I/O事件发生了,却并不知道是哪那几个流(可能有一个,多个,甚至全部),我们只能无差别轮询所有流,找出能读出数据,或者写入数据的流,对他们进行操作。所以select具有O(n)的无差别轮询复杂度,同时处理的流越多,无差别轮询时间就越长。
(2)poll==>时间复杂度O(n)
poll本质上和select没有区别,它将用户传入的数组拷贝到内核空间,然后查询每个fd对应的设备状态, 但是它没有最大连接数的限制,原因是它是基于链表来存储的.
(3)epoll==>时间复杂度O(1)
epoll可以理解为event poll,不同于忙轮询和无差别轮询,epoll会把哪个流发生了怎样的I/O事件通知我们。所以我们说epoll实际上是事件驱动(每个事件关联上fd)的,此时我们对这些流的操作都是有意义的。(复杂度降低到了O(1))

select,poll,epoll都是IO多路复用的机制。I/O多路复用就通过一种机制,可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作。但select,poll,epoll本质上都是同步I/O,因为他们都需要在读写事件就绪后自己负责进行读写,也就是说这个读写过程是阻塞的,而异步I/O则无需自己负责进行读写,异步I/O的实现会负责把数据从内核拷贝到用户空间