目录
(5)set:查看本地定义的shell变量(本地变量)和环境变量
残留问题:本地变量不会被子进程继承下去,为什么本地变量的子进程echo能使用本地变量local_val呢?——内建命令
1.问题1:main函数可以带参数吗?最多可以带多少? -可以,实际是3个
3.命令行可以带第三个参数!: char *env[](环境变量)
为什么要写时拷贝?创建子进程的时候,就把数据分开,不行吗? ?
fork有两个返回值,pid_ t id ,同一个变量,怎么会有不同的值?
——详情见大标题二
- [zsh@ecs-78471 ~]$ echo $PATH
- /usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/zsh/.local/bin:/home/zsh/bin

- [zsh@ecs-78471 ~]$ env
- XDG_SESSION_ID=3849
- HOSTNAME=ecs-78471
- TERM=xterm
- SHELL=/bin/bash
- HISTSIZE=10000
- SSH_CLIENT=218.6.151.253 54910 22
- SSH_TTY=/dev/pts/0
- USER=zsh
- …………………………………………
- [zsh@ecs-78471 ~]$ aaaa=100 aaaa是本地变量
- [zsh@ecs-78471 ~]$ env | grep aaaa 环境变量查不到aaaa
- [zsh@ecs-78471 ~]$ export bbbb=50 bbbb是本地变量
- [zsh@ecs-78471 ~]$ env | grep bbbb
- bbbb=50 环境变量查的到bbbb
- [zsh@ecs-78471 ~]$ set | grep aaaa
- aaaa=100 set既可以查到本地变量aaaa
- [zsh@ecs-78471 ~]$ set | grep bbbb
- bbbb=50 set也可以查到环境变量bbbb
一个问题?基本指令也是程序,为什么我们的代码程序运行要带路径,而系统的指令不用带路径? ?比如使用指令ls,pwd时直接使用即可,使用自己的myproc 可执行程序时(gcc -o mproc.c myproc) 需要./myproc.c
答:系统中是存在相关的环境变量,保存了程序的搜索路径的! 比如:执行 ls 这个可执行程序时,系统会在PATH中一个一个搜索,在特定路径(系统所有命令都在usr/bin路径下)下可以找到ls,就可以执行
系统中搜索可执行程序的环境变量叫做 PATH !

——①把自己的程序拷贝进环境变量中,就可以直接myproc执行程序了,拷贝的过程就是安装软件,但是不建议这样做,本身我们的软件就没什么意义,会污染系统,删除=卸载

——②把myproc自己的文件加入环境变量PATH中,export PATH=$PATH:路径(相当于把PATH中的路径改成PATH和myproc的路径)

错误示范:如果直接 export PATH=路径 ,会覆盖环境变量PATH的原有路径:这里pwd还能用,ls,top什么的就不能用了

