当前位置: 首页 > news >正文

网站群建设讲话常见c2c网站有哪些

网站群建设讲话,常见c2c网站有哪些,无法与网站建立安全连接,wordpress 缩略图尺寸本文导航一. Linux基础知识杂记0. terminal操作快捷键等1. 为什么vfork的子进程里用return#xff0c;整个程序会挂掉#xff0c;而且exit不会(zz)2. 进程内存管理详解3. 关于堆和自由存储区概念的区别4. cache和buffer的区别5. C实现线程池6. 静态函数和虚函数的区别7. C里是… 本文导航一. Linux基础知识杂记0. terminal操作快捷键等1. 为什么vfork的子进程里用return整个程序会挂掉而且exit不会(zz)2. 进程内存管理详解3. 关于堆和自由存储区概念的区别4. cache和buffer的区别5. C实现线程池6. 静态函数和虚函数的区别7. C里是怎么定义常量的常量存放在内存的哪个位置8. 编译链接程序过程9. C和C中的const变量存储在那10. Cpriority_queue方法一priority_queue方法二利用vector11. 友元12. gcc语法选项参数实例头文件链接选项 -I编译时定义宏-D自测实例13. find命令14. linux系统目录和文件类型15. ls16. cp17. cat18. more19. grep20. echo20. 补充命令20.1 ps20.2 20.3 man21. vim21.1 vim配置文件21.2 vim命令及模式切换1**由命令模式切换到文本模式**2**命令模式光标移动**3**自动调整格式**4**大括号对应跳转切换**5**删除**6**光标移动**7**撤销上一步操作**8**恢复上一步被撤销的操作**9**复制**10**粘贴**11**折叠**12**分屏**13剪切14块移动15处理大小写22. gdb调试gdb交互命令运行设置断点查看源代码打印表达式查询运行信息分割窗口23. Linux进程状态1进程状态24. 环境变量Linux环境变量分类Linux设置环境变量的方法Linux环境变量使用25. 内核空间和用户空间26. Makefile27. 中断处理流程28. 内核信号捕捉过程29. Linux x86程序启动-main如何被执行到30. Linux内存管理1进程空间数据结构关系31. valgrind调试32. gdb core文件调试33. 调试段错误的方法34. 函数调用过程35. linux内核解析笔记35.1register 寄存器35.2 寻址模式和简单指令35.3 函数调用过程36. linux下检查内存状态的命令**1、free命令****2、vmstat命令****3、top命令****4、cat /proc/meminfo**5、 ps aux命令37. nm命令一. Linux基础知识杂记 0. terminal操作快捷键等 终端的设置项 对终端的设置主要包括配置文件首选项和键盘快捷键的设置。选择“编辑” 菜单进入相关的设置。 创建终端的标签页 1 通过 CtrlShiftT 快捷键创建新标签页。 2 选择 “文件” 菜单 - 选择“打开标签页” 选项。 3 在终端中单击鼠标右键或按下键盘上的Application 键选择 “打开标签页” 选项。 终端默认的常用快捷键 快捷键描述CtrlAltT启动终端F1打开帮助指南F10激活菜单栏F11全屏切换AltF打开 “文件” 菜单fileAltE打开 “编辑” 菜单editAltV打开 “查看” 菜单viewAltS打开 “搜索” 菜单searchAltT打开 “终端” 菜单terminalAltH打开 “帮助” 菜单helpCtrlShiftC复制CtrlShiftV粘贴CtrlShiftT新建标签页Ctrl Shiftn在已有终端上打开一个新的终端窗口CtrlShiftW关闭标签页CtrlShiftN新建终端窗口CtrlShiftQ关闭终端窗口CtrlShiftPgUp标签页左移CtrlShiftPgDn标签页右移AltN切换到第 N个标签页N0…9CtrlPgUp切换到上一个标签页page upCtrlPgDn切换到下一个标签页pagedownCtrlAltFn切换到字符界面n1…6 如果需要切换回图形界面需要使用CtrlAltF7 或 AltF7CtrlShift放大窗口包括窗口内的字体Ctrl±缩写窗口包括窗口内的字体Ctrl0普通大小阿拉伯数字 0Ctrld关闭一个tab即关闭一个终端上的标签CtrlP显示上一条历史命令同 up arrow功能CtrlN显示下一条历史命令同 down arrow功能CtrlR反向搜索历史命令CtrlO回车同enter 键功能CtrlJ回车同enter 键功能CtrlM回车同enter 键功能CtrlA光标移动到行的开头CtrlE光标移动到行的结尾CtrlB光标向后移动一个位置backwardCtrlF光标向前移动一个位置forwardCtrlLeft-Arrow光标移动到上一个单词的词首CtrlRight-Arrow光标移动到下一个单词的词尾CtrlT将光标位置的字符和前一个字符进行位置交换CtrlU剪切从行的开头到光标前一个位置的所有字符CtrlK剪切从光标位置到行末的所有字符CtrlY粘贴 ctrlu或者 ctrlk 剪切的内容CtrlH删除光标位置的前一个字符同backspace 键功能Ctrl*删除光标位置的前一个字符同 ctrlh组合键功能CtrlD删除光标位置的一个字符同 delete键功能CtrlW删除光标位置的前一个单词同altbackspace 组合键功能Ctrl恢复 ctrlh或者 ctrld 或者 ctrlw 删除的内容CtrlL清除当前屏幕内容同 clear命令功能CtrlS暂停屏幕输出CtrlQ继续屏幕输出 小技巧 在终端窗口命令提示符下连续按两次 Tab键、或者连续按三次 Esc 键、或者按 CtrlI组合键将显示所有的命令及工具名称。 1. 为什么vfork的子进程里用return整个程序会挂掉而且exit不会(zz) #include #include #include intmain(void) { intvar; var 88; if((pid vfork()) 0) {printf(vfork error);exit(-1); } elseif(pid 0) { /* 子进程 */var;return0; } printf(pid%d, glob%d, var%d\n, getpid(), glob, var); return 0; }基础知识 首先说一下fork和vfork的差别 fork 是 创建一个子进程并把父进程的内存数据copy到子进程中。vfork是 创建一个子进程并和父进程的内存数据share一起用。 这两个的差别是一个是copy一个是share。关于fork可以参看酷壳之前的《一道fork的面试题》 你 man vfork 一下你可以看到vfork是这样的工作的 1保证子进程先执行。 2当子进程调用exit()或exec()后父进程往下执行。 那么为什么要干出一个vfork这个玩意 原因在man page也讲得很清楚了 意思是这样的—— 起初只有fork但是很多程序在fork一个子进程后就exec一个外部程序于是fork需要copy父进程的数据这个动作就变得毫无意了而且这样干还很重注后来fork做了优化详见本文后面所以BSD搞出了个父子进程共享的 vfork这样成本比较低。因此vfork本就是为了exec而生。 为什么return会挂掉exit()不会 从上面我们知道结束子进程的调用是exit()而不是return如果你在vfork中return了那么这就意味main()函数return了注意因为函数栈父子进程共享所以整个程序的栈就跪了。 如果你在子进程中return那么基本是下面的过程 1子进程的main() 函数 return了于是程序的函数栈发生了变化。 2而main()函数return后通常会调用 exit()或相似的函数如_exit()exitgroup() 3这时父进程收到子进程exit()开始从vfork返回但是尼玛老子的栈都被你子进程给return干废掉了你让我怎么执行注栈会返回一个诡异的栈地址对于某些内核版本的实现直接报“栈错误”就给跪了然而对于某些内核版本的实现于是有可能会再次调用main()于是进入了一个无限循环的结果直到vfork 调用返回 error 好了现在再回到 return 和 exitreturn会释放局部变量并弹栈回到上级函数执行。exit直接退掉。如果你用c 你就知道return会调用局部对象的析构函数exit不会。注exit不是系统调用是glibc对系统调用 _exit()或_exitgroup()的封装 可见子进程调用exit() 没有修改函数栈所以父进程得以顺利执行。 2. 进程内存管理详解 内存分配方式 简介 在C中内存分成5个区他们分别是堆、栈、自由存储区、全局/静态存储区和常量存储区。 C语言不包括自由存储区。   栈在执行函数时函数内局部变量的存储单元都可以在栈上创建函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中效率很高但是分配的内存容量有限。   堆就是那些由 malloc分配的内存块他们的释放编译器不去管由我们的应用程序去控制一般一个malloc就要对应一个 free。如果程序员没有释放掉那么在程序结束后操作系统会自动回收。(是C语言的概念)   自由存储区就是那些由new等分配的内存块他和堆是十分相似的不过它是用delete来结束自己的生命的。是C的抽象概念因为大多数new操作是基于malloc实现的所以这种情况下自由存储区和堆是一样的C对象是分配在堆上的。而如果new操作被重载后不使用Malloc分配内存则所谓自由存储区就不等价于堆区   全局/静态存储区全局变量和静态变量被分配到同一块内存中在以前的C语言中全局变量又分为初始化的和未初始化的在C里面没有这个区分了他们共同占用同一块内存区。   常量存储区这是一块比较特殊的存储区他们里面存放的是常量不允许修改。 明确区分堆与栈 堆与栈的区分问题似乎是一个永恒的话题由此可见初学者对此往往是混淆不清的所以我决定拿他第一个开刀。   首先我们举一个例子 void f() { int* pnew int[5]; }这条短短的一句话就包含了堆与栈看到new我们首先就应该想到我们分配了一块堆内存那么指针p呢他分配的是一块栈内存所以这句话的意思就是在栈内存中存放了一个指向一块堆内存的指针p。在程序会先确定在堆中分配内存的大小然后调用operator new分配内存然后返回这块内存的首地址放入栈中他在VC6下的汇编代码如下 00401028 push 14h 0040102A call operator new (00401060) 0040102F add esp,4 00401032 mov dword ptr [ebp-8],eax 00401035 mov eax,dword ptr [ebp-8] 00401038 mov dword ptr [ebp-4],eax这里我们为了简单并没有释放内存那么该怎么去释放呢 是delete p么澳错了应该是delete []p这是为了告诉编译器我删除的是一个数组编译器就会根据相应的Cookie信息去进行释放内存的工作。 堆和栈究竟有什么区别 好了我们回到我们的主题堆和栈究竟有什么区别   主要的区别由以下几点   (1). 管理方式不同   (2). 空间大小不同   (3). 能否产生碎片不同   (4). 生长方向不同   (5). 分配方式不同   (6). 分配效率不同   管理方式对于栈来讲是由编译器自动管理无需我们手工控制对于堆来说释放工作由程序员控制容易产生memory leak。   空间大小一般来讲在32位系统下堆内存可以达到4G的空间从这个角度来看堆内存几乎是没有什么限制的。但是对于栈来讲一般都是有一定的空间大小的例如在VC6下面默认的栈空间大小是1M好像是记不清楚了。当然我们可以修改   打开工程依次操作菜单如下Project-Setting-Link在Category 中选中Output然后在Reserve中设定堆栈的最大值和commit。   注意reserve最小值为4Bytecommit是保留在虚拟内存的页文件里面它设置的较大会使栈开辟较大的值可能增加内存的开销和启动时间。   碎片问题对于堆来讲频繁的new/delete势必会造成内存空间的不连续从而造成大量的碎片使程序效率降低。对于栈来讲则不会存在这个问题因为栈是先进后出的队列他们是如此的一一对应以至于永远都不可能有一个内存块从栈中间弹出在他弹出之前在他上面的后进的栈内容已经被弹出详细的可以参考数据结构这里我们就不再一一讨论了。   生长方向对于堆来讲生长方向是向上的也就是向着内存地址增加的方向对于栈来讲它的生长方向是向下的是向着内存地址减小的方向增长。   分配方式堆都是动态分配的没有静态分配的堆。栈有2种分配方式静态分配和动态分配。静态分配是编译器完成的比如局部变量的分配。动态分配由alloca函数进行分配但是栈的动态分配和堆是不同的他的动态分配是由编译器进行释放无需我们手工实现。   分配效率栈是机器系统提供的数据结构计算机会在底层对栈提供支持分配专门的寄存器存放栈的地址压栈出栈都有专门的指令执行这就决定了栈的效率比较高。堆则是C/C函数库提供的它的机制是很复杂的例如为了分配一块内存库函数会按照一定的算法具体的算法可以参考数据结构/操作系统在堆内存中搜索可用的足够大小的空间如果没有足够大小的空间可能是由于内存碎片太多就有可能调用系统功能去增加程序数据段的内存空间这样就有机会分到足够大小的内存然后进行返回。显然堆的效率比栈要低得多。   从这里我们可以看到堆和栈相比由于大量new/delete的使用容易造成大量的内存碎片由于没有专门的系统支持效率很低由于可能引发用户态和核心态的切换内存的申请代价变得更加昂贵。所以栈在程序中是应用最广泛的就算是函数的调用也利用栈去完成函数调用过程中的参数返回地址EBP和局部变量都采用栈的方式存放。所以我们推荐大家尽量用栈而不是用堆。   虽然栈有如此众多的好处但是由于和堆相比不是那么灵活有时候分配大量的内存空间还是用堆好一些。   无论是堆还是栈都要防止越界现象的发生除非你是故意使其越界因为越界的结果要么是程序崩溃要么是摧毁程序的堆、栈结构产生以想不到的结果,就算是在你的程序运行过程中没有发生上面的问题你还是要小心说不定什么时候就崩掉那时候debug可是相当困难的 常见的内存错误及其对策 内存分配未成功却使用了它。编程新手常犯这种错误因为他们没有意识到内存分配会不成功。常用解决办法是在使用内存之前检查指针是否为NULL。如果指针p是函数的参数那么在函数的入口处用assert(p!NULL)进行检查。如果是用malloc或new来申请内存应该用if(pNULL) 或if(p!NULL)进行防错处理。内存分配虽然成功但是尚未初始化就引用它。犯这种错误主要有两个起因一是没有初始化的观念二是误以为内存的缺省初值全为零导致引用初值错误例如数组。内存的缺省初值究竟是什么并没有统一的标准尽管有些时候为零值我们宁可信其无不可信其有。所以无论用何种方式创建数组都别忘了赋初值即便是赋零值也不可省略不要嫌麻烦。内存分配成功并且已经初始化但操作越过了内存的边界。例如在使用数组时经常发生下标“多1”或者“少1”的操作。特别是在for循环语句中循环次数很容易搞错导致数组操作越界。忘记了释放内存造成内存泄露。含有这种错误的函数每被调用一次就丢失一块内存。刚开始时系统的内存充足你看不到错误。终有一次程序突然死掉系统出现提示内存耗尽。动态内存的申请与释放必须配对程序中malloc与free的使用次数一定要相同否则肯定有错误new/delete同理。释放了内存却继续使用它。 有三种情况   (1). 程序中的对象调用关系过于复杂实在难以搞清楚某个对象究竟是否已经释放了内存此时应该重新设计数据结构从根本上解决对象管理的混乱局面。   (2). 函数的return语句写错了注意不要返回指向“栈内存”的“指针”或者“引用”因为该内存在函数体结束时被自动销毁。   (3). 使用free或delete释放了内存后没有将指针设置为NULL。导致产生“野指针”。   那么如何避免产生野指针呢这里列出了5条规则平常写程序时多注意一下养成良好的习惯。 规则1用malloc或new申请内存之后应该立即检查指针值是否为NULL。防止使用指针值为NULL的内存。 规则2不要忘记为数组和动态内存赋初值。防止将未被初始化的内存作为右值使用。 规则3避免数组或指针的下标越界特别要当心发生“多1”或者“少1”操作。 规则4动态内存的申请与释放必须配对防止内存泄漏。 规则5用free或delete释放了内存之后立即将指针设置为NULL防止产生“野指针”。 3. 关于堆和自由存储区概念的区别 事实上我在网上看的很多博客划分自由存储区与堆的分界线就是new/delete与malloc/free。然而尽管C标准没有要求但很多编译器的new/delete都是以malloc/free为基础来实现的。那么请问借以malloc实现的new所申请的内存是在堆上还是在自由存储区上 从技术上来说堆heap是C语言和操作系统的术语。堆是操作系统所维护的一块特殊内存它提供了动态分配的功能当运行程序调用malloc()时就会从中分配稍后调用free可把内存交还。而自由存储是C中通过new和delete动态分配和释放对象的抽象概念通过new来申请的内存区域可称为自由存储区。基本上所有的C编译器默认使用堆来实现自由存储也即是缺省的全局运算符new和delete也许会按照malloc和free的方式来被实现这时藉由new运算符分配的对象说它在堆上也对说它在自由存储区上也正确。但程序员也可以通过重载操作符改用其他内存来实现自由存储例如全局变量做的对象池这时自由存储区就区别于堆了。我们所需要记住的就是 堆是操作系统维护的一块内存而自由存储是C中通过new与delete动态分配和释放对象的抽象概念。堆与自由存储区并不等价。 问题的来源 再回过头来来看看这个问题的起源在哪里。最先我们使用C语言的时候并没有这样的争议很明确地知道malloc/free是在堆上进行内存操作。直到我们在Bjarne Stroustrup的书籍中数次看到free store 自由存储区,说实话我一直把自由存储区等价于堆。而在Herb Sutter的《exceptional C》中明确指出了free store自由存储区 与 heap堆 是有区别的。关于自由存储区与堆是否等价的问题讨论大概就是从这里开始的 Free Store The free store is one of the two dynamic memory areas, allocated/freed by new/delete. Object lifetime can be less than the time the storage is allocated; that is, free store objects can have memory allocated without being immediately initialized, and can be destroyed without the memory being immediately deallocated. During the period when the storage is allocated but outside the object’s lifetime, the storage may be accessed and manipulated through a void* but none of the proto-object’s nonstatic members or member functions may be accessed, have their addresses taken, or be otherwise manipulated. Heap The heap is the other dynamic memory area, allocated/freed by malloc/free and their variants. Note that while the default global new and delete might be implemented in terms of malloc and free by a particular compiler, the heap is not the same as free store and memory allocated in one area cannot be safely deallocated in the other. Memory allocated from the heap can be used for objects of class type by placement-new construction and explicit destruction. If so used, the notes about free store object lifetime apply similarly here. 作者也指出之所以把堆与自由存储区要分开来是因为在C标准草案中关于这两种区域是否有联系的问题一直很谨慎地没有给予详细说明而且特定情况下new和delete是按照malloc和free来实现或者说是放过来malloc和free是按照new和delete来实现的也没有定论。这两种内存区域的运作方式不同、访问方式不同所以应该被当成不一样的东西来使用。 结论 自由存储是C中通过new与delete动态分配和释放对象的抽象概念而堆heap是C语言和操作系统的术语是操作系统维护的一块动态分配内存。 new所申请的内存区域在C中称为自由存储区。藉由堆实现的自由存储可以说new所申请的内存区域在堆上。 堆与自由存储区还是有区别的它们并非等价。 假如你来自C语言从没接触过C或者说你一开始就熟悉C的自由储存概念而从没听说过C语言的malloc可能你就不会陷入“自由存储区与堆好像一样好像又不同”这样的迷惑之中。这就像Bjarne Stroustrup所说的 usually because they come from a different language background. 大概只是语言背景不同罢了。 4. cache和buffer的区别 1、Buffer缓冲区是系统两端处理速度平衡从长时间尺度上看时使用的。它的引入是为了减小短期内突发I/O的影响起到流量整形的作用。比如生产者——消费者问题他们产生和消耗资源的速度大体接近加一个buffer可以抵消掉资源刚产生/消耗时的突然变化。 2、Cache缓存则是系统两端处理速度不匹配时的一种折衷策略。因为CPU和memory之间的速度差异越来越大所以人们充分利用数据的局部性locality特征通过使用存储系统分级memory hierarchy的策略来减小这种差异带来的影响。 3、假定以后存储器访问变得跟CPU做计算一样快cache就可以消失但是buffer依然存在。比如从网络上下载东西瞬时速率可能会有较大变化但从长期来看却是稳定的这样就能通过引入一个buffer使得OS接收数据的速率更稳定进一步减少对磁盘的伤害。 4、TLBTranslation Lookaside Buffer翻译后备缓冲器名字起错了其实它是一个cache. 5. C实现线程池 #ifndef THREAD_POOL_H #define THREAD_POOL_H#include vector #include queue #include memory #include thread #include mutex #include condition_variable #include future #include functional #include stdexceptclass ThreadPool { public:ThreadPool(size_t);templateclass F, class... Argsauto enqueue(F f, Args... args) - std::futuretypename std::result_ofF(Args...)::type;~ThreadPool(); private:// need to keep track of threads so we can join themstd::vector std::thread workers;// the task queuestd::queue std::functionvoid() tasks;// synchronizationstd::mutex queue_mutex;std::condition_variable condition;bool stop; };// the constructor just launches some amount of workers inline ThreadPool::ThreadPool(size_t threads): stop(false) {for(size_t i 0;ithreads;i)workers.emplace_back([this]{for(;;){std::functionvoid() task;{std::unique_lockstd::mutex lock(this-queue_mutex);this-condition.wait(lock,[this]{ return this-stop || !this-tasks.empty(); });if(this-stop this-tasks.empty())return;task std::move(this-tasks.front());this-tasks.pop();}task();}}); }// add new work item to the pool templateclass F, class... Args auto ThreadPool::enqueue(F f, Args... args) - std::futuretypename std::result_ofF(Args...)::type {using return_type typename std::result_ofF(Args...)::type;auto task std::make_shared std::packaged_taskreturn_type() (std::bind(std::forwardF(f), std::forwardArgs(args)...));std::futurereturn_type res task-get_future();{std::unique_lockstd::mutex lock(queue_mutex);// dont allow enqueueing after stopping the poolif(stop)throw std::runtime_error(enqueue on stopped ThreadPool);tasks.emplace([task](){ (*task)(); });}condition.notify_one();return res; }// the destructor joins all threads inline ThreadPool::~ThreadPool() {{std::unique_lockstd::mutex lock(queue_mutex);stop true;}condition.notify_all();for(std::thread worker: workers)worker.join(); }使用方法 #include iostream #include ThreadPool.hint main() {// create thread pool with 4 worker threadsThreadPool pool(4);// enqueue and store futureauto result pool.enqueue([](int answer) { return answer; }, 42);// get result from future, print 42std::cout result.get() std::endl; }另一个例子 #include iostream #include ThreadPool.hvoid func() {std::this_thread::sleep_for(std::chrono::milliseconds(100));std::coutworker thread ID:std::this_thread::get_id()std::endl; }int main() {ThreadPool pool(4);while(1){pool.enqueue(fun);} }#ifndef ThreadPool_h #define ThreadPool_h#include vector #include queue #include thread #include mutex #include condition_variable #include future #include functionalclass ThreadPool { public:ThreadPool(size_t); //构造函数size_t n 表示连接数templateclass F, class... Argsauto enqueue(F f, Args... args) //任务管道函数- std::futuretypename std::result_ofF(Args...)::type; //利用尾置限定符 std future用来获取异步任务的结果~ThreadPool(); private:// need to keep track of threads so we can join themstd::vector std::thread workers; //追踪线程// the task queuestd::queue std::functionvoid() tasks; //任务队列用于存放没有处理的任务。提供缓冲机制// synchronization 同步std::mutex queue_mutex; //互斥锁std::condition_variable condition; //条件变量bool stop; };// the constructor just launches some amount of workers inline ThreadPool::ThreadPool(size_t threads): stop(false) {for(size_t i 0;ithreads;i)workers.emplace_back( //以下为构造一个任务即构造一个线程[this]{for(;;){std::functionvoid() task; //线程中的函数对象{//大括号作用临时变量的生存期即控制lock的时间std::unique_lockstd::mutex lock(this-queue_mutex);this-condition.wait(lock,[this]{ return this-stop || !this-tasks.empty(); }); //当stopfalsetasks.empty(),该线程被阻塞 !this-stopthis-tasks.empty()if(this-stop this-tasks.empty())return;task std::move(this-tasks.front());this-tasks.pop();}task(); //调用函数运行函数}}); }// add new work item to the pool templateclass F, class... Args auto ThreadPool::enqueue(F f, Args... args) // 引用限定符参数的右值引用 此处表示参数传入一个函数 - std::futuretypename std::result_ofF(Args...)::type {using return_type typename std::result_ofF(Args...)::type;//packaged_task是对任务的一个抽象我们可以给其传递一个函数来完成其构造。之后将任务投递给任何线程去完成通过 //packaged_task.get_future()方法获取的future来获取任务完成后的产出值auto task std::make_sharedstd::packaged_taskreturn_type() ( //指向F函数的智能指针std::bind(std::forwardF(f), std::forwardArgs(args)...) //传递函数进行构造);//future为期望get_future获取任务完成后的产出值std::futurereturn_type res task-get_future(); //获取future对象如果task的状态不为ready会阻塞当前调用者{std::unique_lockstd::mutex lock(queue_mutex); //保持互斥性避免多个线程同时运行一个任务// dont allow enqueueing after stopping the poolif(stop)throw std::runtime_error(enqueue on stopped ThreadPool);tasks.emplace([task](){ (*task)(); }); //将task投递给线程去完成vector尾部压入}condition.notify_one(); //选择一个wait状态的线程进行唤醒并使他获得对象上的锁来完成任务(即其他线程无法访问对象)return res; }//notify_one不能保证获得锁的线程真正需要锁并且因此可能产生死锁// the destructor joins all threads inline ThreadPool::~ThreadPool() {{std::unique_lockstd::mutex lock(queue_mutex);stop true;}condition.notify_all(); //通知所有wait状态的线程竞争对象的控制权唤醒所有线程执行for(std::thread worker: workers)worker.join(); //因为线程都开始竞争了所以一定会执行完join可等待线程执行完 }#endif /* ThreadPool_h */ 简单版https://www.cnblogs.com/lzpong/p/6397997.html #pragma once #ifndef THREAD_POOL_H #define THREAD_POOL_H#include vector #include queue #include thread #include atomic #include condition_variable #include future #include functional #include stdexceptnamespace std { #define MAX_THREAD_NUM 256//线程池,可以提交变参函数或拉姆达表达式的匿名函数执行,可以获取执行返回值 //不支持类成员函数, 支持类静态成员函数或全局函数,Opteron()函数等 class threadpool {using Task std::functionvoid();// 线程池std::vectorstd::thread pool;// 任务队列std::queueTask tasks;// 同步std::mutex m_lock;// 条件阻塞std::condition_variable cv_task;// 是否关闭提交std::atomicbool stoped;//空闲线程数量std::atomicint idlThrNum;public:inline threadpool(unsigned short size 4) :stoped{ false }{idlThrNum size 1 ? 1 : size;for (size 0; size idlThrNum; size){ //初始化线程数量pool.emplace_back([this]{ // 工作线程函数while(!this-stoped){std::functionvoid() task;{ // 获取一个待执行的 taskstd::unique_lockstd::mutex lock{ this-m_lock };// unique_lock 相比 lock_guard 的好处是可以随时 unlock() 和 lock()this-cv_task.wait(lock,[this] {return this-stoped.load() || !this-tasks.empty();}); // wait 直到有 taskif (this-stoped this-tasks.empty())return;task std::move(this-tasks.front()); // 取一个 taskthis-tasks.pop();}idlThrNum--;task();idlThrNum;}});}}inline ~threadpool(){stoped.store(true);cv_task.notify_all(); // 唤醒所有线程执行for (std::thread thread : pool) {//thread.detach(); // 让线程“自生自灭”if(thread.joinable())thread.join(); // 等待任务结束 前提线程一定会执行完}}public:// 提交一个任务// 调用.get()获取返回值会等待任务执行完,获取返回值// 有两种方法可以实现调用类成员// 一种是使用 bind .commit(std::bind(Dog::sayHello, dog));// 一种是用 mem_fn .commit(std::mem_fn(Dog::sayHello), dog)templateclass F, class... Argsauto commit(F f, Args... args) -std::futuredecltype(f(args...)){if (stoped.load()) // stop true ??throw std::runtime_error(commit on ThreadPool is stopped.);using RetType decltype(f(args...)); // typename std::result_ofF(Args...)::type, 函数 f 的返回值类型auto task std::make_sharedstd::packaged_taskRetType() (std::bind(std::forwardF(f), std::forwardArgs(args)...)); // wtf !std::futureRetType future task-get_future();{ // 添加任务到队列std::lock_guardstd::mutex lock{ m_lock };//对当前块的语句加锁 lock_guard 是 mutex 的 stack 封装类构造的时候 lock()析构的时候 unlock()tasks.emplace([task](){ // push(Task{...})(*task)();});}cv_task.notify_one(); // 唤醒一个线程执行return future;}//空闲线程数量int idlCount() { return idlThrNum; }};}#endif精简版 class thread_pool {std::atomic_bool done;thread_safe_queuestd::functionvoid() work_queue; // 1std::vectorstd::thread threads; // 2join_threads joiner; // 3void worker_thread(){while(!done) // 4{std::functionvoid() task;if(work_queue.try_pop(task)) // 5{task(); // 6}else{std::this_thread::yield(); // 7}}}public:thread_pool():done(false),joiner(threads){unsigned const thread_countstd::thread::hardware_concurrency(); // 8try{for(unsigned i0;ithread_count;i){threads.push_back( std::thread(thread_pool::worker_thread,this)); // 9}}catch(...){donetrue; // 10throw;}}~thread_pool(){donetrue; // 11}templatetypename FunctionTypevoid submit(FunctionType f){work_queue.push(std::functionvoid()(f)); // 12} };#include memory // 为了使用std::shared_ptrtemplatetypename T class threadsafe_queue { public:threadsafe_queue();threadsafe_queue(const threadsafe_queue);threadsafe_queue operator(const threadsafe_queue) delete; // 不允许简单的赋值void push(T new_value);bool try_pop(T value); // 1std::shared_ptrT try_pop(); // 2void wait_and_pop(T value);std::shared_ptrT wait_and_pop();bool empty() const; };6. 静态函数和虚函数的区别 静态函数在编译的时候就已经确定运行时机虚函数在运行的时候动态绑定。虚函数因为用了虚函数表机制调用的时候会增加一次内存开销 7. C里是怎么定义常量的常量存放在内存的哪个位置 常量在C里的定义就是一个top-level const加上对象类型常量定义必须初始化。对于局部对象常量存放在栈区对于全局对象常量存放在全局/静态存储区。对于字面值常量常量存放在常量存储区。 8. 编译链接程序过程 二、g执行的四个过程 1、预处理条件编译头文件包含宏替换的处理生成.i文件。 2、编译将预处理后的文件转换成汇编语言生成.s文件 3、汇编汇编变为目标代码(机器代码)生成.o的文件 4、链接连接目标代码,生成可执行程序 三、最简单的C程序——helloworld! // 新建hello.cpp文件vim编辑 #include iostream using namespace std; int main(void) {count hello world! endl;reutrn 0; }******1****预处理阶段**** g -E hello.cpp hello.i​ 通过vim打开hello.i文件你会发现一些情况最好是自己观察看看哪些内容被换了 ​ 宏的替换还有注释的消除还有找到相关的库文件将#include文件的全部内容插入。若用括起文件则在系统的INCLUDE目录中寻找文件若用括起文件则在当前目录中寻找文件。 ​ 用编辑器打开hello.i会发现有很多很多代码你只需要看最后部分就会发现预处理做了宏的替换还有注释的消除可以理解为无关代码的清除。 ******2****编译**** g -S hello.cpp​ 生成hello.s文件.s文件表示是汇编文件用编辑器打开就都是汇编指令。可以通过vim编辑器看看hello.s里面的内容【都是汇编指令哈哈】。 ******3****汇编**** g -c hello.cpp​ 汇编变为目标代码(机器代码)生成.o的文件,.o是gcc生成的目标文件用编辑器打开就都是二进制机器码。 ******4****链接** **——****链接生成可执行文件库文件进行链接**** g -o hello hello.cpp程序运./hello【输出hello world!】 ​ 在成功编译之后就进入了链接阶段。在这里涉及到一个重要的概念函数库(可以这么理解就是不带main函数的.cpp生成的)。 ​ 可以重新查看这个小程序在这个程序中并没有定义”cout”的函数(准确说cout不是函数cout却很独特既不是函数似乎也不是C特别规定出来的像iffor一类有特殊语法的“语句”其实说到底还是函数调用不过这函数有些特殊用的是运算符重载确切地说是重载了“”运算符。这里如果用pritf()函数说明会更好暂且当做函数理解吧实现且在预编译中包含进的”iostream”中也只有该函数的声明而没有定义函数的实现那么是在哪里实现”cout”函数的呢系统把这些函数实现都被做到名为stdc的库文件中去了在没有特别指定时g会到系统默认的搜索路径”/usr/lib”下进行查找也就是链接到stdc库函数中去这样就能实现函数”cout”了而这也就是链接的作用。 9. C和C中的const变量存储在那 c语言中const全局变量存储在只读数据段编译期最初将其保存在符号表中第一次使用时为其分配内存在程序结束时释放。 而const局部变量局部变量就是在函数中定义的一个const变量存储在栈中代码块结束时释放。 在c语言中可以通过指针对const局部变量进行修改而不可以对const全局变量进行修改。因为const全局变量是存储在只读数据段 而c中一个const不是必需创建内存空间而在c中一个const总是需要一块内存空间。 **在c中是否要为const全局变量分配内存空间取决于这个const变量的用途如果是充当着一个值替换即就是将一个变量名替换为一个值那么就不分配内存空间不过当对这个const全局变量取地址或者使用extern时会分配内存**存储在只读数据段。也是不能修改的。 c中对于局部的const变量要区别对待 对于基础数据类型也就是const int a 10这种编译器会把它放到符号表中不分配内存当对其取地址时会分配内存 对于基础数据类型如果用一个变量初始化const变量如果const int a b,那么也是会给a分配内存 对于自定数据类型比如类对象那么也会分配内存。 c中const默认为外部连接c中const默认为内部连接.当c语言两个文件中都有const int a的时候编译器会报重定义的错误。而在c中则不会因为c中的const默认是内部连接的。如果想让c中的const具有外部连接必须显示声明为: extern const int a 10。 那我们先看以下几个常见的问题加深C编译器的理解 « 符号表如何存储静态变量包括全局变量和局部变量和类非静态成员变量有哪些区别 对于静态变量C编译器的处理方法和Java解释器处理的方法有类似的地方。那就是访问控制符public/private/protected和static一样都会写入符号表对于非静态成员变量符号表存储的什么内容有没有这些访问控制符类的非静态成员在类中的表示是通过偏移量来访问只有和对象邦定以后才能找到其真实的地址那么在编译后的目标文件中没有为成员分配地址访问控制符也就没有写入到目标文件。【注1:为什么访问控制符是在编译期间处理而不放在运行期间处理如果要放在运行期间处理必须完成新的连接器和加载器来保证另外重要的是运行效率的问题】【注2:参考CSDN上一篇文章一个类的成员变量修改了访问控制符在另外一个文件被引用是否必须编译修改的文件才能连接成功《今天面试碰到的一个以前没有想过的问题顺便给一点分出去》】。 « 符号表如何处理const、volatile变量 C编译器把Const对象放在了符号表之中C语言一般是放在只读数据区。【为什么C编译器这么做我想一个原因就是减少一些存储操作次数】。对于volatile变量我们应该怎么考虑声明的voliate告诉编译器不能优化它呀我们只能在符号表中增加标识位来告诉编译器自己不优化volatile变量。 « 符号表怎么处理虚函数、虚继承、多继承的情况 其实对于这些处理在Lippman的《Inside the C Object Model》中有详细的介绍。也许你知道那些应该放在符号表中那些是不能的。例如对于局部静态对象static class obj;的定义那些不需要放在符号表中【注局部静态变量只要求在函数执行时初始化一次】为了保证Obj满足要求需要附加标志变量这个附加变量能放在符号表中吗不能原因很简单它需要在运行时控制对象的构造和析构。 10. Cpriority_queue 方法一priority_queue 这种方法需要#includequeue 最基本的使用方法对于一串数字建堆 riority_queueint heap;这种情况下默认为最大堆也就是堆顶元素值最大。 如果需要建立最小堆可以采用如下方式 priority_queueint, vectorint, greaterint qi2;//最小堆 priority_queueint, vectorint, lessint qi2;//最大堆然而在多数情况下我们还需要记录一些排序元素的额外信息比如索引之类的则需要以下三个步骤 定义堆中需要存储的结构体 struct Node{ int x; int y; int val; Node(int a,int b,int valin):x(a),y(b),val(valin){} };确定堆中元素的存储顺序也就是最大堆还是最小堆 //设置比较函数确定堆中元素的顺序是最大堆还是最小堆 struct cmp{bool operator()(const Node a,const Node b){return a.valb.val;//最小堆//return a.valb.val;//最大堆} };建堆 priority_queueNode,vectorNode,cmp heap;//建堆 heap.pop();//出堆 heap.push();//入堆 heap.top();//获取堆顶元素方法二利用vector 这种法法需要#includealgorithm #include functional vectorint a; //建堆 make_heap(a.begin(),a.end(), lessint() );//最大堆 make_heap(a.begin(),a.end(), greaterint() );//最小堆 //pop pop_heap(a.begin(),a.end(), lessint() );//最大值出堆 pop_heap(a.begin(),a.end(), lessint() );//最小值出堆 //插入元素 push_heap(a.begin(),a.end(),cmp); //堆排序 sort_heap(a.begin(),a.end(),cmp); // push_heap ( begin , end ) 将最后一个元素插入堆中堆自动调整 // pop_heap ( begin , end ) 将第一个元素从堆中删去堆自动调整并放到最后 // find ( begin , end , value ) 从begin到end查找value若找不到返回end11. 友元 在C中我们使用类对数据进行了隐藏和封装类的数据成员一般都定义为私有成员成员函数一般都定义为公有的以此提供类与外界的通讯接口。但是有时需要定义一些函数这些函数不是类的一部分但又需要频繁地访问类的数据成员这时可以将这些函数定义为该函数的友元函数。除了友元函数外还有友元类两者统称为友元。友元的作用是提高了程序的运行效率即减少了类型检查和安全性检查等都需要时间开销但它破坏了类的封装性和隐藏性使得非成员函数可以访问类的私有成员。 友元函数 友元函数是可以直接访问类的私有成员的非成员函数。它是定义在类外的普通函数它不属于任何类但需要在类的定义中加以声明声明时只需在友元的名称前加上关键字friend其格式如下 friend 类型 函数名(形式参数); 友元函数的声明可以放在类的私有部分也可以放在公有部分它们是没有区别的都说明是该类的一个友元函数。 一个函数可以是多个类的友元函数只需要在各个类中分别声明。 友元函数的调用与一般函数的调用方式和原理一致。 友元类 友元类的所有成员函数都是另一个类的友元函数都可以访问另一个类中的隐藏信息包括私有成员和保护成员。 当希望一个类可以存取另一个类的私有成员时可以将该类声明为另一类的友元类。定义友元类的语句格式如下 friend class 类名; 其中friend和class是关键字类名必须是程序中的一个已定义过的类。 例如以下语句说明类B是类A的友元类 class A { … public: friend class B; … }; 经过以上说明后类B的所有成员函数都是类A的友元函数能存取类A的私有成员和保护成员。 ​ 使用友元类时注意 ​ (1) 友元关系不能被继承。 ​ (2) 友元关系是单向的不具有交换性。若类B是类A的友元类A不一定是类B的友元要看在类中是否有相应的声明。 ​ (3) 友元关系不具有传递性。若类B是类A的友元类C是B的友元类C不一定是类A的友元同样要看类中是否有相应的申明 《windows环境多线程编程原理与应用》中解释   如果将类的封装比喻成一堵墙的话那么友元机制就像墙上了开了一个门那些得   到允许的类或函数允许通过这个门访问一般的类或者函数无法访问的私有属性和方      法。友元机制使类的封装性得到消弱所以使用时一定要慎重。 友元类 将外界的某个类在本类别的定义中说明为友元那么外界的类就成为本类的“朋      友”那个类就可以访问本类的私有数据了。 class Merchant{private :int m_MyMoney;int m_MyRoom;… …Public:Friend class Lawyer;Int getmoney();… …};Class Lawyer{Private:… …Public:… …};只有你赋予某个类为你的友元时那个类才有访问你的私有数据的权利。说明一个函数为一个类的友元函数则该函数可以访问此类的私有数据和方法。定义方法是在类的定义中在函数名前加上关键字friend. 需要友元与友元的优缺点: ​ 通常对于普通函数来说,要访问类的保护成员是不可能的如果想这么做那么必须把类的成员都生命成为public(共用的)然而这做带来的问题遍是任何外部函数都可以毫无约束的访问它操作它c利用friend修饰符可以让一些你设定的函数能够对这些保护数据进行操作避免把类成员全部设置成public最大限度的保护数据成员的安全。 ​ 友元能够使得普通函数直接访问类的保护数据避免了类成员函数的频繁调用可以节约处理器开销提高程序的效率但所矛盾的是即使是最大限度大保护同样也破坏了类的封装特性这即是友元的缺点在现在cpu速度越来越快的今天我们并不推荐使用它但它作为c一个必要的知识点一个完整的组成部分我们还是需要讨论一下的。 在类里声明一个普通数学在前面加上friend修饰那么这个函数就成了该类的友元可以访问该类的一切成员。 ​ 下面我们来看一段代码看看我们是如何利用友元来访问类的一切成员的 #include iostream using namespace std; class Internet { public: Internet(char *name,char *address) // 改为internet(const char *name , const char *address) { strcpy(Internet::name,name); strcpy(Internet::address,address); } friend void ShowN(Internet obj); //友元函数的声明 public:              // 改为private char name[20]; char address[20]; }; void ShowN(Internet obj) //类外普通函数定义访问a对象的保护成员name,不能写成,void Internet::ShowN(Internet obj) { coutobj.nameendl; //可访问internet类中的成员 } void main() { Internet a(谷歌,http://www.google.cn/;); ShowN(a); cin.get(); } 示例2 分别定义一个类A和类B 各有一个私有整数成员变量通过构造函数初始化类A有一个成员函数Show(B b)用来打印A和B的私有成员变量请分别通过友元函数和友元类来实现此功能。使用友元类 和 友元函数实现 #include iostreamusing namespace std; class B; class A; void Show( A , B );//友元类实现 class B { private: int tt; friend class A; friend void Show( A , B );public: B( int temp 100):tt ( temp ){} };class A { private: int value; friend void Show( A , B );public: A(int temp 200 ):value ( temp ){}void Show( B b ) {cout value endl;cout b.tt endl; } };//友元函数实现 void Show( A a, B b ) { cout a.value endl; cout b .tt endl; }int main() { A a; B b; a.Show( b ); Show( a, b );return 0; }12. gcc gcc命令 使用GNU推出的基于C/C的编译器是开放源代码领域应用最广泛的编译器具有功能强大编译代码支持性能优化等特点。现在很多程序员都应用GCC怎样才能更好的应用GCC。目前GCC可以用来编译C/C、FORTRAN、JAVA、OBJC、ADA等语言的程序可根据需要选择安装支持的语言。 语法 gcc(选项)(参数)选项 -o指定生成的输出文件名 -E仅执行编译预处理 -S将C代码转换为汇编代码 -Wall显示所有警告信息 wornging all -c仅执行预处理、编译和汇编操作不进行连接操作。 -I: -g: 支持gdb调试 输出文件会变大 -On(1~3级)三级编译优化n越大优化越多 -D: 编译时定义宏例如在编译时添加宏可以启动和关闭条件编译,主要起开关作用参数 C源文件指定C语言源代码文件。 实例 常用编译命令选项 假设源程序文件名为test.c 无选项编译链接 gcc test.c将test.c预处理、汇编、编译并链接形成可执行文件。这里未指定输出文件默认输出为a.out。 选项 -o gcc test.c -o test将test.c预处理、汇编、编译并链接形成可执行文件test。-o选项用来指定输出文件的文件名。 选项 -E gcc -E test.c -o test.i将test.c预处理输出test.i文件。 选项 -S gcc -S test.i //注意是大写将预处理输出文件test.i汇编成test.s文件。 选项 -c 做预处理、编译和汇编操作。得到二进制文件 gcc -c test.s将汇编输出文件test.s编译输出test.o文件。 无选项链接 gcc test.o -o test将编译输出文件test.o链接成最终可执行文件test。 选项 -O gcc -O1 test.c -o test使用编译优化级别1编译程序。级别为1~3级别越大优化效果越好但编译时间越长。 头文件链接选项 -I 编译文件时只需要编译源文件就行但是编译源文件的时候会去找头文件。如果头文件和源文件不在同一个目录下时编译时无法找到头文件则可以使用-I命令执行头文件的位置 gcc -I./nextdir hello.c -o hello //hello.c的头文件在./nextdir下-I命令位置可以在任意地方。而且-I后可以有空格可以没有编译时定义宏-D #includehello.h #ifdef DEBUG #define HI 20 #endif int main() {printf(\n,HI);}gcc hello.c -o hello -D DEBUG多源文件的编译方法 如果有多个源文件基本上有两种编译方法 假设有两个源文件为test.c和testfun.c 多个文件一起编译 gcc testfun.c test.c -o test将testfun.c和test.c分别编译后链接成test可执行文件。 分别编译各个源文件之后对编译后输出的目标文件链接。 gcc -c testfun.c #将testfun.c编译成testfun.o gcc -c test.c #将test.c编译成test.o gcc testfun.o test.o -o test #将testfun.o和test.o链接成test以上两种方法相比较第一中方法编译时需要所有文件重新编译而第二种方法可以只重新编译修改的文件未修改的文件不用重新编译。 自测实例 三个文件 test.h test.cpp main.cpp 分别编译并链接 g -c test.cpp //自动生成test.o的目标文件 g -c main.cpp //生成main.o的目标文件 g test.o main.o -o main //链接所有的目标文件13. find命令 find [option] path ... [expression]-type 按文件类型搜索-name 按文件名搜索-maxdepth 按指定深度搜索默认是搜索所有子目录-size 按文件大小查找兆是M,k是小写k-atime、ctime、mtime(最近访问、最近改动文件内容一天计算、最近更改属性mode、硬链接等)-exec :将find搜索的结果集执行某一指定命令。-ok :以交互式的方式将find搜索的结果集执行某一指定命令-xargs :分片处理结果集 例如find ./ -maxpath 1 -name *.jpg //只搜索本层级的所有文件find ./ -maxpath 2 -name *.jpg //只搜索2层文件find ./ -type f //搜索文件普通文件类型find ./ -type l //找出所有的软连接find ./ -size 20M -size -50M //搜索大于20M小于50M的文件find ./ -ctime 1 //查最近一天修改内容的文件find ./ -name *tmp* -exec ls -l {} \; //将查找的内容装入{}中执行ls 14. linux系统目录和文件类型 其中 usr: unix software resource bin: 存二进制可执行文件shell命令 目录表示 标识符 ~ :家目录但不是home目录而是用户目录home下包含所有用户 . :当前目录 .. :上一级目录 - 上次使用的目录 cd ~ cd . cd .. cd -15. ls ls: -a 隐藏文件-l 列出文件详细信息-R 递归查同级下的所有目录和文件的内容-dl 列出目录的详细信息后面可以跟目录名称默认是当前目录的信息-R递归查询所有文件 16. cp 拷贝文件 cp file1 file2 #会将file2中的内容覆盖file1和file2内容一样 cp file2 dir/ #拷贝到目录下 cp file1 ../ #拷贝到上一级拷贝目录 cp dir1 dir2 -r #递归拷贝非空目录 cp dir1 ~/ -r cp -a dir1 .. #拷贝目录的所有内容到上级而且目录的时间戳不变而-r则会更新17. cat cat file # 查看文件内容 cat # 从标准输入读入遇到换行则回显 终端下输入ctrl -d结束 tac file #倒着查看文件内容 18. more 查看文件内容空格翻页回车下一行q结束 19. grep 以文件内容为对象查找 grep [选项] ‘查找的内容’ path [选项] -c 只输出匹配行的计数-I 不区分大小写只适用于单字符-h 查询多文件时不显示文件名-l 查询多文件时只输出包含匹配字符的文件名-n 显示匹配行的行号-s -v-R 递归查子目录下的所有文件可以查询某个进程的信息ps aux | grep bash #查询bash的进程20. echo echo [选项] [字符串] 1.输入一行文本并显示在标准输出上 $ echo Tecmint is a community of Linux Nerds 会输出下面的文本: Tecmint is a community of Linux Nerds 2.输出一个声明的变量值 比如声明变量x并给它赋值为10。 $ x10会输出它的值 $ echo The value of variable x $x The value of variable x 10 3.使用echo命令打印所有的文件和文件夹ls命令的替代 $ echo * 4.打印制定的文件类型 比如让我们假设你想要打印所有的‘.jpeg‘文件使用下面的命令。 $ echo *.jpeg network.jpeg 5.echo可以使用重定向符来输出到一个文件而不是标准输出 $ echo Test Page testpage ## Check Content avitecmint:~$ cat testpage Test Page 20. 补充命令 20.1 ps 最详解析https://developer.aliyun.com/article/710681 20.2 $: ./a.out 在后台运行进程20.3 man man命令是Linux下的帮助指令通过man指令可以查看Linux中的指令帮助、配置文件帮助和编程帮助等信息。 语法 man(选项)(参数)    选项可有可无但参数必须有。选项 -a在所有的man帮助手册中搜索 -f等价于whatis指令显示给定关键字的简短描述信息 -P指定内容时使用分页程序 -M指定man手册搜索的路径。参数 数字指定从哪本man手册中搜索帮助关键字指定要搜索帮助的关键字。 也可以这样输入命令“man [章节号] 手册名称”。 man是按照手册的章节号的顺序进行搜索的比如 man sleep只会显示sleep命令的手册如果想查看库函数sleep就要输入如下所示 man 3 sleepLinux的man手册共有以下几个章节 1、Standard commands 标准命令 2、System calls 系统调用 3、Library functions 库函数 4、Special devices 设备说明 5、File formats 文件格式 6、Games and toys 游戏和娱乐 7、Miscellaneous 杂项 8、Administrative Commands 管理员命令 21. vim 21.1 vim配置文件 添加~/.vimrc配置文件.vimrc文件可编写内容如下 set nocompatible required filetype off required set the runtime path to include Vundle and initialize set rtp~/.vim/bundle/Vundle.vim call vundle#begin() alternatively, pass a path where Vundle should install plugins call vundle#begin(~/some/path/here) let Vundle manage Vundle, required Plugin gmarik/Vundle.vim Add all your plugins here (note older versions of Vundle used Bundle instead of Plugin) All of your Plugins must be added before the following line call vundle#end() required filetype plugin indent on required显示行号 set nu启动时隐去援助提示 set shortmessatI语法高亮 syntax on使用vim的键盘模式 set nocompatible不需要备份 set nobackup没有保存或文件只读时弹出确认 set confirm鼠标可用 set mouseatab缩进 set tabstop4 set shiftwidth4 set expandtab set smarttab文件自动检测外部更改 set autoreadc文件自动缩进 set cindent自动对齐 set autoindent智能缩进 set smartindent高亮查找匹配 set hlsearch背景色 set backgrounddark显示匹配 set showmatch显示标尺就是在右下角显示光标位置 set ruler去除vi的一致性 set nocompatible允许折叠 set foldenable 设置折叠根据语法折叠 set fdmsyntax手动折叠 set fdmmanual设置键盘映射通过空格设置折叠 nnoremap space ((foldclosed(line(.)0)?zc:zo))CR不要闪烁 set novisualbell启动显示状态行 set laststatus2浅色显示当前行 autocmd InsertLeave * se nocul用浅色高亮当前行 autocmd InsertEnter * se cul显示输入的命令 set showcmd被分割窗口之间显示空白 set fillcharsvert:/set fillcharsstl:/set fillcharsstlnc:/ 21.2 vim命令及模式切换 三种模式切换 1由命令模式切换到文本模式 ​ vi从命令模式切换到文本输入模式。每个键以不同方式使vi进入文本输入模式。按****[ESC]****键使vi从文本输入模式回到命令模式。表1列出了vi从命令模式切换到文本输入模式的命令键及其功能。 ​ 表1 切换到文本输入模式的命令键 键功能i在光标左侧输入正文I在光标所在行的行首输入正文a在光标右侧输入正文A在光标所在行的行尾输入正文o在光标所在行的下一行增添新行光标位于新行的行首O在光标所在行的上一行增添新行光标位于新行的行首s光标所在处的一个字符被删除进入文本模式S删除光标所在处的整行进入文本模式 2命令模式光标移动 h j k l 左 下 上 右3自动调整格式 ggG (命令模式)4大括号对应跳转切换 % 命令模式 前提光标在括号位置5删除 1. 从光标位置开始到行尾D (命令模式) 2. 从光标开始到行首d0 3. 多行删除ndd(n1,2,3,4.....) 4. 单行删除dd 5. 删除单个字符x (命令模式) 工作模式不切换 6. 删除单个单词 光标置于单词首字母位置 dw (命令模式) 7. 全选删除 命令模式下 gg dG6光标移动 1. 移至行首0 2. 移至行尾$ 3. 跳转到文首,命令模式: gg 4. 跳转到文尾,命令模式: G 5. 跳转到指定位置,命令模式: n G (n为行号) ; 末行模式n 7撤销上一步操作 u8恢复上一步被撤销的操作 ctrlr9复制 单行复制 在命令模式下将光标移动到将要复制的行处按“yy”进行复制 多行复制 在命令模式下将光标移动到将要复制的首行处按“nyy”复制n行其中n为1、2、3…… 全选复制 在命令模式下ggyG 自定义选中要复制的内容命令模式vhjkl自由选中也也就是说v是开启选中模式 跨文件复制内容从a文件复制到b文件打开b文件然后末行模式r!cat b回车Vim跨文件复制 现在把a.txt的三行复制到b.txt 1、用vim打开a.txt # vim a.txt Esc进入指令模式默认刚打开就是这个模式 输入a3yy 解释引号要结合shift输入a代表剪贴板a26个英文字母都可以3yy当然代表从当前行复制3行了 退出a.txt :q 2、打开b.txt 光标移动到你想要复制的位置 进入指令模式输入ap 解释引号要结合shift输入a代表使用剪贴板ap当然代表粘贴了 10粘贴 在命令模式下将光标移动到将要粘贴的行处按“p”进行粘贴11折叠 在可折叠处大括号中间 zc 折叠所有的大括号 zn 打开所有的折叠 zC 对所在范围内所有嵌套的折叠点进行折叠 zo 展开折叠 zO 对所在范围内所有嵌套的折叠点展开 [z 到当前打开的折叠的开始处。 ]z 到当前打开的折叠的末尾处。 zj 向下移动。到达下一个折叠的开始处。关闭的折叠也被计入。 zk 向上移动到前一折叠的结束处。关闭的折叠也被计入。12分屏 : new 末行模式新建一个文件 splite filename 末行模式水平打开一个已存在文件 vsplite filename 末行模式垂直打开一个已存在文件 ctrlwj 命令模式切换到下面的文件输入 ctrlwk 命令模式切换到上面的文件输入 ctrlwv 命令模式垂直打开一个匿名文件复制当前文件内容 ctrlws 命令模式水平打开一个匿名文件复制当前文件内容 ctrlwo 命令模式只保留一个文件关闭其他文件 ctrls 锁定键入 ctrlq 解锁13剪切 命令模式 v //进入visual模式按字符选中使用hjkl控制选择区域 命令模式 V //进入visual模式按行选中使用jk控制选择区域 命令模式 ctrlv //进入visual模式按块选中[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sO4rpS8d-1679822744343)(linux编程笔记和C编程笔记.assets/image-20201110105402359.png)] 命令模式x或d //选中代码后执行删除则会放入剪切板之后使用p粘贴14块移动 选中一段代码后进行左移右移 shift 右移 : shift 左移 末行模式按行号选择块移动 10100 //第10行到第100行右缩进15处理大小写 1. 单个字符的处理 ~切换光标所在位置的字符的大小写形式大写转换为小写小写转换为大写3~将光标位置开始的3个字母改变其大小写 2. 文本整体的处理 gu切换为小写gU切换为大写剩下的就是对这两个命令的限定限定行字母和单词等等。 2.1 整篇文章 无须进入命令行模式键入 ggguG整篇文章转换为小写gg文件头G文件尾gu切换为小写gggUG整篇文章切换为大写gg文件头G文件尾gU切换为大写 2.2 只转化某个单词 guw、guegUw、gUegu5w转换 5 个单词gU5w 2.3 转换行 gU0 从光标所在位置到行首都变为大写gU$ 从光标所在位置到行尾都变为大写gUG 从光标所在位置到文章最后一个字符都变为大写gU1G 从光标所在位置到文章第一个字符都变为大写 22. gdb调试 gdb交互命令 启动gdb后进入到交互模式通过以下命令完成对程序的调试注意高频使用的命令一般都会有缩写熟练使用这些缩写命令能提高调试的效率 运行 run简记为 r 其作用是运行程序当遇到断点后程序会在断点处停止运行等待用户输入下一步的命令。continue 简写c 继续执行到下一个断点处或运行结束next简写 n单步跟踪程序当遇到函数调用时也不进入此函数体此命令同 step 的主要区别是step 遇到用户自定义的函数将步进到函数中去运行而 next 则直接调用函数不会进入到函数体内。step 简写s单步调试如果有函数调用则进入函数与命令n不同n是不进入调用的函数的until当你厌倦了在一个循环体内单步跟踪时这个命令可以运行程序直到退出循环体。until行号 运行至某行不仅仅用来跳出循环finish 运行程序直到当前函数完成返回并打印函数返回时的堆栈地址和返回值及参数值等信息。call 函数(参数)调用程序中可见的函数并传递“参数”如call gdb_test(55)quit简记为 q 退出gdb 设置断点 break n 简写b n:在第n行处设置断点 可以带上代码路径和代码名称 b OAGUPDATE.cpp:578 b fn1 if ab条件断点设置 break funcbreak缩写为b在函数func()的入口处设置断点如break cb_button delete 断点号n删除第n个断点 disable 断点号n暂停第n个断点 enable 断点号n开启第n个断点 clear 行号n清除第n行的断点 info b info breakpoints 显示当前程序的断点设置情况 delete breakpoints清除所有断点 查看源代码 list 简记为 l 其作用就是列出程序的源代码默认每次显示10行。list 行号将显示当前文件以“行号”为中心的前后10行代码如list 12list 函数名将显示“函数名”所在函数的源代码如list mainlist 不带参数将接着上一次 list 命令的输出下边的内容。 打印表达式 print 表达式简记为 p 其中“表达式”可以是任何当前正在被测试程序的有效表达式比如当前正在调试C语言的程序那么“表达式”可以是任何C语言的有效表达式包括数字变量甚至是函数调用。print a将显示整数 a 的值print a将把 a 中的值加1,并显示出来print name将显示字符串 name 的值print gdb_test(22)将以整数22作为参数调用 gdb_test() 函数print gdb_test(a)将以变量 a 作为参数调用 gdb_test() 函数display 表达式在单步运行时将非常有用使用display命令设置一个表达式后它将在每次单步进行指令后紧接着输出被设置的表达式及值。如 display awatch 表达式设置一个监视点一旦被监视的“表达式”的值改变gdb将强行终止正在被调试的程序。如 watch awhatis 查询变量或函数info function 查询函数扩展info locals 显示当前堆栈页的所有变量 查询运行信息 where/bt 当前运行的堆栈列表bt backtrace 显示当前调用堆栈up/down 改变堆栈显示的深度set args 参数:指定运行时的参数show args查看设置好的参数info program 来查看程序的是否在运行进程号被暂停的原因。 分割窗口 layout用于分割窗口可以一边查看代码一边测试layout src显示源代码窗口layout asm显示反汇编窗口layout regs显示源代码/反汇编和CPU寄存器窗口layout split显示源代码和反汇编窗口Ctrl L刷新窗口 23. Linux进程状态 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KhZBvo0p-1679822744345)(linux编程笔记和C编程笔记.assets/image-20200910154419178.png)] 1进程状态 Duninterruptible sleep (usually IO)Rrunning or runnable (on run queue) 可执行状态或运行态Sinterruptible sleep (waiting for an event to complete) 可中断的睡眠状态Tstopped by job control signaltstopped by debugger during the tracingWpaging (not valid since the 2.6.xx kernel)Xdead (should never be seen)Zdefunct (“zombie”) process, terminated but not - reaped by its parent R (TASK_RUNNING)可执行状态 ​ 只有在该状态的进程才可能在CPU上运行。而同一时刻可能有多个进程处于可执行状态这些进程的task_struct结构进程控制块被放入对应CPU的可执行队列中一个进程最多只能出现在一个CPU的可执行队列中。进而进程调度器就从各个CPU的可执行队列中分别选择一个进程在该CPU上运行。 S (TASK_INTERRUPTIBLE)可中断的睡眠状态 处于这个状态的进程因为等待某某事件的发生比如等待socket连接、等待信号量而被挂起。这些进程的task_struct结构进程控制块被放入对应事件的等待队列中。当这些事件发生时由外部中断触发、或由其他进程触发对应的等待队列中的一个或多个进程将被唤醒。 通过ps命令会看到一般情况下进程列表中的绝大多数进程都处于TASK_INTERRUPTIBLE状态除非机器的负载很高。毕竟CPU就这么几个进程动辄几十上百个如果不是绝大多数进程都在睡眠CPU又怎么响应得过来。 D (TASK_UNINTERRUPTIBLE)不可中断的睡眠状态 与TASK_INTERRUPTIBLE状态类似进程处于睡眠状态但是此刻进程是不可中断的。不可中断指的并不是CPU不响应外部硬件的中断而是指进程不响应异步信号。 绝大多数情况下进程处在睡眠状态时总是应该能够响应异步信号的。否则你将惊奇的发现kill -9竟然杀不死一个正在睡眠的进程了于是我们也很好理解为什么ps命令看到的进程几乎不会出现TASK_UNINTERRUPTIBLE状态而总是TASK_INTERRUPTIBLE状态。 而TASK_UNINTERRUPTIBLE状态存在的意义就在于内核的某些处理流程是不能被打断的。如果响应异步信号程序的执行流程中就会被插入一段用于处理异步信号的流程这个插入的流程可能只存在于内核态也可能延伸到用户态于是原有的流程就被中断了。 例如在进程对某些硬件进行操作时比如进程调用read系统调用对某个设备文件进行读操作而read系统调用最终执行到对应设备驱动的代码并与对应的物理设备进行交互可能需要使用TASK_UNINTERRUPTIBLE状态对进程进行保护以避免进程与设备交互的过程被打断造成设备陷入不可控的状态。这种情况下的TASK_UNINTERRUPTIBLE状态总是非常短暂的通过ps命令基本上不可能捕捉到。 T/t (TASK_STOPPED or TASK_TRACED)暂停状态或跟踪状态 T (TASK_STOPPED)状态向进程发送一个SIGSTOP信号它就会因响应该信号而进入TASK_STOPPED状态除非该进程本身处于TASK_UNINTERRUPTIBLE状态而不响应信号。 SIGSTOP与SIGKILL信号一样是非常强制的。不允许用户进程通过signal系列的系统调用重新设置对应的信号处理函数。 向进程发送一个SIGCONT信号kill -18可以让其从TASK_STOPPED状态恢复到TASK_RUNNING状态或者kill -9直接尝试杀死。 t (TASK_STOPPED)状态当进程正在被跟踪时它处于TASK_TRACED这个特殊的状态。“正在被跟踪”指的是进程暂停下来等待跟踪它的进程对它进行操作。比如在gdbUNIX及UNIX-like下的调试工具调试中对被跟踪的进程下一个断点进程在断点处停下来的时候就处于TASK_TRACED状态。而在其他时候被跟踪的进程还是处于前面提到的那些状态。 Z (TASK_DEAD - EXIT_ZOMBIE)退出状态进程成为僵尸进程 进程在退出的过程中处于TASK_DEAD状态。在这个退出过程中进程占有的所有资源将被回收除了task_struct结构以及少数资源以外。于是进程就只剩下task_struct这么个空壳故称为僵尸。 之所以保留task_struct是因为task_struct里面保存了进程的退出码、以及一些统计信息。而其父进程很可能会关心这些信息。父进程可以通过wait系列的系统调用如wait4、waitid来等待某个或某些子进程的退出并获取它的退出信息保存在task_struct里。然后wait系列的系统调用会顺便将子进程的尸体task_struct也释放掉。 X (TASK_DEAD - EXIT_DEAD)退出状态进程即将被销毁 进程在退出过程中也可能不会保留它的task_struct。比如这个进程是多线程程序中被detach过的进程。或者父进程通过设置SIGCHLD信号的handler为SIG_IGN显式的忽略了SIGCHLD信号。这是posix的规定尽管子进程的退出信号可以被设置为SIGCHLD以外的其他信号。 此时进程将被置于EXIT_DEAD退出状态这意味着接下来的代码立即就会将该进程彻底释放。所以EXIT_DEAD状态是非常短暂的几乎不可能通过ps命令捕捉到。 24. 环境变量 Linux是一个多用户多任务的操作系统可以在Linux中为不同的用户设置不同的运行环境具体做法是设置不同用户的环境变量。 Linux环境变量分类 一、按照生命周期来分Linux环境变量可以分为两类 1、永久的需要用户修改相关的配置文件变量永久生效。 2、临时的用户利用export命令在当前终端下声明环境变量关闭Shell终端失效。 二、按照作用域来分Linux环境变量可以分为 1、系统环境变量系统环境变量对该系统中所有用户都有效。 2、用户环境变量顾名思义这种类型的环境变量只对特定的用户有效。 Linux设置环境变量的方法 一、在/etc/profile文件中添加变量 对所有用户生效永久的 用vim在文件/etc/profile文件中增加变量该变量将会对Linux下所有用户有效并且是“永久的”。 例如编辑/etc/profile文件添加CLASSPATH变量 vim /etc/profile export CLASSPATH./JAVA_HOME/lib;$JAVA_HOME/jre/lib注修改文件后要想马上生效还要运行source /etc/profile不然只能在下次重进此用户时生效。 二、在用户目录下的.bash_profile文件中增加变量 【对单一用户生效永久的】 用vim ~/.bash_profile文件中增加变量改变量仅会对当前用户有效并且是“永久的”。 vim ~/.bash.profile export CLASSPATH./JAVA_HOME/lib;$JAVA_HOME/jre/lib注修改文件后要想马上生效还要运行$ source ~/.bash_profile不然只能在下次重进此用户时生效。 三、直接运行export命令定义变量 【只对当前shellBASH有效临时的】 在shell的命令行下直接使用export 变量名变量值 定义变量该变量只在当前的shellBASH或其子shellBASH下是有效的shell关闭了变量也就失效了再打开新shell时就没有这个变量需要使用的话还需要重新定义。 Linux环境变量使用 一、Linux中常见的环境变量有 PATH指定命令的搜索路径 PATH声明用法 PATH$PAHT:PATH 1:PATH 2:PATH 3:--------: PATH n export PATH 你可以自己加上指定的路径中间用冒号隔开。环境变量更改后在用户下次登陆时生效。 可以利用echo $PATH查看当前当前系统PATH路径。 HOME指定用户的主工作目录即用户登陆到Linux系统中时默认的目录。HISTSIZE指保存历史命令记录的条数。LOGNAME指当前用户的登录名。HOSTNAME指主机的名称许多应用程序如果要用到主机名的话通常是从这个环境变量中来取得的SHELL指当前用户用的是哪种Shell。LANG/LANGUGE和语言相关的环境变量使用多种语言的用户可以修改此环境变量。MAIL指当前用户的邮件存放目录。 注意上述变量的名字并不固定如HOSTNAME在某些Linux系统中可能设置成HOST 二、Linux也提供了修改和查看环境变量的命令下面通过几个实例来说明 echo 显示某个环境变量值 echo $PATHexport 设置一个新的环境变量 export HELLO“hello” (可以无引号)env 显示所有环境变量set 显示本地定义的shell变量unset 清除环境变量 unset HELLOreadonly 设置只读环境变量 readonly HELLO 三、C程序调用环境变量函数 getenv()返回一个环境变量。setenv()设置一个环境变量。unsetenv()清除一个环境变量。 25. 内核空间和用户空间 内核态cpu可以访问内存的所有数据包括外围设备例如硬盘网卡cpu也可以将自己从一个程序切换到另一个程序。 用户态只能受限的访问内存且不允许访问外围设备占用cpu的能力被剥夺cpu资源可以被其他程序获取。 为什么要有用户态和内核态 由于需要限制不同的程序之间的访问能力, 防止他们获取别的程序的内存数据, 或者获取外围设备的数据, 并发送到网络, CPU划分出两个权限等级 – 用户态和内核态。 现代的计算机体系结构中存储管理通常都包含保护机制。提供保护的目的是要避免系统中的一个任务访问属于另外的或属于操作系统的存储区域。如在 IntelX86体系中就提供了特权级这种保护机制通过特权级别的区别来限制对存储区域的访问。 基于这种构架Linux操作系统对自身进行了划分一部分核心软件独立于普通应用程序运行在较高的特权级别上Linux 使用Intel体系的特权级3来运行内核。它们驻留在被保护的内存空间上拥有访问硬件设备的所有权限 Linux将此称为内核空间。 相对的其它部分被作为应用程序在用户空间执行。它们只能看到允许它们使用的部分系统资源并且不能使用某些特定的系统功能不能直接访问硬件不能直接访问内核空间当然还有其他一些具体的使用限制。Linux使用 Intel体系的特权级0来运行用户程序。 从 安全角度讲将用户空间和内核空间置于这种非对称访问机制下是很有效的它能抵御恶意用户的窥探也能防止质量低劣的用户程序的侵害从而使系统运行得更稳 定可靠。但是如果像这样完全不允许用户程序访问和使用内核空间的资源那么我们的系统就无法提供任何有意义的功能了。为了方便用户程序使用在内核空间才 能完全控制的资源而又不违反上述的特权规定从硬件体系结构本身到操作系统都定义了标准的访问界面。 内核与用户空间信息交互方式 1使用内存映像 Linux通 过内存映像机制来提供用户程序对内存直接访问的能力。内存映像的意思是把内核中特定部分的内存空间映射到用户级程序的内存空间去。也就是说用户空间和内 核空间共享一块相同的内存。这样做的直观效果显而易见内核在这块地址内存储变更的任何数据用户可以立即发现和使用根本无须数据拷贝。而在使用系统调 用交互信息时在整个操作过程中必须有一步数据拷贝的工作——或者是把内核数据拷贝到用户缓冲区或只是把用户数据拷贝到内核缓冲区——这对于许多数据传 输量大、时间要求高的应用这无疑是致命的一击许多应用根本就无法忍受数据拷贝所耗费的时间和资源。 2编写自己的系统调用 从前文可以看出系统调用是用户级程序访问内核最基本的方法。目前linux大致提供了二百多个标准的系统调用 并且允许我们添加自己的系统调用来实现和内核的信息交换。比如我们希望建立一个系统调用日志系统将所有的系统调用动作记录下来以便进行入侵检测。此 时我们可以编写一个内核服务程序。该程序负责收集所有的系统调用请求并将这些调用信息记录到在内核中自建的缓冲里。我们无法在内核里实现复杂的入侵检 测程序因此必须将该缓冲里的记录提取到用户空间。最直截了当的方法是自己编写一个新系统调用实现这种提取缓冲数据的功能。当内核服务程序和新系统调用都 实现后我们就可以在用户空间里编写用户程序进行入侵检测任务了入侵检测程序可以定时、轮训或在需要的时候调用新系统调用从内核提取数据然后进行入侵 检测(具体步骤和代码参见Linux内核之旅网站电子杂志第四期)。 1用户态切换到内核态的3种方式 a. 系统调用 这是用户态进程主动要求切换到内核态的一种方式用户态进程通过系统调用申请使用操作系统提供的服务程序完成工作比如前例中fork()实际上就是执行了一个创建新进程的系统调用。而系统调用的机制其核心还是使用了操作系统为用户特别开放的一个中断来实现例如Linux的int 80h中断。 b. 异常 当CPU在执行运行在用户态下的程序时发生了某些事先不可知的异常这时会触发由当前运行进程切换到处理此异常的内核相关程序中也就转到了内核态比如缺页异常。 c. 外围设备的中断 当外围设备完成用户请求的操作后会向CPU发出相应的中断信号这时CPU会暂停执行下一条即将要执行的指令转而去执行与中断信号对应的处理程序如果先前执行的指令是用户态下的程序那么这个转换的过程自然也就发生了由用户态到内核态的切换。比如硬盘读写操作完成系统会切换到硬盘读写的中断处理程序中执行后续操作等。 26. Makefile 一个规则两个函数三个自动变量 makefile命令规则makefile 和Makefile 写makefile文件为了便于管理项目可以先分别编译每个文件然后联编。这样的话改动一个文件则其他.o文件不需要更改。 1一个规则 语法 目标 依赖条件 hello:hello.c [一个table]gcc hello.c -o hello分部编译 hello:hello.ogcc hello.o -o hello hello.o:hello.cgcc -c hello.c -o hello.o2两个函数 src$(wildcard *.c) //找到当前目录下所有后缀为.c的文件赋值给src obj$(patsubst %.c,%.o, $(src)) //把src变量里所有后缀为.c的文件替换成.o例子 现有文件add.c sub.c div.c hello.c src$(wildcard *.c) obj$(patsubset %.c,%.o,$(src))all:a.out a.out:$(obj)gcc $(obj) -o a.out clean:-rm -rf $(obj) a.out //-作用是删除不存在的文件时不报错顺序执行。在使用make clean的时候可以使用:make clean -n命令来模拟执行避免误删。 3三个自动变量 $ 表示规则命令中的目标 $ 表示规则中的第一个条件 $^ 表示规则中的所有条件组成一个列表以空格隔开如果这个列表中有重复的项则消除重复的项模式规则 %.o:%.cgcc -c $ -o $通常在makefile中定义一些变量方便修改维护 src main.c fun1.c fun2.c CCgccCPPFLAGS:c预处理的选项 如-I CFLAGS:C编译器的选项 -Wall -g -c LDFLAGS:连接器选项 -L -l27. 中断处理流程 1获取中断号 将中断号压入栈中将当前寄存器信息压入栈中栈上的信息作为函数参数调用do_IRQ函数 当中断发生时Linux系统会跳转到asm_do_IRQ()函数所有中断程序的总入口函数并且把中断号irq传进来。根据中断号找到中断号对应的irq_desc结构irq_desc结构为内核中中断的描述结构内核中有一个irq_desc结构的数组irq_desc_ptrs[NR_IRQS]然后调用irq_desc中的handle_irq函数即中断入口函数。我们编写中断的驱动即填充并注册irq_desc结构。 较好的解释 https://www.cnblogs.com/aaronLinux/p/10842499.html 28. 内核信号捕捉过程 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1YC6arRZ-1679822744349)(linux编程笔记和C编程笔记.assets/image-20200914132548527.png)] 29. Linux x86程序启动-main如何被执行到 https://zhuanlan.zhihu.com/p/52054044 30. Linux内存管理 1进程空间数据结构关系 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pUJAwcNX-1679822744360)(linux编程笔记和C编程笔记.assets/image-20200917171240231.png)] [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-H5nDEf8N-1679822744361)(linux编程笔记和C编程笔记.assets/image-20200917171522604.png)] [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vddOfOKD-1679822744362)(linux编程笔记和C编程笔记.assets/image-20200917174533525.png)] 31. valgrind调试 valgrind。 valgrind是一款专门用作内存调试内存泄露检测的开源工具软件valgrind这个名字取自北欧神话英灵殿的入口不过不能不承认它确实是Linux下做内存调用分析的神器。一般Linux系统上应该没有自带valgrind需要自行进行下载安装。 下载地址http://valgrind.org/downloads/current.html 进入下载文件夹分别执行(需要root权限且必须按默认路径安装否则有加载错误) ./configure make make install 安装成功后使用类似如下命令启动程序 valgrind --toolmemcheck --leak-checkfull --track-originsyes --leak-resolutionhigh --show-reachableyes --log-filememchecklog ./controller_test 其中–log-filememchecklog指记录日志文件名字为memchecklog–toolmemcheck和**–leak-checkful**l用于内存检测。 可以得到类似的记录 23735 23735 Thread 1: 23735 Invalid read of size 4 23735 at 0x804F327: ResourceHandlerHBMessage::~ResourceHandler() (ResourceHandler.cpp:48) 23735 by 0x804FDBE: ConnectionManagerHBMessage::~ConnectionManager() (ConnectionManager.cpp:74) 23735 by 0×8057288: MainThread::~MainThread() (MainThread.cpp:73) 23735 by 0x8077B2F: main (Main.cpp:177) 23735 Address 0×0 is not stack’d, malloc’d or (recently) free’d 23735可以看到说明为无法访问Address 0x0明显为一处错误。 这样valgrind直接给出了出错原因以及程序中所有的内存调用、释放记录非常智能在得知错误原因的情况下找出错误就效率高多了。 再说一句valgrind同时给出了程序的Memory Leak情况的报告给出了new-delete对应情况所有泄漏点位置给出这一点在其他工具很难做到十分好用。 32. gdb core文件调试 linux core dump 文件 gdb分析 core dump又叫核心转储, 当程序运行过程中发生异常, 程序异常退出时, 由操作系统把程序当前的内存状况存储在一个core文件中, 叫core dump. (linux中如果内存越界会收到SIGSEGV信号然后就会core dump) 在程序运行的过程中有的时候我们会遇到Segment fault(段错误)这样的错误。这种看起来比较困难因为没有任何的栈、trace信息输出。该种类型的错误往往与指针操作相关。往往可以通过这样的方式进行定位。 一 造成segment fault产生core dump的可能原因 1.内存访问越界 a) 由于使用错误的下标导致数组访问越界 b) 搜索字符串时依靠字符串结束符来判断字符串是否结束但是字符串没有正常的使用结束符 c) 使用strcpy, strcat, sprintf, strcmp, strcasecmp等字符串操作函数将目标字符串读/写爆。应该使用strncpy, strlcpy, strncat, strlcat, snprintf, strncmp, strncasecmp等函数防止读写越界。 2 多线程程序使用了线程不安全的函数。 3 多线程读写的数据未加锁保护。对于会被多个线程同时访问的全局数据应该注意加锁保护否则很容易造成core dump 4 非法指针 a) 使用空指针 b) 随意使用指针转换。一个指向一段内存的指针除非确定这段内存原先就分配为某种结构或类型或者这种结构或类型的数组否则不要将它转换为这种结构或类型的指针而应该将这段内存拷贝到一个这种结构或类型中再访问这个结构或类型。这是因为如果这段内存的开始地址不是按照这种结构或类型对齐的那么访问它时就很容易因为bus error而core dump. 5 堆栈溢出.不要使用大的局部变量因为局部变量都分配在栈上这样容易造成堆栈溢出破坏系统的栈和堆结构导致出现莫名其妙的错误。 二 配置操作系统使其产生core文件 首先通过ulimit命令查看一下系统是否配置支持了dump core的功能。通过ulimit -c或ulimit -a可以查看core file大小的配置情况如果为0则表示系统关闭了dump core。可以通过ulimit -c unlimited来打开。若发生了段错误但没有core dump是由于系统禁止core文件的生成。 解决方法: $ulimit -c unlimited  只对当前shell进程有效 或在**~/.bashrc** 的最后加入 ulimit -c unlimited 一劳永逸 # ulimit -c 0 $ ulimit -a core file size (blocks, -c) 0 data seg size (kbytes, -d) unlimited file size (blocks, -f) unlimited 三 用gdb查看core文件 发生core dump之后, 用gdb进行查看core文件的内容, 以定位文件中引发core dump的行. gdb [exec file] [core file] 如: gdb ./test test.core 使用gdb 调试方法首先要在gcc编译时加入-g选项。 调试core文件在Linux命令行下gdb pname corefile。 例如程序名为controller_testercore文件为core.3421则为gdb controller_tester core.3421。 这样进入了gdb core调试模式。 追踪产生segmenttation fault的位置及代码函数调用情况 gdbbt 这样一般就可以看到出错的代码是哪一句了还可以打印出相应变量的数值进行进一步分析。 gdbprint 变量名 之后就全看各位自己的编程功力与经验了gdb已经做了很多了。 33. 调试段错误的方法 1 dmesg dmesg可以在应用程序crash掉时显示内核中保存的相关信息。如下所示通过dmesg命令可以查看发生段错误的程序名称、引起段错误发生的内存地址、指令指针地址、堆栈指针地址、错误代码、错误原因等。以程序2.3为例 panfengubuntu:~/segfault$ dmesg [ 2329.479037] segfault3[2700]: segfault at 80484e0 ip 00d2906a sp bfbbec3c error 7 in libc2.10.1.so[cb400013e000]122 -g 使用gcc编译程序的源码时加上-g参数这样可以使得生成的二进制文件中加入可以用于gdb调试的有用信息。以程序2.3为例 panfengubuntu:~/segfault$ gcc -g -o segfault3 segfault3.c13.3 nm 使用nm命令列出二进制文件中的符号表包括符号地址、符号类型、符号名等这样可以帮助定位在哪里发生了段错误。以程序2.3为例 复制代码 panfengubuntu:~/segfault$ nm segfault3 08049f20 d _DYNAMIC 08049ff4 d _GLOBAL_OFFSET_TABLE_ 080484dc R _IO_stdin_usedw _Jv_RegisterClasses 08049f10 d __CTOR_END__ 08049f0c d __CTOR_LIST__ 08049f18 D __DTOR_END__ 08049f14 d __DTOR_LIST__ 080484ec r __FRAME_END__ 08049f1c d __JCR_END__ 08049f1c d __JCR_LIST__ 0804a014 A __bss_start 0804a00c D __data_start 08048490 t __do_global_ctors_aux 08048360 t __do_global_dtors_aux 0804a010 D __dso_handlew __gmon_start__ 0804848a T __i686.get_pc_thunk.bx 08049f0c d __init_array_end 08049f0c d __init_array_start 08048420 T __libc_csu_fini 08048430 T __libc_csu_initU __libc_start_mainGLIBC_2.0 0804a014 A _edata 0804a01c A _end 080484bc T _fini 080484d8 R _fp_hw 080482bc T _init 08048330 T _start 0804a014 b completed.6990 0804a00c W data_start 0804a018 b dtor_idx.6992 080483c0 t frame_dummy 080483e4 T mainU memcpyGLIBC_2.01234567891011121314151617181920212223242526272829303132333435364.ldd 使用ldd命令查看二进制程序的共享链接库依赖包括库的名称、起始地址这样可以确定段错误到底是发生在了自己的程序中还是依赖的共享库中。以程序2.3为例 panfengubuntu:~/segfault$ ldd ./segfault3linux-gate.so.1 (0x00e08000)libc.so.6 /lib/tls/i686/cmov/libc.so.6 (0x00675000)/lib/ld-linux.so.2 (0x00482000)12342.段错误的调试方法 2.1 使用printf输出信息 这个是看似最简单但往往很多情况下十分有效的调试方式也许可以说是程序员用的最多的调试方式。简单来说就是在程序的重要代码附近加上像printf这类输出信息这样可以跟踪并打印出段错误在代码中可能出现的位置。 为了方便使用这种方法可以使用条件编译指令#ifdef DEBUG和#endif把printf函数包起来。这样在程序编译时如果加上-DDEBUG参数就能查看调试信息否则不加该参数就不会显示调试信息。 2.2 使用gcc和gdb 2.2.1 调试步骤 1、为了能够使用gdb调试程序在编译阶段加上-g参数以程序2.3为例 panfengubuntu:~/segfault$ gcc -g -o segfault3 segfault3.c12、使用gdb命令调试程序 panfengubuntu:~/segfault$ gdb ./segfault3 GNU gdb (GDB) 7.0-ubuntu Copyright (C) 2009 Free Software Foundation, Inc. License GPLv3: GNU GPL version 3 or later http://gnu.org/licenses/gpl.html This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. Type show copying and show warranty for details. This GDB was configured as i486-linux-gnu. For bug reporting instructions, please see: http://www.gnu.org/software/gdb/bugs/... Reading symbols from /home/panfeng/segfault/segfault3...done. (gdb) 1234567891011123、进入gdb后运行程序 (gdb) run Starting program: /home/panfeng/segfault/segfault3 Program received signal SIGSEGV, Segmentation fault. 0x001a306a in memcpy () from /lib/tls/i686/cmov/libc.so.6 (gdb) 123456从输出看出程序2.3收到SIGSEGV信号触发段错误并提示地址0x001a306a、调用memcpy报的错位于/lib/tls/i686/cmov/libc.so.6库中。 4、完成调试后输入quit命令退出gdb (gdb) quit A debugging session is active.Inferior 1 [process 3207] will be killed.Quit anyway? (y or n) y1234562.2.2 适用场景 1、仅当能确定程序一定会发生段错误的情况下使用。 2、当程序的源码可以获得的情况下使用-g参数编译程序。 3、一般用于测试阶段生产环境下gdb会有副作用使程序运行减慢运行不够稳定等等。 4、即使在测试阶段如果程序过于复杂gdb也不能处理。 2.3 使用core文件和gdb 在4.2节中提到段错误会触发SIGSEGV信号通过man 7 signal可以看到SIGSEGV默认的handler会打印段错误出错信息并产生core文件由此我们可以借助于程序异常退出时生成的core文件中的调试信息使用gdb工具来调试程序中的段错误。 2.3.1 调试步骤 1、在一些Linux版本下默认是不产生core文件的首先可以查看一下系统core文件的大小限制 panfengubuntu:~/segfault$ ulimit -c 0122、可以看到默认设置情况下本机Linux环境下发生段错误时不会自动生成core文件下面设置下core文件的大小限制单位为KB panfengubuntu:~/segfault$ ulimit -c 1024 panfengubuntu:~/segfault$ ulimit -c102412343、运行程序2.3发生段错误生成core文件 panfengubuntu:~/segfault$ ./segfault3 段错误 (core dumped)124、加载core文件使用gdb工具进行调试 panfengubuntu:~/segfault$ gdb ./segfault3 ./core GNU gdb (GDB) 7.0-ubuntu Copyright (C) 2009 Free Software Foundation, Inc. License GPLv3: GNU GPL version 3 or later http://gnu.org/licenses/gpl.html This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. Type show copying and show warranty for details. This GDB was configured as i486-linux-gnu. For bug reporting instructions, please see: http://www.gnu.org/software/gdb/bugs/... Reading symbols from /home/panfeng/segfault/segfault3...done.warning: Cant read pathname for load map: 输入/输出错误. Reading symbols from /lib/tls/i686/cmov/libc.so.6...(no debugging symbols found)...done. Loaded symbols for /lib/tls/i686/cmov/libc.so.6 Reading symbols from /lib/ld-linux.so.2...(no debugging symbols found)...done. Loaded symbols for /lib/ld-linux.so.2 Core was generated by ./segfault3. Program terminated with signal 11, Segmentation fault. #0 0x0018506a in memcpy () from /lib/tls/i686/cmov/libc.61234567891011121314151617181920从输出看出同4.2.1中一样的段错误信息。 5、完成调试后输入quit命令退出gdb (gdb) quit14.3.2 适用场景 1、适合于在实际生成环境下调试程序的段错误即在不用重新发生段错误的情况下重现段错误。 2、当程序很复杂core文件相当大时该方法不可用。 4.4 使用objdump 4.4.1 调试步骤 1、使用dmesg命令找到最近发生的段错误输出信息 panfengubuntu:~/segfault$ dmesg ... ... [17257.502808] segfault3[3320]: segfault at 80484e0 ip 0018506a sp bfc1cd6c error 7 in libc-2.10.1.so[11000013e000]123其中对我们接下来的调试过程有用的是发生段错误的地址80484e0和指令指针地址0018506a。 2、使用objdump生成二进制的相关信息重定向到文件中 panfengubuntu:~/segfault$ objdump -d ./segfault3 segfault3Dump1其中生成的segfault3Dump文件中包含了二进制文件的segfault3的汇编代码。 3、在segfault3Dump文件中查找发生段错误的地址 panfengubuntu:~/segfault$ grep -n -A 10 -B 10 80484e0 ./segfault3Dump 121- 80483df: ff d0 call *%eax 122- 80483e1: c9 leave 123- 80483e2: c3 ret 124- 80483e3: 90 nop 125- 126-080483e4 main: 127- 80483e4: 55 push %ebp 128- 80483e5: 89 e5 mov %esp,%ebp 129- 80483e7: 83 e4 f0 and $0xfffffff0,%esp 130- 80483ea: 83 ec 20 sub $0x20,%esp 131: 80483ed: c7 44 24 1c e0 84 04 movl $0x80484e0,0x1c(%esp) 132- 80483f4: 08 133- 80483f5: b8 e5 84 04 08 mov $0x80484e5,%eax 134- 80483fa: c7 44 24 08 05 00 00 movl $0x5,0x8(%esp) 135- 8048401: 00 136- 8048402: 89 44 24 04 mov %eax,0x4(%esp) 137- 8048406: 8b 44 24 1c mov 0x1c(%esp),%eax 138- 804840a: 89 04 24 mov %eax,(%esp) 139- 804840d: e8 0a ff ff ff call 804831c memcpyplt 140- 8048412: c9 leave 141- 8048413: c3 ret 12345678910111213141516171819202122通过对以上汇编代码分析得知段错误发生main函数对应的汇编指令是movl $0x80484e0,0x1c(%esp)接下来打开程序的源码找到汇编指令对应的源码也就定位到段错误了。 4.4.2 适用场景 1、不需要-g参数编译不需要借助于core文件但需要有一定的汇编语言基础。 2、如果使用了gcc编译优化参数-O1-O2-O3的话生成的汇编指令将会被优化使得调试过程有些难度。 4.5 使用catchsegv catchsegv命令专门用来扑获段错误它通过动态加载器ld-linux.so的预加载机制PRELOAD把一个事先写好的库/lib/libSegFault.so加载上用于捕捉断错误的出错信息。 panfengubuntu:~/segfault$ catchsegv ./segfault3 Segmentation fault (core dumped) *** Segmentation fault Register dump:EAX: 00000000 EBX: 00fb3ff4 ECX: 00000002 EDX: 00000000ESI: 080484e5 EDI: 080484e0 EBP: bfb7ad38 ESP: bfb7ad0cEIP: 00ee806a EFLAGS: 00010203CS: 0073 DS: 007b ES: 007b FS: 0000 GS: 0033 SS: 007bTrap: 0000000e Error: 00000007 OldMask: 00000000ESP/signal: bfb7ad0c CR2: 080484e0Backtrace: /lib/libSegFault.so[0x3b606f] ??:0(??)[0xc76400] /lib/tls/i686/cmov/libc.so.6(__libc_start_main0xe6)[0xe89b56] /build/buildd/eglibc-2.10.1/csu/../sysdeps/i386/elf/start.S:122(_start)[0x8048351]Memory map:00258000-00273000 r-xp 00000000 08:01 157 /lib/ld-2.10.1.so 00273000-00274000 r--p 0001a000 08:01 157 /lib/ld-2.10.1.so 00274000-00275000 rw-p 0001b000 08:01 157 /lib/ld-2.10.1.so 003b4000-003b7000 r-xp 00000000 08:01 13105 /lib/libSegFault.so 003b7000-003b8000 r--p 00002000 08:01 13105 /lib/libSegFault.so 003b8000-003b9000 rw-p 00003000 08:01 13105 /lib/libSegFault.so 00c76000-00c77000 r-xp 00000000 00:00 0 [vdso] 00e0d000-00e29000 r-xp 00000000 08:01 4817 /lib/libgcc_s.so.1 00e29000-00e2a000 r--p 0001b000 08:01 4817 /lib/libgcc_s.so.1 00e2a000-00e2b000 rw-p 0001c000 08:01 4817 /lib/libgcc_s.so.1 00e73000-00fb1000 r-xp 00000000 08:01 1800 /lib/tls/i686/cmov/libc-2.10.1.so 00fb1000-00fb2000 ---p 0013e000 08:01 1800 /lib/tls/i686/cmov/libc-2.10.1.so 00fb2000-00fb4000 r--p 0013e000 08:01 1800 /lib/tls/i686/cmov/libc-2.10.1.so 00fb4000-00fb5000 rw-p 00140000 08:01 1800 /lib/tls/i686/cmov/libc-2.10.1.so 00fb5000-00fb8000 rw-p 00000000 00:00 0 08048000-08049000 r-xp 00000000 08:01 303895 /home/panfeng/segfault/segfault3 08049000-0804a000 r--p 00000000 08:01 303895 /home/panfeng/segfault/segfault3 0804a000-0804b000 rw-p 00001000 08:01 303895 /home/panfeng/segfault/segfault3 09432000-09457000 rw-p 00000000 00:00 0 [heap] b78cf000-b78d1000 rw-p 00000000 00:00 0 b78df000-b78e1000 rw-p 00000000 00:00 0 bfb67000-bfb7c000 rw-p 00000000 00:00 0 [stack]123456789101112131415161718192021222324252627282930313233343536373839404142434445一些注意事项 1、出现段错误时首先应该想到段错误的定义从它出发考虑引发错误的原因。 2、在使用指针时定义了指针后记得初始化指针在使用的时候记得判断是否为NULL。 3、在使用数组时注意数组是否被初始化数组下标是否越界数组元素是否存在等。 4、在访问变量时注意变量所占地址空间是否已经被程序释放掉。 5、在处理变量时注意变量的格式控制是否合理等。 34. 函数调用过程 当发生函数调用的时候,栈空间中存放的数据是这样的: 1、调用者函数把被调函数所需要的参数按照与被调函数的形参顺序相反的顺序压入栈中,即:从右向左依次把被调函数所需要的参数压入栈; 2、调用者函数使用call指令调用被调函数,并把call指令的下一条指令的地址当成返回地址压入栈中(这个压栈操作隐含在call指令中); 3、在被调函数中,被调函数会先保存调用者函数的栈底地址(push ebp),然后再保存调用者函数的栈顶地址,即:当前被调函数的栈底地址(mov ebp,esp); 4、在被调函数中,从ebp的位置处开始存放被调函数中的局部变量和临时变量,并且这些变量的地址按照定义时的顺序依次减小,即:这些变量的地址是按照栈的延伸方向排列的,先定义的变量先入栈,后定义的变量后入栈; 所以,发生函数调用时,入栈的顺序为: 参数N 参数N-1 参数N-2 … 参数3 参数2 参数1 函数返回地址 上一层调用函数的EBP/BP 局部变量1 局部变量2 … 局部变量N *函数调用栈如下图所示:* [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VfLIrPYB-1679822744363)(linux编程笔记和C编程笔记.assets/image-20201014172535843.png)] 函数返回值传递 详细过程可参考《程序员的自我修养》10.2.3节。 如果一个函数需要返回一个大于8字节的数据参数传递的方式是 给被调参数传递一个临时栈上内存快的地址然后进入函数体函数返回时会将函数体内修改的数据经过eax寄存器复制到临时内存块上之后返回调用者栈帧上后在此次将临时内存快上的数据复制到调用者创建的对象上。 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BmMUjgFq-1679822744364)(linux编程笔记和C编程笔记.assets/image-20210420224438980.png)] 35. linux内核解析笔记 35.1register 寄存器 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-71SSVo22-1679822744365)(linux编程笔记和C编程笔记.assets/image-20201014112058583.png)] [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pkLbcMfe-1679822744366)(linux编程笔记和C编程笔记.assets/image-20201014163644110.png)] cpu实际取指令时是通过cs:eip来定位一条指令。 另外还有标志寄存器参数比较多。 35.2 寻址模式和简单指令 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4aV9OESl-1679822744366)(linux编程笔记和C编程笔记.assets/image-20201014164545780.png)] pushl %eax 等价的命令为 subl $4,%esp //将栈顶指针esp减4因为栈是向下增长向低地址增加 movl %eax,(%esp)//然后将eax中存放的值放到esp所指地址中 至此就完成了压栈动作popl %eax 等价命令 movl (%esp),%eax //将esp保存的地址的值赋给eax addl $4,%esp //将esp保存的地址加4 至此完成了弹栈操作call 0x12345 等价命令 pushl %eip(*) //将eip保存的下一条指令地址压栈 movl $0x12345 ,%eip(*) //将0x12345这个地址作为立即数赋给eip,完成跳转ret 命令等价于 popl %eip(*) //从堆栈中弹出下一条指令到eip执行两个宏指令 enter 等价于 pushl %ebp //创建栈帧 movl %esp,%ebpleave 等价于 movl %ebp,%esp //栈帧回退esp转到当前栈帧的ebp位置 popl %ebp //ebp变为从栈中弹出的值即上个栈帧的ebp位置35.3 函数调用过程 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-01f4ILbt-1679822744371)(linux编程笔记和C编程笔记.assets/image-20201014172619722.png)] 整个栈操作的过程是 1.调用者执行call指令调用背调函数call指令完成将eip保存着下一条指令压入栈中设置eip为被调用代码的第一条指令。 2.进入被调用函数体内创建被调用函数的栈帧先将上一个栈帧的ebp入栈然后将ebp与esp调整到相同位置此位置为这个函数栈帧的开始位置。 3.然后就是执行函数体 4.退栈将ebp保存的地址赋给esp这样就使esp跳转到了这个函数栈帧的底部并重新给ebp赋值然后就进入了调用函数的栈帧。 5.然后执行ret从栈中弹出eip的新指令进入调用者的下一条指令执行。g:pushl %ebpmovl %esp,%ebpmovl 8(%ebp),%eaxaddl $3,%eaxpopl %ebpret f:pushl %ebpmovl %esp, %ebpsubl $4, %espmovl 8(%ebp), %eaxmovl %eax, (%esp)call gleave ret main:pushl %ebpmovl %esp, %ebpsubl $4, %espmovl $8, (%esp)call faddl $1, %eaxleaveret36. linux下检查内存状态的命令 1、free命令 free 命令会显示系统内存的使用情况包括物理内存、交换内存(swap)和内核缓冲区内存等。 $ freetotal used free shared buff/cache available Mem: 32946324 2489392 11422656 1622872 19034276 28352888 Swap: 0 0 0释义 Mem内存使用情况。 Swap交换空间虚拟内存使用情况。 total系统总共可用物理内存、交换空间大小。 used已经被使用的物理内存、交换空间大小。 free剩余可用物理内存、交换空间大小。 shared被共享使用的物理内存大小。 buff/cache被 buffer 和 cache 使用的物理内存大小。 available还可以被应用程序使用的物理内存大小。 常见用法 free -h      //以更友好的方式显示会以K、M、G为单位来显示 free -h -s 3   //以一定时间间隔重复的输出这个命令是每3秒输出一次free 命令中的信息都来自于 /proc/meminfo 文件。 2、vmstat命令 vmstat 是Virtual Meomory Statistics虚拟内存统计的缩写可对操作系统的虚拟内存、进程、CPU活动进行监控是对系统的整体情况进行的统计。 $ vmstat procs -----------memory----------- --swap-- --io-- --system-- -----cpu-----r b swpd free buff cache si so bi bo in cs us sy id wa st1 0 0 14376368 161976 1130836 0 0 0 3 2 2 0 0 100 0 0与内存使用情况相关的是memory列和swap列我们只看这两列。 memory列 swpd使用的虚拟内存大小。 free空闲物理内存大小。 buffbuffer cache内存大小。 cachepage cache的内存大小。 swap列 si每秒从交换区读入到内存的大小由磁盘调入内存单位kb/s so每秒从内存写出到交换区的大小由内存调入磁盘单位kb/s 常见用法 vmstat 1    //每隔1s打印一次 vmstat 1 5   //每隔1秒打印一次打印五次 vmstat -s    //显示内存相关统计信息及多种系统活动数量3、top命令 使用top命令可以查看正在运行的进程和系统负载信息包括cpu负载、内存使用、各个进程所占系统资源等top命令以一定频率动态更新这些统计信息。 top - 10:45:21 up 211 days, 17:14, 2 users, load average: 0.08, 0.09, 0.03 Tasks: 228 total, 1 running, 227 sleeping, 0 stopped, 0 zombie Cpu(s): 0.4%us, 0.1%sy, 0.1%ni, 99.4%id, 0.0%wa, 0.0%hi, 0.0%si, 0.0%st Mem: 32880232k total, 22032060k used, 10848172k free, 569680k buffers Swap: 0k total, 0k used, 0k free, 17771208k cachedPID USER PR NI VIRT RES SHR S %CPU %MEM TIME COMMAND 9757 root 20 0 15160 1224 836 R 2.0 0.0 0:00.01 top 1 root 20 0 19364 1644 1312 S 0.0 0.0 2:06.03 init 2 root 20 0 0 0 0 S 0.0 0.0 0:00.30 kthreadd 3 root RT 0 0 0 0 S 0.0 0.0 0:02.75 migration/0 4 root 20 0 0 0 0 S 0.0 0.0 2:04.41 ksoftirqd/0 5 root RT 0 0 0 0 S 0.0 0.0 0:00.00 stopper/0 6 root RT 0 0 0 0 S 0.0 0.0 0:39.92 watchdog/0 7 root RT 0 0 0 0 S 0.0 0.0 0:01.44 migration/1 8 root RT 0 0 0 0 S 0.0 0.0 0:00.00 stopper/1 9 root 20 0 0 0 0 S 0.0 0.0 1:23.26 ksoftirqd/1 10 root RT 0 0 0 0 S 0.0 0.0 0:31.05 watchdog/1 11 root RT 0 0 0 0 S 0.0 0.0 0:02.06 migration/2 12 root RT 0 0 0 0 S 0.0 0.0 0:00.00 stopper/2 13 root 20 0 0 0 0 S 0.0 0.0 0:45.78 ksoftirqd/2 14 root RT 0 0 0 0 S 0.0 0.0 0:29.91 watchdog/2 15 root RT 0 0 0 0 S 0.0 0.0 0:10.61 migration/3 16 root RT 0 0 0 0 S 0.0 0.0 0:00.00 stopper/3 17 root 20 0 0 0 0 S 0.0 0.0 1:57.03 ksoftirqd/3 18 root RT 0 0 0 0 S 0.0 0.0 0:32.77 watchdog/3 19 root RT 0 0 0 0 S 0.0 0.0 0:01.82 migration/4 20 root RT 0 0 0 0 S 0.0 0.0 0:00.00 stopper/4 21 root 20 0 0 0 0 S 0.0 0.0 1:58.64 ksoftirqd/4 22 root RT 0 0 0 0 S 0.0 0.0 0:32.96 watchdog/4 23 root RT 0 0 0 0 S 0.0 0.0 0:03.28 migration/5 24 root RT 0 0 0 0 S 0.0 0.0 0:00.00 stopper/5 25 root 20 0 0 0 0 S 0.0 0.0 0:50.67 ksoftirqd/5 26 root RT 0 0 0 0 S 0.0 0.0 0:30.28 watchdog/5 27 root RT 0 0 0 0 S 0.0 0.0 0:06.60 migration/6 28 root RT 0 0 0 0 S 0.0 0.0 0:00.00 stopper/6反映系统内存使用状况的是下面这两行 Mem: 32880232k total, 22032060k used, 10848172k free, 569680k buffers Swap: 0k total, 0k used, 0k free, 17771208k cachedMem行是物理内存使用情况分别是物理内存总量已使用的物理内存总量空闲物理内存总量用作内核缓存区的内存量。 Swap行是交换区使用情况分别是交换区总量已使用的交换区总量空闲交换区总量缓冲的交换区总量。 Top命令的下侧区域显示的是各个进程使用的系统资源统计信息内存相关列如下 VIRT列进程使用的虚拟内存总量单位kb。RES列进程使用的、未被换出的物理内存大小单位kb。SHR列共享内存大小单位kb。%MEM 列进程使用的物理内存百分比。 4、cat /proc/meminfo /proc/meminfo是了解Linux系统内存使用状况的主要接口我们最常用的”free”、”vmstat”等命令就是通过它获取数据的。/proc/meminfo所包含的信息比”free”等命令要丰富得多但也很复杂不再一一解释了。感兴趣的话可以看看这篇文章http://linuxperf.com/?p142对 /proc/meminfo 有较详细的解释。 [[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2MsmWn6p-1679822744372)(linux编程笔记和C编程笔记.assets/copycode.gif)]](javascript:void(0) $ cat /proc/meminfo MemTotal: 16430636 kB MemFree: 14376492 kB MemAvailable: 15298732 kB Buffers: 161976 kB Cached: 1022440 kB SwapCached: 0 kB Active: 1369780 kB Inactive: 438696 kB Active(anon): 624376 kB Inactive(anon): 49444 kB Active(file): 745404 kB Inactive(file): 389252 kB Unevictable: 0 kB Mlocked: 0 kB SwapTotal: 0 kB SwapFree: 0 kB Dirty: 60 kB Writeback: 0 kB AnonPages: 624068 kB Mapped: 88140 kB Shmem: 49752 kB Slab: 108076 kB SReclaimable: 82864 kB SUnreclaim: 25212 kB KernelStack: 4464 kB PageTables: 10480 kB NFS_Unstable: 0 kB Bounce: 0 kB WritebackTmp: 0 kB CommitLimit: 8215316 kB Committed_AS: 1811204 kB VmallocTotal: 34359738367 kB VmallocUsed: 34780 kB VmallocChunk: 34359695100 kB HardwareCorrupted: 0 kB AnonHugePages: 182272 kB CmaTotal: 0 kB CmaFree: 0 kB HugePages_Total: 0 HugePages_Free: 0 HugePages_Rsvd: 0 HugePages_Surp: 0 Hugepagesize: 2048 kB DirectMap4k: 169832 kB DirectMap2M: 8218624 kB DirectMap1G: 10485760 kB[[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9A4uiQlm-1679822744373)(linux编程笔记和C编程笔记.assets/copycode.gif)]](javascript:void(0) 5、 ps aux命令 ps aux 命令可以查看系统中各个进程的运行情况包括了进程占用的内存%MEM 列就是各个进程的内存占用百分比。 $ ps aux USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND root 1231 0.9 2.2 1368496 375824 ? SNl Jun21 346:39 ./bcm-agent bls 6891 0.1 0.9 2892024 159516 ? Sl Jun21 55:29 /opt/bls/lib/jre/bin/java -Duser.languageen_us -Xmx500m -XX:UseParallelGC -XX:UseParallelOldGC - root 389 0.0 0.2 96376 46212 ? Ss Jun21 0:14 /usr/lib/systemd/systemd-journald root 1245 0.0 0.2 1200080 40200 ? Sl Jun21 19:48 /opt/hosteye/bin/hosteye --is_child_modetrue --is_console_modefalse --start_mode0 root 1199 0.0 0.1 288132 28188 ? Ssl Jun21 2:06 /usr/sbin/rsyslogd -n root 898 0.0 0.1 573840 17152 ? Ssl Jun21 5:08 /usr/bin/python -Es /usr/sbin/tuned -l -P37. nm命令 nm是names的缩写 nm命令主要是用来列出某些文件中的符号说白了就是一些函数和全局变量等。 一般来说 搞linux开发的人 才会用到nm命令 非开发的人 应该用不到。 有何用处 在一个目标文件中查看变量、函数等在哪个段偏移量是多少符号类型是什么。 例如 $: readelf -s tmp.o 语法格式: nm [参数] 常用选项 参考实例 显示hello.o 中的未定义符号需要和其他对象文件进行链接 [rootlinuxcool ~]# nm -u hello.o 在/usr/lib/ 目录下找出哪个库文件定义了memset函数 [rootlinuxcool ~]# nm -A /usr/lib/* 2/dev/null | grep T memset 显示nm的版本号 [rootlinuxcool ~]# nm -v显示调试符号 [rootlinuxcool ~]# nm -a hello.o
http://www.hkea.cn/news/14316183/

