进程概念(二)
1. 进程状态
进程运行时,进程会被CPU调度,但是我运行了很多个进程,凭什么你这个进程就被先运行,这里就是被操作系统管理,这里就要说说进程状态,实际上这里的凭什么就因为进程有状态。
1.1 阻塞和挂起状态
那么,这里我们就要提起两个概念(阻塞/挂起):
阻塞:进程因为等待某种条件就绪,而导致的一种不推进的状态。什么意思呢?首先进程=内核关于进程的数据结构+当前进程的代码和数据,首先数据都是经过CPU调度来运算的,那么进程也是,假如进程已经创建和运行起来了,但是没有被CPU调度,那么这里就是阻塞。再设想,我们在Windows下启动了太多的软件,会出现卡的请况,实际上这里就是启动了太多的进程,CPU调度不过来,当前正在调度的在运行,没有被调度的就是没运行,所谓的阻塞就是卡住了,这里的卡住了就是在等待某种软硬件资源,这里的资源可以指网卡、显卡、磁盘等。**那么进程为什么阻塞呢?进程要通过等待的方式,等具体的资源被别的进程用完后,再被自己使用。那么阻塞就是进程等待某种资源就绪的过程。**从数据结构来理解阻塞:首先进程在被操作系统管理的时候会被描述成一个结构体,硬件在被操作系统管理的时候同样是一个结构体,当我们在官网上下载软件的时候,突然断网了,这里这个进程就不会再被CPU调度,那么怎么解释呢?实际上当我们正在官网上下载的时候这个结构体是指向的CPU(抽象理解),突然断网了就要等待网卡资源,实际上这个进程的结构体就指向了网卡这个结构体,排在网卡这个结构体的后面。
再说一个例子,我们在写C语言的 scanf 时,当我们运行起来,光标会在控制台上一直闪烁,实际上这个进程就是阻塞的,它是在等待键盘输入,也就是在等待键盘的资源。
挂起:首先进程是内核相关进程的数据结构+代码和数据,当我们把进程运行起来,那么就是加载到内存中,那么当我们在进程被CPU调度的时候(也就是在官网上下载的时候),突然断网了,就会阻塞,这里进程暂时不会被运行,那么资源放在内存中就会被浪费,这里存放在内存中的代码和数据就会被释放掉这部分空间,然后把代码和数据暂时存放在磁盘中,需要用的时候就会从磁盘中拿取。那么,挂起状态就是操作系统把数据和代码管理放在磁盘中
1.2 进程状态
上面我们谈及到了阻塞和挂起状态,这里面呢我们谈及到了数据结构来理解进程阻塞和挂起,那么进程是什么状态一般是看进程在那个队列排队,也就是在那个硬件资源排队。
kernel源代码定义:
/* * The task state array is a strange "bitmap" of * reasons to sleep. Thus "running" is zero, and * you can test for combinations of others with * simple bit tests. */ static const char * const task_state_array[] = { "R (running)", /* 0 */ "S (sleeping)", /* 1 */ "D (disk sleep)", /* 2 */ "T (stopped)", /* 4 */ "t (tracing stop)", /* 8 */ "X (dead)", /* 16 */ "Z (zombie)", /* 32 */ };
R运行状态(running): 并不意味着进程一定在运行中,它表明进程要么是在运行中要么在运行队列里。
S睡眠状态(sleeping): 意味着进程在等待事件完成(这里的睡眠有时候也叫做可中断睡眠(interruptible sleep))。
D磁盘休眠状态(Disk sleep)有时候也叫不可中断睡眠状态(uninterruptible sleep),在这个状态的进程通常会等待IO的结束。
T停止状态(stopped): 可以通过发送 SIGSTOP 信号给进程来停止(T)进程。这个被暂停的进程可以通过发送 SIGCONT 信号让进程继续运行。
X死亡状态(dead):这个状态只是一个返回状态,你不会在任务列表里看到这个状态。
1.2.1 进程查看+S状态+R状态
ps axj | head -n1 && ps axj | grep mytest | grep -v grep
上面当我们执行这个test.c程序时,加载到内存中就相当于一个进程了,我们是正在运行吗?这里的状态显示的是S+,也就是休眠状态吗,我们不是已经运行起来了吗,为什么这个进程是休眠状态呢?再来看一个样例:
上面再执行test.c程序时,这里的进程状态显示的时R+,也就是运行状态,我们所观察到的代码不都差不多吗,为什么上面那个程序中while循环中有printf库函数,下面这个进程中没有所显示的状态不一洋呢?这是为什么呢?printf库函数本质就是向外设打印信息,当CPU执行printf这库函数代码的时候,这里就要访问外设,但是这里如果时频繁打印的时候,CPU会很快速的去运行,外设和CPU的速度差太大,导致外设就不一定是就绪的,那么这里就会产生阻塞,这个进程就会被放在对应的外设的结构体队列中。实际上是运行的状态,只不过CPU的速度太快了,执行这一行代码的速度非常快,但是外设没有那么快,导致CPU请求外设资源很慢,这里CPU是不会等外设资源到来再去进行下一个任务,这里就会让这个进程放在外设队列中等待资源到来,所以有很多时间这个进程都是在等待,导致效果上是休眠状态。下面这没有printf库函数的代码是R+状态,是因为没有访问外设资源的动作,不会有阻塞的发生。R状态直接代表进程在运行,实际上是指进程在运行队列中就算做R状态。
1.2.2 D状态
故事:首先OS是管理者身份,进程是被管理者,磁盘是一个进程的小弟。当我们电脑中有很多进程的时候,不防止出现了很多进程都没有运行,也就是下属很多都没有干活,没有干活的话此时就被挂起,一个进程对应的数据和代码都放在了磁盘中,此时OS管理者看到了很多进程下属没有干活就直接给它开除了,但是我们用数据的时候呢,此时数据找不到对用的进程下属了,此时OS管理者和进程和磁盘就开始扯皮,最终要怪的还是OS的设计者,此时设计者就想到了一个处理办法,就是让这里的进程不可中断,就是这里的进程下属很重要,不能被开除,所以就出现了D状态,那么此时操作系统都无法删除这个进程,只有当这个进程自己进入CPU中运行的时候才会中断。系统不能出现D状态,出现D状态说明磁盘被占用空间很大,此时这个进程不能删除,一直等到数据被CPU处理完后这个进程才可中断。
1.2.3 T状态
查看kill掉进程的操作:
运行起来程序后使用kill -19 PID来kill掉程序:
此时我们发现当我们kill掉进程后,状态就变成了T状态。也可以让当前就前进程继续运行,这里使用到kill -18 PID.
当我们在运行起来进程时,我们发现状态变成了S状态,之前没有kill掉程序时还是S+状态,同时我们ctrl+c杀掉程序是不可以的。
为什么这里S状态少了个+字符,原因:首先要清楚不带+字符表示是在后台运行,带上+字符表明是在前台运行的,所谓后台运行就好比是又开了个进程,这个进程一直在运行,但是你kill不掉;前台的程序可以直接用ctrl+c终止掉程序。那么怎么来解决问题呢?使用kill -9 PID来kill掉后台或者前台的程序。
1.2.4 t状态
实例:就是当我们打断点后执行到断点处时就会有这个追踪停止状态:
1.2.5 Z状态(僵尸状态)
那么为什么需要创建进程?因为我们需要创建进程来做完相对应的任务,那么这里我们就有两种情况:关心结果和不关心结果。
例子:我们在写C或者C++代码的时候main(){return 0;}这里的return 0是什么呢?其实就是进程的退出码。
这么查看进程的退出码呢?echo $?
如果一个进程退出了,立马X状态了,bash作为父进程,有没有机会拿到退出结果呢?Linux当进程退出的时候,进程不会立即彻底退出,而是维持一个Z状态,目的时方便父进程读取子进程的退出结果!
那么如何看到Z状态呢?子进程退出,但是不要回收子进程
Z状态的危害:进程的退出状态必须被维持下去,因为他要告诉关心它的进程(父进程),你交给我的任务,我办的怎么样了。可父进程如果一直不读取,那子进程就一直处于Z状态?是的!维护退出状态本身就是要用数据维护,也属于进程基本信息,所以保存在task_struct(PCB)中,换句话说,Z状态一直不退出,PCB一直都要维护?是的!那一个父进程创建了很多子进程,就是不回收,是不是就会造成内存资源的浪费?是的!因为数据结构对象本身就要占用内存,想想C中定义一个结构体变量(对象),是要在内存的某个位置进行开辟空间!内存泄漏?是的!如何避免请看后续!
1.3 孤儿进程
僵尸进程是父子进程同时运行时,当我们子进程结束后,此时的子进程并不是直接结束,而是处于僵尸状态,方便父进程来读取子进程的结果!那么,当我们父子进程同时运行时,假设父进程先结束了,子进程是个什么状态呢?
这里Makefile文件中的@ 表示的是目标文件, @表示的是目标文件,@表示的是目标文件,^表示的是依赖文件列表
上面现象中,我们观察到当我们的父子进程同时运行的时候,父进程先结束了,子进程就在后台运行,父进程是直接结束了,不是说当进程结束后是维持在Z状态吗,那么这里的父进程为什么是直接结束了?原因是这里的父进程的父进程是bash,而当这个父进程结束后,bash会自己回收掉这个父进程,所以这个父进程就直接结束了。这里的子进程为什么父进程变成了1?父进程退出后,子进程要被PPID为1的这个父进程领养,这个PPID为1的父进程就是操作系统。结论:父进程退出,子进程会被OS自动领养(PPID为1的进程就是OS),那么这个被领养的进程就是孤儿进程
为什么子进程会被OS领养?原因就是:如果不领养,子进程后续再退出就没有被回收了,会造成内存泄漏。
这里kill掉后台进程的操作:kill -9 PID 或者 killall 进程名
2. 环境变量
2.1 背景
我们所常见的学java的时候我们需要配置环境变量,这里的环境变量就是这里的所谓的环境变量。
**概念:环境变量一般是指在OS中用来指定OS运行环境的一些参数。**例子:我们在编写C/C++程序的时候,在链接的时候,从来不知道我们的所链接的动静态库在哪里,但是照样可以链接成功,生成可执行程序,原因就是相关环境变量帮助编译器进行查找。此外,环境变量在系统中通常具有全局性!
那么这里有一个问题来进行切入:我们在Linux上写的代码,编译之后,运行的时候为什么要带上./呢?
这里的可执行程序和系统的ls、pwd等等指令都是可执行程序,那么自己写的可执行程序和系统自带的可执行程序有区别吗?没有区别
那么都是可执行程序,那么这里就是上面的问题,为什么自己写的可执行程序需要带上./运行,而系统的指令的可执行程序带上./运行不行呢?
原因就在于这里的通过该环境变量在系统当中在特定的条件下没有找到ls指令。执行一条命令的前提一定是找到它。所以系统存在环境变量用来存放对应的路径的、用户、登录用户等等。
查看环境变量:
echo $PATH
在我的Linux distribution下运行的结果是:/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/jyh/.local/bin:/home/jyh/bin
怎么证明,系统指令ls、pwd等指令是存在变量中呢?
[jyh@VM-12-12-centos study5]$ echo $PATH /usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/jyh/.local/bin:/home/jyh/bin [jyh@VM-12-12-centos study5]$ which ls alias ls='ls --color=auto' /usr/bin/ls [jyh@VM-12-12-centos study5]$
那么怎么让自己的可执行程序在运行前不同加上./运行呢?
[jyh@VM-12-12-centos study5]$ ll total 20 -rw-rw-r-- 1 jyh jyh 75 Feb 20 22:58 Makefile -rwxrwxr-x 1 jyh jyh 8408 Feb 21 20:47 myproc -rw-rw-r-- 1 jyh jyh 554 Feb 20 23:37 myproc.c [jyh@VM-12-12-centos study5]$ pwd /home/jyh/linux_-stu/study5 [jyh@VM-12-12-centos study5]$ export PATH=/home/jyh/linux_-stu/study5 [jyh@VM-12-12-centos study5]$ echo $PATH /home/jyh/linux_-stu/study5 [jyh@VM-12-12-centos study5]$ myproc hello 3 hello 2 hello 1 [jyh@VM-12-12-centos study5]$ cat myproc -bash: cat: command not found [jyh@VM-12-12-centos study5]$ cat myproc.c -bash: cat: command not found [jyh@VM-12-12-centos study5]$
这里有个问题,我把我们的可执行程序路径放进了环境变量,此时可执行程序可以不带./执行,但是系统指令找不到了,这时不要慌,可以把我们的图形化界面程序关掉重新打开就会恢复PATH。那么这里怎么解决既可以不带./执行自己写的可执行程序又能执行系统自带指令程序呢?
[jyh@VM-12-12-centos study5]$ ll total 20 -rw-rw-r-- 1 jyh jyh 75 Feb 20 22:58 Makefile -rwxrwxr-x 1 jyh jyh 8408 Feb 21 20:47 myproc -rw-rw-r-- 1 jyh jyh 554 Feb 20 23:37 myproc.c [jyh@VM-12-12-centos study5]$ pwd /home/jyh/linux_-stu/study5 [jyh@VM-12-12-centos study5]$ export PATH=$PATH:/home/jyh/linux_-stu/study5 //: 表示追加 [jyh@VM-12-12-centos study5]$ echo $PATH /usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/jyh/.local/bin:/home/jyh/bin:/home/jyh/linux_-stu/study5 [jyh@VM-12-12-centos study5]$ myproc hello 3 hello 2 hello 1 [jyh@VM-12-12-centos study5]$ ./myproc hello 3 hello 2 hello 1 [jyh@VM-12-12-centos study5]$ cat myproc.c #include <stdio.h> #include <unistd.h> int main() { int count = 3; while(count){ printf("hello %d\n", count--); sleep(1); } } [jyh@VM-12-12-centos study5]$
另外一种方法就是把对应的路径直接拷贝到/usr/bin目录下。
所以在Linux中,把可执行程序拷贝到系统默认路径下让我们可以直接访问的方式就相当于Linux下软件的安装!
针对什么环境变量,我们再来谈一谈权限管理>我们奇怪的是这个文件的拥有者是一个私人用户,默认情况下,其他人是无法进程写入的。那么这一现象用环境变量来解释就是:正因为有环境变量的存在,当我们登陆的时候,系统就已经知道了我是谁,通过这里的我是谁以及其他权限配置来禁止其他用户写入东西到我们的文件中。查找自己的身份时可以使用指令:echo $USER
2.2 认识环境变量
查看本用户对应的环境变量配置:env ;以及查看本地变量和环境变量的指令:set(用的并不是很多);取消本地变量:unset + 本地变量名
[jyh@VM-12-12-centos study5]$ env XDG_SESSION_ID=12568 HOSTNAME=VM-12-12-centos //(机器名) TERM=xterm SHELL=/bin/bash //(shell) HISTSIZE=3000 //(历史命令数量最大值) SSH_CLIENT=171.43.199.212 2950 22 OLDPWD=/home/jyh SSH_TTY=/dev/pts/0 USER=jyh //(用户) LD_LIBRARY_PATH=:/home/jyh/.VimForCpp/vim/bundle/YCM.so/el7.x86_64 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/jyh PATH=/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/jyh/.local/bin:/home/jyh/bin (可执行程序搜索路径) PWD=/home/jyh/linux_-stu/study5 //(当前文件路径) LANG=en_US.utf8 SHLVL=1 HOME=/home/jyh //(家目录) LOGNAME=jyh //(登录用户) SSH_CONNECTION=171.43.199.212 2950 10.0.12.12 22 LESSOPEN=||/usr/bin/lesspipe.sh %s PROMPT_COMMAND=history -a; history -a; printf "\033]0;%s@%s:%s\007" "${USER}" "${HOSTNAME%%.*}" "${PWD/#$HOME/~}" XDG_RUNTIME_DIR=/run/user/1002 HISTTIMEFORMAT=%F %T _=/usr/bin/env [jyh@VM-12-12-centos study5]$
2.3 获取环境变量
铺垫:main函数最多可以带几个参数呢?int argc、char* argv[]、char* envp[],其中最后一个envp是环境变量表。
其实这里环境变量表就是一个指针数组,这个数组中的每一个元素都存放的是字符串,以’\0’为结束标志的字符串,这个数组中最后的一个无效元素肯定是NULL,用来记录大小。
这个环境变量具体是存的什么呢?那么这里去访问这个环境变量表:
[jyh@VM-12-12-centos study5]$ ll total 24 -rw-rw-r-- 1 jyh jyh 161 Feb 21 21:39 env.c -rw-rw-r-- 1 jyh jyh 150 Feb 21 21:40 Makefile -rwxrwxr-x 1 jyh jyh 8360 Feb 21 21:40 myenv -rw-rw-r-- 1 jyh jyh 568 Feb 21 21:29 myproc.c [jyh@VM-12-12-centos study5]$ cat env.c #include <stdio.h> int main(int argc, char* argv[], char* envp[]) { for(int i = 0; envp[i]; ++i){ printf("envp[%d]->%s\n", i, envp[i]); } return 0; } [jyh@VM-12-12-centos study5]$ ./myenv envp[0]->XDG_SESSION_ID=12568 envp[1]->HOSTNAME=VM-12-12-centos envp[2]->TERM=xterm envp[3]->SHELL=/bin/bash envp[4]->HISTSIZE=3000 envp[5]->SSH_CLIENT=171.43.199.212 2950 22 envp[6]->OLDPWD=/home/jyh envp[7]->SSH_TTY=/dev/pts/0 envp[8]->USER=jyh envp[9]->LD_LIBRARY_PATH=:/home/jyh/.VimForCpp/vim/bundle/YCM.so/el7.x86_64 envp[10]->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: envp[11]->MAIL=/var/spool/mail/jyh envp[12]->PATH=/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/jyh/.local/bin:/home/jyh/bin:/home/jyh/linux_-stu/study5 envp[13]->PWD=/home/jyh/linux_-stu/study5 envp[14]->LANG=en_US.utf8 envp[15]->SHLVL=1 envp[16]->HOME=/home/jyh envp[17]->LOGNAME=jyh envp[18]->SSH_CONNECTION=171.43.199.212 2950 10.0.12.12 22 envp[19]->LESSOPEN=||/usr/bin/lesspipe.sh %s envp[20]->PROMPT_COMMAND=history -a; history -a; printf "\033]0;%s@%s:%s\007" "${USER}" "${HOSTNAME%%.*}" "${PWD/#$HOME/~}" envp[21]->XDG_RUNTIME_DIR=/run/user/1002 envp[22]->HISTTIMEFORMAT=%F %T envp[23]->_=./myenv [jyh@VM-12-12-centos study5]$
我们所看到的就差不多是我们直接执行系统指令env看到的结果,这里的指针数组的每个元素所对应的就是一个个字符串。
那么我们不要main函数参数可以访问环境变量吗?可以
这里我们需要使用到extern char** environ这个二级字符指针,它是指向环境字符串数组的,也就是指向envp字符串指针数组的。操作如下:
[jyh@VM-12-12-centos study5]$ ls env.c Makefile myenv myproc.c [jyh@VM-12-12-centos study5]$ cat env.c #include <stdio.h> #include <unistd.h> extern char** environ; int main() { for(int i = 0; environ[i]; ++i){ printf("environ[%d]->%s\n",i ,environ[i]); } return 0; } [jyh@VM-12-12-centos study5]$ make make: `myenv' is up to date. [jyh@VM-12-12-centos study5]$ ./myenv environ[0]->XDG_SESSION_ID=12568 environ[1]->HOSTNAME=VM-12-12-centos environ[2]->TERM=xterm environ[3]->SHELL=/bin/bash environ[4]->HISTSIZE=3000 environ[5]->SSH_CLIENT=171.43.199.212 2950 22 environ[6]->OLDPWD=/home/jyh environ[7]->SSH_TTY=/dev/pts/0 environ[8]->USER=jyh environ[9]->LD_LIBRARY_PATH=:/home/jyh/.VimForCpp/vim/bundle/YCM.so/el7.x86_64 environ[10]->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: environ[11]->MAIL=/var/spool/mail/jyh environ[12]->PATH=/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/jyh/.local/bin:/home/jyh/bin:/home/jyh/linux_-stu/study5 environ[13]->PWD=/home/jyh/linux_-stu/study5 environ[14]->LANG=en_US.utf8 environ[15]->SHLVL=1 environ[16]->HOME=/home/jyh environ[17]->LOGNAME=jyh environ[18]->SSH_CONNECTION=171.43.199.212 2950 10.0.12.12 22 environ[19]->LESSOPEN=||/usr/bin/lesspipe.sh %s environ[20]->PROMPT_COMMAND=history -a; history -a; printf "\033]0;%s@%s:%s\007" "${USER}" "${HOSTNAME%%.*}" "${PWD/#$HOME/~}" environ[21]->XDG_RUNTIME_DIR=/run/user/1002 environ[22]->HISTTIMEFORMAT=%F %T environ[23]->_=./myenv [jyh@VM-12-12-centos study5]$
我们这里可以来直接遍历得到需要的环境变量,但是这样就很麻烦,所以系统提供了一个接口:char* getenv(char* name);获取成功就返回环境变量内容,失败就返回NULL,操作如下:
[jyh@VM-12-12-centos study5]$ ll total 24 -rw-rw-r-- 1 jyh jyh 552 Feb 21 22:09 env.c -rw-rw-r-- 1 jyh jyh 151 Feb 21 22:03 Makefile -rwxrwxr-x 1 jyh jyh 8512 Feb 21 22:09 myenv -rw-rw-r-- 1 jyh jyh 568 Feb 21 21:29 myproc.c [jyh@VM-12-12-centos study5]$ ./myenv 该环境变量的内容是:jyh [jyh@VM-12-12-centos study5]$ cat env.c #include <stdio.h> #include <stdlib.h> int main() { char* user = getenv("USER"); if(user == NULL){ perror("没有此环境变量!\n"); exit(-1); } else{ printf("该环境变量的内容是:%s\n", user); } return 0; } [jyh@VM-12-12-centos study5]$
当然这就是ls、pwd指令怎么执行后的结果是我们所看到的结果的原因,原因就是从环境变量中直接过间接的获取。
2.4 环境变量是什么
环境变量本质就是字符串,这里的多个环境变量就形成了一张内存级的环境变量表(字符串指针数组),这张表由用户登录系统的时候就会形成给用户,环境变量中的每一个环境变量都有自己的用途,例如:可执行程序搜索查找、身份认证等等。
**那么环境变量是从哪里来的呢?环境变量都是从系统的相关配置文件中读取的!**这里的文件就比如:etc/bashrc/
这里怎么来理解环境变量表是内存级的呢?
[jyh@VM-12-12-centos study5]$ myval=100 [jyh@VM-12-12-centos study5]$ echo $myval 100 [jyh@VM-12-12-centos study5]$
这里我们可以在命令行上来定义一个变量myval,我们说shell实际上是一个进程,它是用来读取命令和命令行的,其实当shell启动时,就会去维护一个环境变量表,然后这个变量就是在shell内部定义的,当我们执行命令的时候,这里就是创建了一个子进程,然后shell进程会把环境变量表再拷贝给子进程。所以,这个环境变量是在shell中被维护的!怎么证明呢?
[jyh@VM-12-12-centos study5]$ export mypassword='12345678' [jyh@VM-12-12-centos study5]$ env XDG_SESSION_ID=13984 HOSTNAME=VM-12-12-centos TERM=xterm SHELL=/bin/bash HISTSIZE=3000 SSH_CLIENT=171.43.199.212 1479 22 OLDPWD=/home/jyh/linux_-stu SSH_TTY=/dev/pts/0 USER=jyh LD_LIBRARY_PATH=:/home/jyh/.VimForCpp/vim/bundle/YCM.so/el7.x86_64 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: mypassword=12345678 MAIL=/var/spool/mail/jyh PATH=/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/jyh/.local/bin:/home/jyh/bin PWD=/home/jyh/linux_-stu/study5 LANG=en_US.utf8 SHLVL=1 HOME=/home/jyh LOGNAME=jyh SSH_CONNECTION=171.43.199.212 1479 10.0.12.12 22 LESSOPEN=||/usr/bin/lesspipe.sh %s PROMPT_COMMAND=history -a; history -a; printf "\033]0;%s@%s:%s\007" "${USER}" "${HOSTNAME%%.*}" "${PWD/#$HOME/~}" XDG_RUNTIME_DIR=/run/user/1002 HISTTIMEFORMAT=%F %T _=/usr/bin/env [jyh@VM-12-12-centos study5]$
上面我们观察到,当我们输入指令export mypassword='12345678’后,这个变量就被导入到了环境变量中,说明了这个环境变量表是被shell来维护的!那么怎么证明创建新的子进程后shell进程会拷贝环境变量表到子进程呢?
[jyh@VM-12-12-centos study5]$ ls env.c Makefile myproc.c test.c [jyh@VM-12-12-centos study5]$ make gcc -o mytest test.c [jyh@VM-12-12-centos study5]$ ls env.c Makefile myproc.c mytest test.c [jyh@VM-12-12-centos study5]$ ./mytest 12345678 [jyh@VM-12-12-centos study5]$ env XDG_SESSION_ID=13984 HOSTNAME=VM-12-12-centos TERM=xterm SHELL=/bin/bash HISTSIZE=3000 SSH_CLIENT=171.43.199.212 1479 22 OLDPWD=/home/jyh/linux_-stu/study5/test.c SSH_TTY=/dev/pts/0 USER=jyh LD_LIBRARY_PATH=:/home/jyh/.VimForCpp/vim/bundle/YCM.so/el7.x86_64 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: mypassword=12345678 MAIL=/var/spool/mail/jyh PATH=/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/jyh/.local/bin:/home/jyh/bin PWD=/home/jyh/linux_-stu/study5 LANG=en_US.utf8 SHLVL=1 HOME=/home/jyh LOGNAME=jyh SSH_CONNECTION=171.43.199.212 1479 10.0.12.12 22 LESSOPEN=||/usr/bin/lesspipe.sh %s PROMPT_COMMAND=history -a; history -a; printf "\033]0;%s@%s:%s\007" "${USER}" "${HOSTNAME%%.*}" "${PWD/#$HOME/~}" XDG_RUNTIME_DIR=/run/user/1002 HISTTIMEFORMAT=%F %T _=/usr/bin/env [jyh@VM-12-12-centos study5]$
所以,环境变量是可以被相关的子进程继承下去的,环境变量具有全局属性!
上述中我们导入环境变量表中要使用到export指令,但是我们不使用export指令会出现什么现象呢?
[jyh@VM-12-12-centos study5]$ myenviron=12345 [jyh@VM-12-12-centos study5]$ echo $myenviron 12345 [jyh@VM-12-12-centos study5]$ vim test.c [jyh@VM-12-12-centos study5]$ cat test.c #include <stdio.h> #include <stdlib.h> int main() { printf("%s\n", getenv("myenviron")); return 0; } [jyh@VM-12-12-centos study5]$ make gcc -o mytest test.c [jyh@VM-12-12-centos study5]$ ls env.c Makefile myproc.c mytest test.c [jyh@VM-12-12-centos study5]$ ./mytest Segmentation fault [jyh@VM-12-12-centos study5]$ env XDG_SESSION_ID=13984 HOSTNAME=VM-12-12-centos TERM=xterm SHELL=/bin/bash HISTSIZE=3000 SSH_CLIENT=171.43.199.212 1479 22 OLDPWD=/home/jyh/linux_-stu/study5/test.c SSH_TTY=/dev/pts/0 USER=jyh LD_LIBRARY_PATH=:/home/jyh/.VimForCpp/vim/bundle/YCM.so/el7.x86_64 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: mypassword=12345678 MAIL=/var/spool/mail/jyh PATH=/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/jyh/.local/bin:/home/jyh/bin PWD=/home/jyh/linux_-stu/study5 LANG=en_US.utf8 SHLVL=1 HOME=/home/jyh LOGNAME=jyh SSH_CONNECTION=171.43.199.212 1479 10.0.12.12 22 LESSOPEN=||/usr/bin/lesspipe.sh %s PROMPT_COMMAND=history -a; history -a; printf "\033]0;%s@%s:%s\007" "${USER}" "${HOSTNAME%%.*}" "${PWD/#$HOME/~}" XDG_RUNTIME_DIR=/run/user/1002 HISTTIMEFORMAT=%F %T _=/usr/bin/env [jyh@VM-12-12-centos study5]$
上述中现象告诉我们如果我们不加export来定义变量,它就不是导入进环境变量,这种变量叫做本地变量,只能在shell内部有效,如果想再导入到环境变量表中让子进程继承,那么直接可以使用指令:export 变量名。所以export的作用是把本地变量导入到环境变量表中!
题外话:再来说一个命令:. 。这个命令其实就是一个点,这个点命令其实就等于source,用法就是source 配置文件名,作用就是让配置文件立马生效。
2.5 认识命令行参数argc和argv
上面我们在2.3获取环境变量中说到了main函数的三个参数,其中我们只是解决了一个envp这个参数是什么的问题,下面我们再来谈一谈argc和argv是什么。
同样的argv也是一个字符串指针数组,每个元素对应的一个字符串,同样字符串也是以’\0’为结束标志的,也是以NULL为标识结束数组的。这里的argc指的是argv指针数组中的元素个数。
那么我们打印来看看argv中到底有什么?
[jyh@VM-12-12-centos study5]$ ls env.c Makefile myproc.c test.c [jyh@VM-12-12-centos study5]$ cat test.c #include <stdio.h> #include <stdlib.h> int main(int argc, char* argv[]) { for(int i = 0; argv[i]; ++i){ printf("argv[%d]->%s\n", i, argv[i]); } return 0; } [jyh@VM-12-12-centos study5]$ make gcc -o mytest test.c -std=c99 [jyh@VM-12-12-centos study5]$ ls env.c Makefile myproc.c mytest test.c [jyh@VM-12-12-centos study5]$ ./mytest argv[0]->./mytest [jyh@VM-12-12-centos study5]$
通过上述的现象,我们观察到的是argv这个字符串数组中就只有./mytest这个可执行程序。但是我们可以再其可执行程序后加上参数,对应的效果就是这样的:
[jyh@VM-12-12-centos study5]$ ./mytest -a -b -c argv[0]->./mytest argv[1]->-a argv[2]->-b argv[3]->-c [jyh@VM-12-12-centos study5]$
那么这里的参数列表也就随后保存在了argv这个字符串指针数组中了,这样我们就可以理解为什么ls指令后可以跟上参数列表了,比如:ls -a -l,这里的ls就是一个可执行程序,后面就是参数列表。
那么这里的参数列表怎么来用呢?
[jyh@VM-12-12-centos study5]$ ls env.c Makefile myproc.c test.c [jyh@VM-12-12-centos study5]$ mae -bash: mae: command not found [jyh@VM-12-12-centos study5]$ make gcc -o mytest test.c -std=c99 [jyh@VM-12-12-centos study5]$ ls env.c Makefile myproc.c mytest test.c [jyh@VM-12-12-centos study5]$ cat test.c #include <string.h> #include <stdio.h> #include <stdlib.h> void argument(const char* name) { printf("argument:%s [-a|-b|-c]\n",name); exit(0); } int main(int argc, char* argv[]) { if(argc != 2) { argument(argv[0]); } if(strcmp(argv[1], "-a") == 0) { printf("print current dirrectory!\n"); } else if(strcmp(argv[1], "-b") == 0) { printf("print current dirname\n"); } else if(strcmp(argv[1], "-c") == 0) { printf("print pwd\n"); } return 0; } [jyh@VM-12-12-centos study5]$ ./mytest argument:./mytest [-a|-b|-c] [jyh@VM-12-12-centos study5]$ ./mytest -a print current dirrectory! [jyh@VM-12-12-centos study5]$ ./mytest -b print current dirname [jyh@VM-12-12-centos study5]$ ./mytest -c print pwd [jyh@VM-12-12-centos study5]$
我们发现,每个参数列表对应的就是不同的操作,还可以对参数列表个数进程设置等等操作。通过这样类比一下:ls -a -l等等指令!