- [zsh@ecs-78471 ~]$ env
- XDG_SESSION_ID=3849
- HOSTNAME=ecs-78471
- TERM=xterm
- SHELL=/bin/bash SHELL:显示shell所在路径
- HISTSIZE=10000 HISTSIZE:历史能够记录自己敲过的命令条数
- SSH_CLIENT=218.6.151.253 54910 22 SSH_CLIENT:ip地址
- SSH_TTY=/dev/pts/0
- USER=zsh USER:用户名
- LD_LIBRARY_PATH=:/home/zsh/.VimForCpp/vim/bundle/YCM.so/el7.x86_64
- 下面是ls的配色方案:
- LS_COLORS=rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:mi=01;05;37;41:su=37;41:sg=30;43:ca=30;41:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31:*.tgz=01;31:*.arc=01;31:*.arj=01;31:*.taz=01;31:*.lha=01;31:*.lz4=01;31:*.lzh=01;31:*.lzma=01;31:*.tlz=01;31:*.txz=01;31:*.tzo=01;31:*.t7z=01;31:*.zip=01;31:*.z=01;31:*.Z=01;31:*.dz=01;31:*.gz=01;31:*.lrz=01;31:*.lz=01;31:*.lzo=01;31:*.xz=01;31:*.bz2=01;31:*.bz=01;31:*.tbz=01;31:*.tbz2=01;31:*.tz=01;31:*.deb=01;31:*.rpm=01;31:*.jar=01;31:*.war=01;31:*.ear=01;31:*.sar=01;31:*.rar=01;31:*.alz=01;31:*.ace=01;31:*.zoo=01;31:*.cpio=01;31:*.7z=01;31:*.rz=01;31:*.cab=01;31:*.jpg=01;35:*.jpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35:*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35:*.svg=01;35:*.svgz=01;35:*.mng=01;35:*.pcx=01;35:*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.m2v=01;35:*.mkv=01;35:*.webm=01;35:*.ogm=01;35:*.mp4=01;35:*.m4v=01;35:*.mp4v=01;35:*.vob=01;35:*.qt=01;35:*.nuv=01;35:*.wmv=01;35:*.asf=01;35:*.rm=01;35:*.rmvb=01;35:*.flc=01;35:*.avi=01;35:*.fli=01;35:*.flv=01;35:*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.yuv=01;35:*.cgm=01;35:*.emf=01;35:*.axv=01;35:*.anx=01;35:*.ogv=01;35:*.ogx=01;35:*.aac=01;36:*.au=01;36:*.flac=01;36:*.mid=01;36:*.midi=01;36:*.mka=01;36:*.mp3=01;36:*.mpc=01;36:*.ogg=01;36:*.ra=01;36:*.wav=01;36:*.axa=01;36:*.oga=01;36:*.spx=01;36:*.xspf=01;36:
- MAIL=/var/spool/mail/zsh MAIL:系统中的邮箱路径
- PATH:命令的搜索路径:
- PATH=/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/zsh/.local/bin:/home/zsh/bin
- PWD=/home/zsh PWD:当前用户所处路径
- LANG=en_US.UTF-8 LANG:支持的编码格式,现在支持的是UTF8
- HISTCONTROL=ignoredups
- SHLVL=1
- HOME=/home/zsh HOME:代表不同用户的家目录
- LOGNAME=zsh LOGNAME:登录的人是谁
- SSH_CONNECTION=218.6.151.253 54910 192.168.0.183 22
- LESSOPEN=||/usr/bin/lesspipe.sh %s
- XDG_RUNTIME_DIR=/run/user/1002
- HISTTIMEFORMAT=%F %T zsh
- _=/usr/bin/env
echo $HOSTNAME——显示主机名
- [zsh@ecs-78471 ~]$ echo $SHELL 显示shell所在路径
- /bin/bash
- [zsh@ecs-78471 ~]$ echo $HISTSIZE 历史能够记录自己敲过的命令条数
- 10000
- [zsh@ecs-78471 ~]$ history 可以查看历史记录的命令
- ……
- [zsh@ecs-78471 ~]$ history | wc -l 查看历史记录命令的总条数
- 1440
- [zsh@ecs-78471 ~]$ echo $HOME
- /home/zsh
环境变量:系统当中用做特殊用途的系统变量。
env:查看所有的环境变量
命令行变量分两种:
1.普通变量(在env查不到)
2.环境变量(全局)(在env能查到)
环境变量具有全局属性:环境变量是会被子进程继承下去的! !
所谓得本地变量,本质就是在bash内部定义的变量,不会被子进程继承下去!


定义 qi_104是本地变量,用set能找到qi_104这个本地变量,env就找不到本地变量,用myenv显示环境变量是空,当export qi_ 104 把本地变量导成环境变量后,myenv显示环境变量就是 qi_ 104


Linux下大部分命令都是通过子进程的方式执行的!
但是,还有一部分命令,不通过子进程的方式执行,而是由bash自己执行(调用自己的对应的函数来完成特定的功能),我们把这种命令叫做内建命令!
——————————————————————————————先讲讲main函数参数
main函数的前两个参数分别是下图所示:这两个参数我们称为:命令行参数


