已经学习了
调试
Linux下的调试和windows下有区别吗?
1、调试思路上,一定时一样的
2、调试的操作方式、一定时不一样的-------》命令行调试gdb
注意编译报错问题:
error: 'for' loop initial declarations are only allowed in C99 mode
note: use option -std=c99 or -std=gnu99 to compile your code
实验
创建一个.c文件
cpp
1 #include <stdio.h>
2 #include <time.h> //可用man 3 time 查看用法
3 void Print(int sum)
4 {
5 long long timestamp = time(NULL);//时间戳
6 printf("sum = %d , timestamp=%lld\n",sum,timestamp);
7 }
8 int AddToVal(int from,int to)
9 {
10 int sum =0;
11 int i;
12 for( i= from; i < to; i++)
13 {
14 sum += i;
15 }
16 return sum;
17 }
18
19 int main()
20 {
21
22 int sum = AddToVal(0,100);
23 Print(sum);
24
25 return 0;
26 }
~
可以用在线工具转换一下时间戳
如果出现bash 提醒证明没有安装,就需要
yum install -y gdb
gdb [文件名]
注意现在时不可以调试的
要知道我们的版本是要在debug下才可以调试,在release版本下是进行不了调试的
为什么要有debug版本和release版本
因为debug版本是个程序员调试的
release是给用户使用的
所以debug版本的体积肯定比release板的大
如何让它gcc编译出来就是debug版本呢?gcc [文件.c] -o [修改名字] -g //-g就是指定为debug版本
修改makefile
readelf -S [可执行程序文件] //可以读取可执行程序二进制格式//读到了很多不懂的东西,反正要知道可执行文件并不是简单的二进制,而是分了很多各区就可以
readelf -S 【可执行文件】
这样太多信息了readelf -S 【可执行文件】| grep debug
gdb 操作
显示代码
(gdb) l 0 //从第0行显示 -list
(gdb) l 1 //从第1行显示
l 0 //执行命令后
按回车会自动显示下行代码因为会记住命令
打断点 breakpoint
(gdb) b n //给第n行打断点 ---形成编号,break也可以打断点
这里打了断点不像windows下可以看到断点,如何查看断点呢?
(gdb) info b//查看断点信息---看到编号
(gdb) d [编号] //去掉断点 delete
(gdb) r //运行起来并来到断点处
调试运行 r --run
逐过程 :n----next //到下一行
逐语句:s ----step
(gbd) c //运行至下一个断点处
bt //调用堆栈
finish //跑完逐语句的当函数
p //临时查看变量的值
display //长显示变量的值
undisplay
until N
为什么会来到18行?
总结下:
-------=======================================================------------------------
disable breakappoint [编号] //关闭断点,并不是取消断点
enable [编号] //打开断点
冯诺依曼体系结构
这里的存储器指的是谁?
内存:掉电易失
磁盘(外存):永久性存储能力
外设((输入设备和输出设备)或者仅输入仅输出):网卡和磁盘(都是输入输出 )
|----------------------|----|
| CPU:运算器+控制器+其他 (计算的) | 快 |
| 存储器(内存) (临时存储) | 较快 |
| 外设 (永久存储) | 较慢 |
CPU 其实只可以被动接受别人的指令,别人的数据- - -》执行别人的指令计算别人数据的目的
CPU有自己的指令集才可以认识到别人的指令
我们写代码,编译本质是做什么?将二进制可执行程序编译成指令,才可以让CPU认识
将外设上的数据预先存进内存里,定期再刷新,就可以很好的解决cpu和外设读取写入数据的差异
操作系统帮我们做策略(帮我们管理软硬件,临时数据的处理,刷新数据,清理,增加)
所以开机要加载操作系统
硬件和软件的完美结合就是计算机
将外设数据搬到内存中,内存中的数据写入到外设中的过程叫IO的过程INPUT/OUTPUT
总结:在数据层面
cpu不和外设直接沟通,只和内存打交道
为了提高整机效率
example:
进度条 明明调用了printf为什么没被打印出来?显示屏是外设,代码加载到cpu不是直接输出到外设而是加载到内存中定期刷新的,这也是体系结构可以解释的
cpu的控制器:状态捕捉
cpu运算器:算术运算和逻辑运算
操作系统
是一个进行软硬件的管理
对于管理者的理解:
管理者不需要和被管理者直接交互依旧能够把被管理对象管理起来
管理者:需要具备重大事宜决策能力如何做到的?
管理本质是管理者对数据的管理
我和管理者不需要有交互,但是我的所有数据,早就在管理者手上而且一直在更新
硬件做管理本质先描述再组织,描述后有了对应的内核的数据结构,再用特定数据结构将对象管理起来
总结:
1、做管理本质上是对数据进行管理
2、拿数据是有特定的驱动程序进行的
3、操作系统拿到数据之后要对数据进行描述(磁盘是磁盘的,键盘是键盘的),对应的设备再组织用特定的数据结构组织进行管理(先描述再组织)、
------------============================================----------------------------
银行与计算机软硬件体系结构的比较
系统调用层(保护操作系统)
shell(指令操作)c库(编程操作)。。。。等:
当我们向显示器打印是不是硬件写入?
显示器属于输出外设,在程序运行时,调用库中的printf, 根据冯诺依曼体系结构,从外设数据打入内存中,再由CPU处理返回给内存再打印到外设上,所以当我们调用printf,c++中的cout并不是直接打入硬件上的, 其实是printf调用了操作系统接口,让操作系统帮我们访问硬件打印的
其实硬件写入是一件成本很高的事情,我们第一次学习hello world就是输出到显示屏,不是库写的多好,而是操作系统给我们提供了接口
软硬件结构图:
系统调用和库函数概念
1、在开发角度,操作系统对外会表现为一个整体 但是会暴露自己的部分接口,供上层开发使用,这部分 由操作系统提供的接口,叫做系统调用。
2、系统调用在使用上,功能比较基础,对用户的要求相对也比较高,所以,有心的开发者可以对部分系统调用进行适度封装,从而形成库,有了库,就很有利于更上层用户或者开发者进行二次开发。
什么是进程?
struct task_struct 内核结构体 -》 内核对象task_struct 对象--》将该结构和你的代码和数据关联起来--》先描述,在组织的工作
注意:
进程=内核数据结构(task_struct)+进程对应的磁盘代码
为什么会有PCB(task_struct)结构体呢?
管理的本质是对数据做管理, 所以就要先拿到数据,拿到的数据可能很多、乱、杂。所以要对数据进行归类,根据先描述再组织,和面向对象的思想,提取对应的数据进行(按照统一标准)管理
操作系统是一个软件,它早就加载到内存中一直运行着,当要启动一个程序所谓的把磁盘上的程序加载到内存
----===---=====----============-----------------=================-----=================
见见进程:
windows下:
实验Linux
如何查看进程呢?
ps ajx //查看所有进程可以加一些指令一起和管道使用
ps ajx | grep [你要查的进程] //grep 行过滤
小技巧:
Linux中,进程都拥有以下的ID
- Process ID(PID)
Linux中标识进程的一个数字,它的值是不确定的,是由系统分配的(但是有一个例外,启动阶段,kernel运行的第一个进程是init,它的PID是1,是所有进程的最原始的父进程),每个进程都有唯一PID,当进程退出运行之后,PID就会回收,可能之后创建的进程会分配这个PID - Parent Process ID(PPID)
字面意思,父进程的PID - Process Group ID(PGID)
PGID就是进程所属的Group的Leader的PID,如果PGID=PID,那么该进程是Group Leader - Session ID(SID)
和PGID非常相似,SID就是进程所属的Session Leader的PID,如果SID==PID,那么该进程是session leader
杀掉进程
kill -9 [PID编码] //进程ID
第一个系统调用getpid
查看运行起来后,才会使用系统调用接口,才获取进程中的标识
注意:
=========================================================================
根目录下有一个文件proc是一个内存级的文件
注意
下面的这个数字目录其实是进程编号就是刚刚在运行代码的PID
就可以找到
ls -d //如果查看的是一个目录就只显示目录名不显示子文件
exe是进程对应的可执行文件(磁盘)
进程是加载磁盘上exe
如果在运行中删除exe,就是已经写入内存中了,将exe删除会发生什么?
另一种调用方式(了解)
getppid()
探讨一下
如果我们结束这个进程会发生什么呢?
总结:
在命令行上启动的进程,一般它的父进程没有特殊情况的话,都是bash
父bash交给子bash去执行,出问题没有影响到父进程
比如写了个代码是错误的,父bash创建子bash执行,直接报错此时子bash搞砸了,子bash结束了返回报错问题,但是父bash还是可以用
Linux如何创建子进程(见见🐖跑)
man 2 fork //系统调用手册
学习返回值
这个id有两个值(知道就行后面会解释)
两个死循环可以同时执行就证明了有两个进程
并发式编程 不同系统和语言不一样