相关文章:

  • 泰安企业建站公司平台未备案网站查询
  • 网站建设费用怎么做分录家装设计师怎么学
  • 分享10个国外优秀的平面设计网站为什么招聘网站做不大
  • 服务器上的网站不能访问重庆森林百度云
  • 注册消防工程师seo实战培训课程
  • 网站怎么做伪静态上海网站设计推荐刻
  • 成都网站建设优秀公司建设和交通局网站
  • 建设网站公司怎么分工做家具城网站的意义
  • 临沂网网站建设绘制网站结构图
  • 网站做404公司设计网站多少钱
  • wordpress 中英文站点青浦区网站建设
  • 做壁纸的网站素材网站php程序源码
  • 徐州网站建设方案推广想要接网站业务如何做
  • 2008 iis 添加网站原生h5网站怎么做
  • 阿里云网站建设方案书沈阳建信建设工程有限公司位置
  • 福利WordPress网站自动采集源码黄冈网站设计推广哪家好
  • 玉山县建设局的网站wordpress大主题上传
  • 浙江省住房和城乡建设局网站网站开发飞沐
  • a032网站模版网页设计模板代码网站
  • vpn免流网站建设网站开发最严重的问题
  • 知名设计网站WordPress推送百家号
  • 怎么利用婚庆网站做营销世界500强企业有哪些
  • 网站建设的财务计划书门户app网站建设多少钱
  • 商务局网站溪江农贸市场建设有了网站怎么做app
  • 往网站上做新东西需要什么威海哪家网站做的好
  • 国内一家做国外酒店团购的网站有想做企业网站建设
  • 网站托管 域名申请一个域名可以建设一个网站吗
  • 网站建设分期进行怎么入账上海装修公司咨询
  • 知乎做笔记的网站管理咨询的主体包括哪些
  • 网站开发职业生涯规划书wordpress开启伪静态找不到页面