我们给main函数传递的前两个参数 argc,char* argv[] 称为 命令行参数,传递的是命令行中输入的程序名和选项!比如命令行输入了./myproc -a -b -c,argc对应就是4,argv[ ] 中传入了这4个字符串,argv[0] = "./myproc",argv[1] = "-a",argv[2] = "-b",argv[3] = "-c",argv[4] = "NULL",指针数组以NULL结尾。
给命令行参数传程序名和选项意义是什么?我们通过实现一个命令行版的计算器来理解:
我们要实现的功能:
执行 ./myproc -a 10 20 要实现 10+20=30;
执行 ./myproc -s 10 20 要实现 10-20=-10
- #include
- #include
- #include
- #include
-
- int main()
- {
- if (argc != 4)
- {
- 如果用户输入不对,打印使用手册,-a加法,-s(subtract)减法,-m乘法,-d除法:
- printf("Usage: %s [-a|-s|-m|-d] one_data two_data\n", argv[0]);
- return 0;
- }
- int x = atoi(argv[2]); atoi:把字符串转为整数
- int y = atoi(argv[3]);
- if (strcmp("-a", argv[1]) == 0) 如果输入-a,就是加法
- {
- printf("%d+%d=%d\n", x, y, x + y);
- }
- else if (strcmp("-s", argv[1]) == 0) 如果输入-s,就是减法
- {
- printf("%d-%d=%d\n", x, y, x - y);
- }
- else if (strcmp("-m", argv[1]) == 0) 如果输入-m,就是乘法
- {
- printf("%d*%d=%d\n", x, y, x * y);
- }
- else if (strcmp("-d", argv[1]) == 0 && y != 0) 如果输入-d,就是除法
- {
- printf("%d/%d=%d\n", x, y, x / y);
- }
- else
- {
- 输入错误说明不会用,还是打印使用手册
- printf("Usage: %s [-a|-s|-m|-d] one_data two_data\n", argv[0]);
- }
-
- return 0;
- }
答:同一个程序,通过传递不同的参数,让同一个程序有不同的执行逻辑 / 执行结果。
这就解释了指令中那么多选项的由来和起作用的方式!!Linux系统中,会根据不通的选项,让不同的命令,可以有不同的表现!
这就解释了我们平时输入的指令传入了哪里
指针数组env[]和argv[]差不多,只不过传的是所有的指向环境变量的地址,指针数组也是以NULL结尾的

每个进程是会被传入环境变量参数的! ! 环境变量传给env[] :
- 1 #include
- 2 int main(int argc,char* argv[],char* env[])
- 3 {存环境变量的指针数组以NULL结尾,所以到NULL时for循环结束:
- 4 for(int i=0;env[i];i++)
- 5 {
- 6 printf("env[%d]:%s\n",i,env[i]);
- 7 }
- 8 return 0;
- 9 }
- [zsh@ecs-78471 10_18_复习环境变量]$ ./test
- env[0]:XDG_SESSION_ID=3849
- env[1]:HOSTNAME=ecs-78471
- env[2]:TERM=xterm
- env[3]:SHELL=/bin/bash
- env[4]:HISTSIZE=10000
- env[5]:SSH_CLIENT=218.6.151.253 54910 22
- env[6]:SSH_TTY=/dev/pts/0
- env[7]:USER=zsh
- env[8]:LD_LIBRARY_PATH=:/home/zsh/.VimForCpp/vim/bundle/YCM.so/el7.x86_64
- env[9]:LS_COLORS=rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:mi=01;05;37;41:su=37;41:sg=30;43:ca=30;41:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31:*.tgz=01;31:*.arc=01;31:*.arj=01;31:*.taz=01;31:*.lha=01;31:*.lz4=01;31:*.lzh=01;31:*.lzma=01;31:*.tlz=01;31:*.txz=01;31:*.tzo=01;31:*.t7z=01;31:*.zip=01;31:*.z=01;31:*.Z=01;31:*.dz=01;31:*.gz=01;31:*.lrz=01;31:*.lz=01;31:*.lzo=01;31:*.xz=01;31:*.bz2=01;31:*.bz=01;31:*.tbz=01;31:*.tbz2=01;31:*.tz=01;31:*.deb=01;31:*.rpm=01;31:*.jar=01;31:*.war=01;31:*.ear=01;31:*.sar=01;31:*.rar=01;31:*.alz=01;31:*.ace=01;31:*.zoo=01;31:*.cpio=01;31:*.7z=01;31:*.rz=01;31:*.cab=01;31:*.jpg=01;35:*.jpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35:*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35:*.svg=01;35:*.svgz=01;35:*.mng=01;35:*.pcx=01;35:*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.m2v=01;35:*.mkv=01;35:*.webm=01;35:*.ogm=01;35:*.mp4=01;35:*.m4v=01;35:*.mp4v=01;35:*.vob=01;35:*.qt=01;35:*.nuv=01;35:*.wmv=01;35:*.asf=01;35:*.rm=01;35:*.rmvb=01;35:*.flc=01;35:*.avi=01;35:*.fli=01;35:*.flv=01;35:*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.yuv=01;35:*.cgm=01;35:*.emf=01;35:*.axv=01;35:*.anx=01;35:*.ogv=01;35:*.ogx=01;35:*.aac=01;36:*.au=01;36:*.flac=01;36:*.mid=01;36:*.midi=01;36:*.mka=01;36:*.mp3=01;36:*.mpc=01;36:*.ogg=01;36:*.ra=01;36:*.wav=01;36:*.axa=01;36:*.oga=01;36:*.spx=01;36:*.xspf=01;36:
- env[10]:MAIL=/var/spool/mail/zsh
- env[11]:PATH=/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/zsh/.local/bin:/home/zsh/bin
- env[12]:PWD=/home/zsh/linux/104new/10_18_复习环境变量
- env[13]:LANG=en_US.UTF-8
- env[14]:bbbb=50
- env[15]:HISTCONTROL=ignoredups
- env[16]:SHLVL=1
- env[17]:HOME=/home/zsh
- env[18]:LOGNAME=zsh
- env[19]:SSH_CONNECTION=218.6.151.253 54910 192.168.0.183 22
- env[20]:LESSOPEN=||/usr/bin/lesspipe.sh %s
- env[21]:XDG_RUNTIME_DIR=/run/user/1002
- env[22]:HISTTIMEFORMAT=%F %T zsh
- env[23]:_=./test
- env[24]:OLDPWD=/home/zsh/linux/104new

但是如果是int fun(void) 就不可以传参。
这就解释了我们可以直接给main()函数传参的原因
- #include
- int main(int argc, char* argv[], char* env[])
- {
- int i = 0;
-
- for (; env[i]; i++) {
- printf("%s\n", env[i]);
- }
- return 0;
- }
——————————————————————————————————————————
- #include
- int main(int argc, char* argv[], char* env[])
- {
- for (int i = 0; env[i]; i++) {
- printf("%s\n", env[i]);
- }
- return 0;
- }

c语言给我们提供了一个全局变量environ

通过环境变量名直接获得环境变量的内容


a.我为什么要获取环境变量? ? 万一以后要用(一定有特殊用途!)
b:环境变量是谁给我的呢?? 目前谈不清,但是我们可以观察到!
环境变量为什么具有全局性!

程序地址空间,不是内存!
程序地址空间=进程地址空间,是操作系统上的概念!
堆,栈相对而生,
堆区向地址增大方向增长
栈区向地址减少方向增长
我们一般在C函数中定义的变量,通常在栈上保存,那么先定义的一定是地址比较高的!
如何理解static变量——函数内定义的变量用static修饰,本质是编译器会把该变量编译进全局数据区!

函数内定义的变量用static修饰,本质是编译器会把该变量编译进全局数据区! 父子进程共享全局变量。

fork创建子进程,父子进程同时运行,若在子进程中第5秒改了全局变量g_val的值,会发现

父子进程读取同一个变量(因为地址一样!),但是子进程修改的全局变量后,父子进程读取到的
内容却不一样! ! ! !
结论:——我们在C/C++中使用的地址,绝对不是物理地址
如果是物理地址,这种现象不可能产生
每一个进程在启动的时候,都会让操作系统给他创建一个地址空间,该地址空间就是进程地址空间
每一个进程 都会有一个自已的进程地址空间! !
操作系统要不要管理这些进程地址空间呢??
先描述,在组织
进程地址空间,其实是内核的一个数据结构,struct mm_ struct (稍后看! )
举例子:
①我们往银行存10亿,但是银行可能没10亿,但银行给你画的饼就是10个亿。
②美国一个富翁有3个私生子,富翁给每个私生子画的大饼:说自己有10个亿以后都是你的财产,富翁: OS
三个私生子:进程
富翁给三个私生子画的大饼:进程地址空间
让每一个进程都认为自己是独占系统中的所有资源的! !
所谓的进程/虚拟地址空间:其实就是OS通过软件的方式,给进程提供一个软件视角,认为自己会独占系统的所有资源(内存),每个进程会维护一个mm_struct,虚拟地址和物理内存通过页表建立映射关系,(上学时,一个班级中,老师点名需要一张名单,这个名单就是虚拟地址空间)
linux下:逻辑地址=虚拟地址=线性地址


每个区域范围,都是可以有对应的编号的,在虚拟地址空间 mm_struct 中有存储各个数据区的起始地址和结束地址,也就是区域

写时拷贝正好可以回答fork 现象:三.2 子进程修改的全局变量后,父子进程读取到的内容却不一样
(下面这两张图是一样的)


因为进程具有独立性! !做到互不影响。当子进程修改g_val时,为了做到子进程不影响父进程,会发生写时拷贝:g_val会再拷贝一份,子进程中的映射关系会改变,指向新的g_val,但是g_val的虚拟地址(相对地址)还是原来的地址,和父进程的g_val虚拟地址(相对地址)一样,但是他们的物理地址不一样,100改成200时,只会改变新的g_val(写时拷贝本身就是有OS的内存管理模块完成的!所以我们感知不到)
答:1.父进程的数据,子进程不一定全用,即便使用,也不一定全部写入——会有浪费空间的嫌疑
2. 最理想的情况,只有会被父子修改的数据,进行分离拷贝。不需要修改的共享即可——但是从技术角度实现复杂,不可能实现
3.如果fork的时候,就无脑拷贝数据给子进程,会增加fork的成本(内存和时间)
所以最终采用写时拷贝:
①写时拷贝只会拷父子修改的,变相的,就是拷贝数据的最小成本
②拷贝的成本依旧存在
写时拷贝本质是延迟拷贝策略!只有真正使用的时候,才给你!
你想要,但是不立马使用的空间,先不给你,那么也就意味着可以先给别人!
变相的提高内存的使用率!
pid_ t id是属于父进程栈空间中定义的变量,fork内部,return 会被执行两次,return 的本质,就是通过寄存器将返回值写入到接受返回值的变量中! !
当id=fork()的时候,谁先返回,谁就要发生写时拷贝,所以,同一个变量,会有不同的内容值,本质是因为大家的虚拟地址是一样的,但是大家对应的物理地址是不一样的! !
访问内存添加了一层软硬件层,可以对转化过程进行审核,非法的访问,就可以直接拦截了
1.保护内存!
2.进程管理-Linux内存管理
通过地址空间,进行功能模块的解耦! !
3.让进程或者程序可以以一种统一的视角看待内存!
方便以统一的方式来编译和加载所有的可执行程序
简化进程本身的设计与实现!