linux 进程无缘无故退出_linux进程无缘无故消失 - CSDN
  • 最近发现每隔一段时间,某一台机器线上的服务总是挂掉,进程直接就消失了,别的机器上的服务都正常跑着,怎么单单某一台的服务的进程就没有了呢,奇怪啊,也没有什么征兆,最后发现:执行 last reboot 查看机器最近...

    最近发现每隔一段时间,某一台机器线上的服务总是挂掉,进程直接就消失了,别的机器上的服务都正常跑着,怎么单单某一台的服务的进程就没有了呢,奇怪啊,也没有什么征兆,最后发现:执行last reboot  查看机器最近都什么时间是否重启过,擦,谁知道为什么就重启了,监控也不完善,自食其力吧。写下本文其实就是想告诉大家有的时候不是你的服务的问题。当然一下提供几种最简便的查看上一次系统的启动时间或者系统到现在已经运行了多长时间:

    1.who命令:

    who -b     -> system boot  2016-11-21 02:09  系统上一次启动时间

    2.last reboot命令

    last reboot:
    reboot   system boot  2.6.32-431.29.2. Mon Nov 21 02:09 - 17:56 (2+15:47)   
    reboot   system boot  2.6.32-431.29.2. Mon Jul 25 11:02 - 17:56 (121+06:53) 
    reboot   system boot  2.6.32-431.29.2. Mon Jun 20 17:51 - 10:59 (34+17:08)  
    reboot   system boot  2.6.32-431.29.2. Mon Jun 20 16:46 - 17:49  (01:03)    
    reboot   system boot  2.6.32-431.29.2. Wed May 25 14:37 - 17:49 (26+03:11)  
    reboot   system boot  2.6.32-431.29.2. Wed May 25 14:24 - 14:37  (00:12)    
    reboot   system boot  2.6.32-431.29.2. Tue May 17 15:25 - 14:37 (7+23:11)   
    reboot   system boot  2.6.32-431.29.2. Wed May 11 14:37 - 15:22 (6+00:45)   
    reboot   system boot  2.6.32-431.29.2. Wed May 11 14:09 - 14:37  (00:27) 

    3.top命令:

    系统启动到现在运行了多长时间,来推断重启是在哪一天

    4.uptime命令:

    top是一样的

    5.w命令

    top也是一样的

    展开全文
  • 一个java -jar服务在被CI启动后,过一段时间,进程就被消失了,不见了。日志没有关于出错的相关信息。对日志中记录的最后一条请求,进行压力测试,但该进程却没有自己消失。个人觉得这个问题很有意思,但是我也明白...

    一个java -jar服务在被CI启动后,过一段时间,进程就被消失了,不见了。日志没有关于出错的相关信息。对日志中记录的最后一条请求,进行压力测试,但该进程却没有自己消失。个人觉得这个问题很有意思,但是我也明白,找到这其中的原因可能需要很长的时间。

    Update(2019-3-15 )

    最近公司的其他项目上,又遇到了一个进程老是无缘无故就挂的现象,按照之前的那种场景来排查,并没有发现有那种CI的出现。顿时又陷入了困境之中。不过我还是按部就班的做了三件事:

    • 用root权限启动改服务
    • 做好对jvm的监控
    • 用strace对进程做好监控
      顺便了解了一下strace的含义,发现其中的字段确实是有很大的意义。
      监控命令如下:
    nohup strace -T -tt -e trace=all -p \`pgrep -f algorithm-work-1.0.0.jar\ ` > trace.\`pgrep -f algorithm-work-1.0.0.jar\`.log &\
    

    监控日志如下:

    webapp@ecs-f1c4-0003:/opt/webapp/logs$ cat trace.26077.log 
    strace: Process 26077 attached
    11:22:43.117920 futex(0x7f631a5fb9d0, FUTEX_WAIT, 26078, NULL) = ? ERESTARTSYS (To be restarted if SA_RESTART is set) <1238.653416>
    11:43:21.776222 --- SIGTERM {si_signo=SIGTERM, si_code=SI_USER, si_pid=26158, si_uid=0} ---
    11:43:21.777278 futex(0x7f63199c0540, FUTEX_WAKE_PRIVATE, 1) = 1 <0.004217>
    11:43:21.786583 rt_sigreturn({mask=[]}) = 202 <0.005049>
    11:43:21.796220 futex(0x7f631a5fb9d0, FUTEX_WAIT, 26078, NULL <unfinished ...>
    11:43:23.605244 +++ exited with 143 +++
    

    从上面看来,uid=0说明操作者是root,但是kill -9不一定有,不过至少是可以看出来是什么时候被干掉的。

    转机
    发现是被别人用来挖矿了==
    在这里插入图片描述
    详细的情况与下面链接的描述一模一样。本来还有一个进程叫做crond64,在top中看到占用的CPU非常高,各个核的都占到了90%+。执行的挖矿脚本的目录在~/.ttp下,打包好了,准备研究研究。
    https://askubuntu.com/questions/1115770/crond64-tsm-virus-in-ubuntu

    压力测试

    首先想到的是:是不是某一个接口出现了问题,所以根据日志中所记录的最后一条请求,对其进行压力测试。脚本如下:

    #!/bin/bash
    # 获取工单详情
    for i in {1..100000}
    do
    	curl --header "token:71e8e4dd40dd65f645ceb214397f578e" --url "192.168.31.117:9997/workorder/mine/orders?id=79" &
    	echo ""
    done
    

    结果被认为遭到了ddos攻击,囧!
    在这里插入图片描述
    该服务进程扛过了这些请求,没有死亡。

    排查CI

    因为整个项目通过gitlab管理,而gitlab中有一项叫做CI,可以通过ci脚本来执行一些脚本达到发布、部署最新的服务到相应服务器上。这里面可能会存在问题,比如说,另外一个ci脚本在执行的时候,会把该服务的进程kill掉,只是会有这种可能,因为CI脚本大部分是通过copy的,但是可能性不高,因为所有的CI脚本都能够顺利执行,所以kill掉的肯定是自身服务的进程,不然CI脚本对应的服务可能起不来,但目前所有的CI脚本都能顺利执行完。但是还是去排查一下CI脚本,没毛病

    是谁kill掉了该进程?

    这个进程消失的原因,可以想到的情况为:jvm崩溃、被操作系统的oom_killer杀掉、被某个脚本杀掉?

    是否为操作系统所终结?

    由于outofmemory被kill掉的进程,会在/var/log下的某个文件中留下最终的遗迹。但是在整个/var/log下、都没有搜索kill的痕迹,如下:
    在这里插入图片描述
    在这里插入图片描述
    如果没有/var/log/messages这个文件,可以通过设置,将这个log文件开启。

    1. root身份打开 /etc/rsyslog.d/50-default.conf

    2. 把注释#去掉

    #*.=info;*.=notice;*.=warn;\
    #    auth,authpriv.none;\
    #    cron,daemon.none;\
    #    mail,news.none        -/var/log/messages
    
    1. 重启后ok
      sudo restart rsyslog

    并没有发现这个oom_killer的痕迹

    JVM自己崩溃?

    在该服务的启动参数中加入了对崩溃日志的指定:
    java -jar -Xms512m -Xmx512m -XX:MaxPermSize=126m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/opt/webapp/xxx-server/ -XX:ErrorFile=/opt/webapp/xxx-server/hs_err_pid_%p.log xxx-server.jar
    但是在该进程被终结后,并没有发现相应目录下的日志文件。所以这种情况下,同样也没有对其进行内存分析的先决条件、jdk自带的一系列工具并没有发挥出作用的余地。

    到底是谁杀掉了这个进程?

    使用nohup strace -T -tt -e trace=all -p 21715 > trace.log &监控该pid的情况,如果是被kill -9,会出现一个log,大致如下:
    在这里插入图片描述

    结果

    通过strace命令的跟踪、最终发现trace.log的内容如下所示:
    在这里插入图片描述
    出现这个的原因基本上是两个、一个是人为的kill -9、或者就是被系统kill -9。系统杀掉该进程的原因、被逐一排除,结果只剩下人为的因素。关于人为的因素、首先查看命令,并没有相关的kill记录。然后发现开发环境与测试环境的该进程基本上同时挂掉、并且都点了这两个环境的某个CI,然后这两个环境上的该进程都挂掉了,因此基本断定是CI操作杀掉了该进程。
    被杀掉的进程名字为:java -jar workorder-server.jar,通过CI发布&启动的进程名字为:java -jar order-server.jar。然后在点击order相关的CI时会执行如下操作:

    script:
      - mvn clean package -pl order-server -am -Dmaven.test.skip=true
      - scp order-server/target/order-server.jar ${user_dev}@${nemt_host_dev}:/opt/webapp/order-server/.
      - ssh ${user_dev}@${nemt_host_dev} 'kill -9 `pgrep -f order-server\.jar` ; echo 1'
      - ssh ${user_dev}@${nemt_host_dev} '. /etc/profile ; cd /opt/webapp/order-server/ ; nohup java  -jar order-server.jar >> /dev/null 2>&1 &'
    

    问题就在kill -9上面,pgrep查出来的进程号有两个,所以执行order相关的CI时,顺带也把workorder干掉了。
    在这里插入图片描述

    解决办法

    修改shell语句,让pgrep order-server时,只显示出order-server的进程号即可。

    展开全文
  • 进程概述 进程:就是进行中的程序 程序:存放指令的程序文件,存放在磁盘上,固定不变的,保存着指令的有序集合。 程序执行过程:将程序从硬盘导入到内存,内存上分为代码区、静态变量区、堆栈区等等 文本区:...

    进程概述

    进程:就是进行中的程序
    程序:存放指令的程序文件,存放在磁盘上,固定不变的,保存着指令的有序集合。
    程序执行过程:将程序从硬盘导入到内存,内存上分为代码区、静态变量区、堆栈区等等
    文本区:储存处理器执行的代码
    数据区:存储变量和动态分配的内存:全局变量,局部变量,静态变量,
    堆栈区:存储着活动进程调用的指令和本地变量,指针变量->栈区,动态分配的内存->堆区
    进程为程序执行提供各种各样的条件,程序需要各种各样操作,磁盘操作、网络操作等等。需要各种管理,这种动态的管理行为就可以通俗的称之为进程。所谓程序开始活动起来了。进程是linux系统调度的最小单位


    进程状态

    进程是程序的执行过程,根据他的生命周期可以分为3种状态。
    执行态:该进程正在运行,即进程正在使用CPU
    就绪态:进程已经具备执行的一切条件,正在等待分配CPU的处理
    等待态:进程不能使用CPU,若等待事件发生(等待的资源分配到),将其唤醒

    ps

    process:os的最小单元,地址空间为4g,其中1gos,另外3g给进程
    ps:查看活动的进程
    ps -aux:查看各个进程的状态,包括运行、就绪、等待等状态(S睡眠 T中断 R运行 Z僵尸)
    ps -aux | grep `aa`:查看指定(aa)进程
    ps -ef:查看所有进程的pidppid等信息
    ps -aux :查看cpu内存的使用状态


    进程标识

    进程不能用程序区分
    因为不同进程可能执行的是同一个程序
    所以,使用ID号来标识区分不同的进程
    OS会为每个进程分配一个唯一的整型ID,作为进程的标识号(pid
    进程0时调度进程,常被称为交换进程,他不执行任何程序,是内核的一部分,因此被称为系统进程。
    进程除了自身ID外,还有父进程ID,每个进程都会有一个父进程,操作系统不会无缘无故产生一个新进程,所有的进程的祖先进程是同一个进程,叫做init进程,进程号是1.init进程是内核自举后的第一个启动进程。
    init进程负责引导系统、启动守护(后台)进程并且运行必要的程序。他不是系统进程,但它以系统的超级用户特权运行。
    父进程是负责子进程空间的清理


    一些概念

    并发:宏观上看起来,同时发生,cpu轮转非常快,给人一种错觉,所有的事情在同时发生
    并行:同时执行,微观上真的同时执行,多个cpu同时执行不同的进程,多个进程真的在同时执行。
    同步:相同的步伐,进程间相互联系,或者共同完成某件事,需要相互协调
    异步:不同的步伐,进程间毫无关联


    进程的意义

    例子:c语言只能让一个任务接一个任务的执行,单片机普通情况也下一样,也是一个接一个的执行任务,除非中断发生,单片机会先中断当前任务的执行,去执行中断需要做的事情,中断执行完毕再回来执行刚才中断的任务。可以拿人来类比,当一个人炒菜的时候,快递打电话来了,让去取快递,不能让送快递的人一直等,只能先不炒菜,去取快递,取完快递再回来继续炒菜。这样实际把炒菜的任务就给耽误了。
    linux中,一个人在炒菜,快递打电话来了,让去取快递,他可以叫他的儿子去取快递,自己继续炒菜。从cpu角度想,由于cpu执行速度较快,看起来任务在同时进行(并发执行),这样所有的事情都不耽误。这就是进程的意义,在某个层面上也能说明父进程子进程的意义。
    再举一个例子:
    ATM机取款时,一方面播放提示音乐,一方面提示密码输入,不带系统的单片机处理起来就很恶心,不能先播音乐,在此期间让用户等待输入,或者播放音乐同时用中断监测用户输入,用户输入会造成音乐的停止。用户体验很差,反过来程序用进程来写效果就很好,子进程播放提示音乐,父进程检测用户输入,父子进程并发执行,可以产生同时运行的效果。提高了用户体验。
    说白了进程的意义就是:让任务并发执行


    获取进程ID

    特殊说明:现在说的进程仅仅是linux下的进程

    • 每个进程都有一个整型的IDPID
    • 每个进程都有一个父进程 PPID
      一个进程想控制其他进程,想与其他进程通信,需要ID
      获取自己的ID
    #include <sys/types.h>
    #include <unistd.h>
    pid_t getpid(void); 

    pid_t getpid(void);
    功能:获取自己的进程ID
    参数:
    返回值:本进程的ID


    获取自己的父进程的ID

    #include <sys/types.h>
    #include <unistd.h>
    pid_t getppid(void);

    pid_t getpid(void);
    功能:获取自己的父进程ID
    参数:
    返回值:本进程的父进程的ID


    创建一个进程

    #include <unistd.h>
           pid_t fork(void);

    功能:创建一个子进程
    参数:
    返回值:有两次,子进程返回0,父进程返回子进程的进程号。
    注意:子进程从fork函数之后执行,fork完全拷贝了父进程的地址空间给子进程,父子进程运行顺序不确定。


    虚拟创建进程

     #include <sys/types.h>
           #include <unistd.h>
    
           pid_t vfork(void);

    功能:创建一个子进程
    参数:
    返回值:有两次,子进程返回0,父进程返回子进程的进程号。
    注意:子进程从fork函数之后执行,子进程有自己的ID与父进程共享资源,子进程先执行,父进程后执行。价值并不大,不能实现任务并发执行


    错误打印函数

    #include <stdio.h>
           void perror(const char *s);

    功能:打印错误信息(某些函数返回负值,表明发生错误,但是不知道具体类型,使用这个函数可以获得具体错误类型)


    销毁进程

    进程的常见的终止方式有5种:
    主动:

    • main函数的自然返回,注意:return不是结束,只是函数结束,当它刚好结束的是main函数,此时导致进程结束。造成return结束进程的错觉。
    • 调用exit函数 ,标准函数
    • 调用_exit函数 ,系统调用函数
    • 调用abort函数,产生SIGABRT信号

    被动:

    • 接收到某个信号,如ctrl+cSIGINTctrl+\ SIGOUT
    • 通过kill 向进程发信号
      前四四种正常的终止,后两种非正常的终止,但无论哪种方式,进程终止都会执行相同的关闭打来的文件,释放占用的内存资源,后两种终止会导致程序有些代码不能正常执行,比如对象的析构、atexit函数的执行。
    • exit__exit函数最大的区别在于exit函数退出之前会检查文件的打开情况,把文件缓冲区的内容写回文件,而__exit直接退出,什么意思?比如打开文件向文件写入内容,如果在文件没有关闭,也没有调用同步到磁盘的函数,文件并没有同步到磁盘,只存在缓冲区内,这时调用exit,那么进程结束时,缓冲区的内容可以同步到文件中,内容已经存在在文件之中了,调用__exit进程直接结束,文件不会有写入的内容。

    启动新进程

    #include <stdlib.h>
           int system(const char *command);
    

    功能:打开命令或者程序
    参数:带路径的程序启动文件,或者在启动变量里声明的程序直接写程序名
    返回值:-1失败
    打开的程序是另一个进程,也可以成为此程序的子进程,因此子进程不一定和父进程视同一个程序,在成功打开所要执行的文件之后,父进程才能继续执行。
    父进程程序

    #include <stdlib.h>
    #include <stdio.h>
    int main(void)
    {
      printf("调用子进程\n");
        system("./son");
        printf("调用子进程结束\n");
        return 0;
    }
    

    子进程程序

    #include <stdio.h>
    #include <unistd.h>
    int main(void)
    {
       printf("这是子进程在执行\n");
        sleep(20);
    }

    程序运行效果

    [root@CentOS workdir]# gcc son.c -o son
    [root@CentOS workdir]# gcc sys.c -o app
    [root@CentOS workdir]# ./app
    调用子进程
    这是子进程在执行
    调用子进程结束
    

    进程调度结果,son父进程指向app

    root      39055  35664  0 19:28 pts/0    00:00:00 ./app
    root      39056  39055  0 19:28 pts/0    00:00:00 ./son

    进程替换,exec函数族

     #include <unistd.h>
    
           extern char **environ;
    
           int execl(const char *path, const char *arg, ...);
           int execlp(const char *file, const char *arg, ...);
           int execle(const char *path, const char *arg,
                      ..., char * const envp[]);
           int execv(const char *path, char *const argv[]);
           int execvp(const char *file, char *const argv[]);
           int execvpe(const char *file, char *const argv[],
                       char *const envp[]);
    

    重点学习四种

    int execl(const char *path, const char *arg, ...);
    int execlp(const char *file, const char *arg, ...);
    int execv(const char *path, char *const argv[]);
    int execvp(const char *file, char *const argv[]);

    这四个函数第一个参数都是可执行程序或者脚本的程序名,execlexecv需要带有完整的路径,第二参数为任意字符,起到占位作用,第三个或者后面的字符为调用者的参数,参数列表最后以NULL结尾,而execlpexecvp不需要只需要带有可执行程序的名称即可,系统自动去环境变量去寻找同名文件,execlexeclp需要NULL结尾.

    函数后缀说明:
    l v:参数呈现形式
    l:list 参数一个个的列出来
    vvector 参数用数组存储起来
    p:目标程序,可以省略路径
    e:环境变量,不考虑

    system程序稍作修改
    父进程程序

    #include <stdlib.h>
    #include <stdio.h>
    #include <unistd.h>
    int main(void)
    {
      printf("当前进程id:%d\n",getpid());
      printf("调用son进程\n");
      execl("./son",NULL);  
      printf("调用子进程结束\n");
        return 0;
    
    }
    

    子进程程序

    #include <stdio.h>
    #include <unistd.h>
    int main(void)
    {
       printf("这是子进程在执行\n");
       printf("son进程id:%d\n",getpid());
       sleep(5);
    }
    

    程序运行效果,不会打印调用子进程结束,父子进程ID号相同,表明当前进程被son进程替换

    [root@CentOS workdir]# gcc sys.c 
    [root@CentOS workdir]# gcc son.c -o son
    [root@CentOS workdir]# ./a.out 
    当前进程id:39606
    调用son进程
    这是子进程在执行
    son进程id:39606
    [root@CentOS workdir]# 

    注意:当时用参数list的函数时,最后一个参数赋值NULL,表明没有参数,否则运行会出错。


    wait进程同步

    #include <sys/types.h>
    #include <sys/wait.h>
    pid_t wait(int *status);
    pid_t waitpid(pid_t pid, int *status, int options);

    pid_t wait(int *status);
    函数返回值为结束子进程的进程号,当前进程中没有子进程则返回-1,参数为子进程结束状态指针,如果单纯想等待子进程的结束而不关心进程结束状态,参数写入NULL即可;若想获得子进程结束状态,将参数地址写入即可,例如定义int statue存储子进程的结束状态,函数调用wait(&statue)即可;
    一旦函数调用wait(),就会立即阻塞自己并自动判断当前进程中是否有某个子进程退出,wait()返回该子进程的状态,终止子进程的ID,所以它总能了解是哪一个子进程终止了。
    pid_t waitpid(pid_t pid, int *status, int options);
    pid:从参数的名字pid和类型pid_t中就可以看出,这里需要的是一个进程ID。但当pid取不同的值时,在这里有不同的意义。
    pid>0时,只等待进程ID等于pid的子进程,不管其它已经有多少子进程运行结束退出了,只要指定的子进程还没有结束,waitpid就会一直等下去。
    pid==-1时,等待任何一个子进程退出,没有任何限制,此时waitpidwait的作用一模一样。
    pid==0时,等待同一个进程组中的任何子进程,如果子进程已经加入了别的进程组,waitpid不会对它做任何理睬。
    pid<-1时,等待一个指定进程组中的任何子进程,这个进程组的ID等于pid的绝对值。
    第二个参数与wait相同,存储制定子进程终止的状态信息。为整形指针类型。
    options:options提供了一些额外的选项来控制waitpid,目前在Linux中只支持WNOHANGWUNTRACED两个选项,这是两个常数,可以用”|”运算符把它们连接起来使用。
    WNOHANG:pid指定进程并不是立即可用(终止状态),waitpid不阻塞,返回值为0.
    WUNTRACED:若实现支持作业控制··· ···涉及到一些跟踪调试方面的知识,加之极少用到,这里就不多说了。
    稍微琢磨一下就能看出,waitwaitpid有很大的关联性甚至wait就是封装了wairpid函数,没错!实际就是这样
    察看/include/unistd.h文件349-352行就会发现以下程序段:

        static inline pid_t wait(int * wait_stat)  
        {  
            return waitpid(-1,wait_stat,0);  
        }  

    返回值和错误
    waitpid的返回值比wait稍微复杂一些,一共有3种情况:

    • 当正常返回的时候,waitpid返回收集到的子进程的进程ID
    • 如果设置了选项WNOHANG,而调用中waitpid发现没有已退出的子进程可收集,则返回0
    • 如果调用中出错,则返回-1,这时errno会被设置成相应的值以指示错误所在;当pid所指示的子进程不存在,或此进程存在,但不是调用进程的子进程,waitpid就会出错返回,这时errno被设置为ECHILD
    展开全文
  • 布了几个项目。...需要用ps aux|grep java 这个命令查一下,把多余的进程关掉,再启动startup.sh 3.这种情况比较少见,就是在系统资源缺少的情况下,被系统自动DOWN掉,或是被其它软件干掉了。

    布了几个项目。居然天天会自动的挂掉。急了。花时间解决了一下。总结方案如下:


    1.磁盘满了。这大家都懂,清一下


    2.tomcat在关闭的或是重启的时候,常常后台进程没有被关闭。需要用ps aux|grep java 这个命令查一下,把多余的进程关掉,再启动startup.sh


    3.这种情况比较少见,就是在系统资源缺少的情况下,被系统自动DOWN掉,或是被其它软件干掉了。


    其实在我这里,这样还是没有解决,进程还是莫名其妙会自己挂掉,日志也没有任何报错。后来打开tomcat主目录下的子目录conf里面的tomcat-users.xml文件,用户配置内容如下:
    <tomcat-users>
      <user name="tomcat" password="tomcat" roles="tomcat" />
      <user name="role1"  password="tomcat" roles="role1"  />
      <user name="both"   password="tomcat" roles="tomcat,role1" />
    </tomcat-users>


    全都关闭掉。


    居然不见重启了。再观察两天。如果没有再发生意外闭的话。看来是客户和老子过不去啊~~

    展开全文
  • 进程的概念主要有两点:第一,进程是一个实体。每个进程都有自己的虚拟地址空间,包括文本区、数据区、和堆栈区。文本区域存储处理器执行的代码;数据区存储变量和动态分配的内存;堆栈区存储着活动进程调用的指令和...

    ---恢复内容开始---

    进程是一个程序一次执行的过程,是操作系统动态执行的基本单元。

    进程的概念主要有两点:第一,进程是一个实体。每个进程都有自己的虚拟地址空间,包括文本区、数据区、和堆栈区。文本区域存储处理器执行的代码;数据区存储变量和动态分配的内存;堆栈区存储着活动进程调用的指令和本地变量。第二,进程是一个“执行中的程序”,它和程序有本质区别。程序是静态的,它是一些保存在磁盘上的指令的有序集合;而进程是一个动态的概念,它是一个运行着的程序,包含了进程的动态创建、调度和消亡的过程,是 Linux 的基本调度单位。只有当处理器赋予程序生命时,它才能成为一个活动的实体,称之为进程。

    内核的调度器复制在所有的进程间分配cpu执行时间,也称时间片,它轮流在每个进程分得的时间片用完后从进程那里抢回控制权。

    进程的内存布局:

    数据区:程序使用的静态变量。

    堆:程序可从该区域动态分配额外内存。

    栈:随函数调用、返回而增减的一片内存,用于为局部变量和函数调用链接信息分配存储空间。

     

    进程标识:

    操作系统OS 会为每个进程分配一个唯一的整型 ID,做为进程的标识号(pid)。进程 0 是调度进程,常被成为交换进程,它不执行任何程序,是内核的一部分,因此也被成为系统进程。进程除了自身的 ID 外,还有父进程 ID(ppid)。也就是说每个进程都必须有它的父进程,操作系统不会无缘无故产生一个新进程。所有进程的祖先进程是同一个进程,它叫做 init 进程,ID 为 1,init 进程是内核自举后的第一个启动的进程。init 进程负责引导系统、启动守护(后台)进程并且运行必要的程序。它不是系统进程,但它以系统的超级用户特权运行。

    pid唯一的标识进程由以下两个函数获得:

    pid_t getpid(void)             //PID(本身)

    pit_t getppid(void)         //ppid(父进程)

    首先可以通过ps命令来查询正在运行的进程;

    如:ps -eo pid,comm,cmd 命令。

    (-e 表示列出全部进程,-o pid,comm,cmd 表示我们需要 PID,COMMAND,CMD 信息)

    进程的用户 ID 与组 ID(进程的运行身份)

    进程在运行过程中,必须具有一类似于用户的身份,以便进行进程的权限控制,缺省情况下,哪个登录用户

    运行程序,该程序进程就具有该用户的身份。

    例如,假设当前登录用户为 gotter,他运行了 ls 程序,则 ls 在运行过程中就具有 gotter 的身份,该 ls 进程的用户 ID 和组 ID 分别为 gotter 和 gotter 所属的组。这类型的 ID 叫做进程的真实用户 ID 和真实组 ID。真实用户 ID 和真实组 ID 可以通过函数 getuid()和 getgid()获得。

    与真实 ID 对应,进程还具有有效用户 ID 和有效组 ID 的属性,内核对进程的访问权限检查时,它检查的是进程的有效用户 ID 和有效组 ID,而不是真实用户 ID 和真实组 ID。缺省情况下,用户的(有效用户 ID 和有效组 ID)与(真实用户 ID 和真实组 ID)是相同的。有效用户 id 和有效组 id 通过函数 geteuid()和 getegid()获得的。

    id 命令可以显示真实有效的用户 ID(UID) 和组 ID(GID)。UID 是对一个用户的单一身份标识。组 ID(GID)则对应多个 UID。

    进程的状态

    进程是程序的执行过程,根据它的生命周期可以划分成 3 种状态,如下图所示。

    执行态:该进程正在运行,即进程正在占用 CPU。

    就绪态:进程已经具备执行的一切条件,正在等待分配 CPU 的处理时间片。

    等待态:进程不能使用 CPU,若等待事件发生(等待的资源分配到)则可将其唤醒。

    进程相关的命令操作:

    nice 命令: 按用户指定的优先级运行进程:

    nice 命令用来设置优先级,优先级的数值为-20~19,其中数值越小优先级越高,数值越大优先级越低,-20 的优先级最高,19 的优先级最低。需要注意的是普通用户只能在 0~19 之间调整应用程序的优先权值,只有超级用户有权调整更高的优先权值(从-20~19)。

    nice [-n <优先级>] [--help] [--version] [执行指令] 选项介绍:

             -n <优先级>       指定优先级;

             --help                帮助信息;

             --version            版本信息;

    查看进程详细信息

    ps -l 打印结果:

    F S UID PID PPID C PRI NI ADDR SZ WCHAN TTY TIME CMD
    0 S 500 12866 12864 1 80 0 - 1681 - pts/0 00:00:00 bash
    0 R 500 12884 12866 1 80 0 - 1619 - pts/0 00:00:00 ps

    其中的几个重要信息有:

    UID : 代表执行者的身份

    PID : 代表这个进程的代号

    PPID :代表这个进程是由哪个进程发展衍生而来的,亦即父进程的代号

    PRI :代表这个进程可被执行的优先级,其值越小越早被执行

    NI :代表这个进程的 nice 值

    PRI 即进程的优先级,此值越小进程的优先级别越高。而 NI,也就是我们所要说的 nice 值(通过 nice 命令设置),其表示进程可被执行的优先级的修正数值。如前面所说,PRI 值越小越快被执行,那么加入 nice 值后,将会使得 PRI 变为:PRI(new)=PRI(old)+nice。所以,nice 命令设置的优先级不是程序最终的优先级,而只是优先级的修正数值。

    renice 命令允许用户修改一个正在运行的进程的优先权。

    用法:renice 优先级 [[-p] pid ] [[-g] pgrp ...] [[-u] 用户名 ...]

    示例,修正 PID28390 进程的优先级为 10:

    [root@xiaolong test]# renice 10 28390

    28390: old priority 0, new priority 10

    后台运行进程的查看与切换

    n  objs 命令查看后台运行的进程。

    示例:

    [root@xiaolong test]# ./a.out &

    [1] 28607

     

    //提示该进程运行的 PID 号

    [root@xiaolong test]# jobs

                [1]+    Running

    ./a.out &

    //后台运行的进程

    这里的 1 表示,后台程序的作业代号。

     当程序在前台运行时,按下 ctrl+z 可以将程序放入后台,并且暂停运行。

    fg 将后台中的命令调至前台继续运行。

    bg 将一个在后台暂停的命令,变成继续执行。

    杀死进程 kill 命令

    杀死进程前需要获取到进程的 PID 号,可以使用 ps 命令获取,PID 号是进程的唯一标识符。示例:

    [root@xiaolong test]# ps

    PID        TTY                 TIME CMD

    27810    pts/0       00:00:00 su

    27960    pts/0       00:00:00 bash

    28709    pts/0       00:04:18 a.out

    一般用法:

    kill PID 号

    kill -9 PID 号对于杀不死的进程可以加-9 参数强制杀死

     

    查看进程状态:

    ps -aux:查看各个进程状态,包括运行就绪等待等状态。

    ps -aux| grep 'aa':查找指定(aa)进程。

    ps -ef:查看所有的进程的 pid,ppid 等信息。

    ps -aux 看%cpu(cpu 使用量)      %mem(内存使用量) ps   查看

    进程创建与控制:

    fork 函数pid_t fork(void);

    linux 环境下,创建进程使用 fork 函数。

    在 linux 中 fork 函数是非常重要的函数,它从已存在进程中创建一个新进程。新进程为子进程,而原进程为父进程。

    ork 函数被调用一次之后返回两次,有三种不同的返回值:

    1、    在父进程中,fork 返回新创建的子进程的 PID 号

    2、    在子进程中,fork 返回 0;

    3、    如果出现错误,fork 返回一个负值。

    fork 函数创建子进程的过程:

    使用 fork 函数得到的子进程是父进程的一个复制品,它从父进程继承了进程的地址空间,包括进程上下文、进程堆栈、内存信息、打开的文件描述符、信号控制设定、进程优先级、进程组号、当前工作目录、根目录、资源限制、控制终端,而子进程所独有的只有它的进程号、资源使用和计时器等。通过这种复制方式创建出子进程后,原有进程和子进程都从函数 fork 返回,各自继续往下运行。也就是说,fork 不仅仅复制了进程的资源,更复制了原有进程的运行状态,所以复制出的新的进程(子进程)虽然什么都和父进程一样,但它从 fork 函数后面开始运行。但是原进程的 fork 返回值与子进程的 fork 返回值不同,在原进程中,fork 返回子进程的 pid,而在子进程中,fork 返回 0,如果 fork 返回负值,表示创建子进程失败。

    vfork 函数 pid_t vfork(void);

    vfork(建立一个新的进程)

    vfork()会产生一个新的子进程.但是 vfork 创建的子进程与父进程共享数据段,而且由 vfork()创建的子进程将先于父进程运行。

    vfork()用法与 fork()相似,但是也有区别,具体区别归结为以下几点:

    11. fork():子进程拷贝父进程的数据段,代码段。 vfork():子进程与父进程共享数据段。

    22. fork():父子进程的执行次序不确定。

    vfork():保证子进程先运行,在调用 exec 或_exit 之前与父进程数据是共享的,在它调用 exec 或_exit 之后父进程才可能被调度运行。

    33. vfork()保证子进程先运行,在她调用 exec 或_exit 之后父进程才可能被调度运行。如果在

    调用这两个函数之前子进程依赖于父进程的进一步动作,则会导致死锁。

    4当需要改变共享数据段中变量的值,则拷贝父进程。

     

    **现在,很多嵌入式 Linux 系统的 fork 函数调用都采用 vfork 函数的实现方式。实际上 uClinux 所有的多进程管理都通过 vfork 来实现。

    进程终止

    进程的终止有5种方式:

    main函数的自然返回return0;

    调用exit函数

    调用_exit函数

    接收到某个信号。如ctrl+c SIGINT 、 ctrl+\ SIGQUIT

    调用abort函数,它产生SIGABRT信号,所以是上一种方式的特例。

    前3种方式为正常的终止,后2种为非正常终止。但是无论哪种方式,进程终止时都将执行相同的关闭打开的文件,释放占用的内存等资源。只是后两种终止会导致程序有些代码不会正常的执行。

    exit和_exit函数都是用来终止进程的。当程序执行到exit和_exit时,进程会无条件的停止剩下的所有操作,清除包括PCB在内的各种数据结构,并终止本程序的运行。但是它们是有区别的

    exit 函数和_exit 函数的最大区别在于 exit 函数在退出之前会检查文件的打开情况,把文件缓冲区中的内容写回文件,就是图中的“清理 I/O 缓冲”。

    由于 linux 的标准函数库中,有一种被称作“缓冲 I/O”操作,其特征就是对应每一个打开的文件,在内存中都有一片缓冲区。每次读文件时,会连续读出若干条记录,这样在下次读文件时就可以直接从内存的缓冲区中读取;同样,每次写文件的时候,也仅仅是写入内存中的缓冲区,等满足一定的条件(如达到一定数量或遇到特定字符等),再将缓冲区中的内容一次性写入文件。这种技术大大增加了文件读写的速度,但也为编程带来了麻烦。比如有一些数据,认为已经写入文件,实际上因为没有满足特定的条件,它们还只是保存在缓冲区内,这时用_exit 函数直接将进程关闭,缓冲区中的数据就会丢失。因此,如想保证数据的完整性,建议使用 exit 函数。

    n  exit 和_exit 函数的原型:

    #include <stdlib.h> //exit 的头件 #include <unistd.h> //_exit 的头文件

    void exit(int status); void _exit(int status);

    status 是一个整型的参数,可以利用这个参数传递进程结束时的状态。一般来说,0 表示正常结束;其他的数值表示出现了错误,进程非正常结束。

    注意:’\n’ 换行符就是缓冲区的结束符

    孤儿进程

    用 fork 函数启动一个子进程时,子进程就有了它自己的生命并将独立运行。

    如果父进程先于子进程退出,则子进程成为孤儿进程,此时将自动被 PID 为 1 的进程(即 init)接管。孤儿进程退出后,它的清理工作有祖先进程 init 自动处理。但在 init 进程清理子进程之前,它一直消耗系统的资源,所以要尽量避免。

    僵尸进程

    如果子进程先退出,系统不会自动清理掉子进程的环境,而必须由父进程调用 wait 或 waitpid 函数来完成清理工作,如果父进程不做清理工作,则已经退出的子进程将成为僵尸进程(defunct),在系统中如果存在的僵尸(zombie)进程过多,将会影响系统的性能,所以必须对僵尸进程进行处理。

      wait 和 waitpid 都将暂停父进程,等待一个子进程退出,并进行清理工作;

      wait 函数随机地等待一个子进程退出,并返回该子进程的 pid;

      waitpid 等待指定 pid 的子进程退出;如果为-1 表示等待所有子进程,同样返回该子进程的 pid。

      status 参数是传出参数,它会将子进程的退出码保存到 status 指向的变量里

      options 用于改变 waitpid 的行为,其中最常用的是 WNOHANG,它表示无论子进程是否退出都将立即返回,不会将调用者的执行挂起。

     waitpid(pid, &n, 0);其中 n 是之前定义的整型变量。

    通常用下面的两个宏来获取状态信息:

    WIFEXITED(n) :如果子进程正常结束,它就取一个非 0 值。

    WEXITSTATUS(n): 如果 WIFEXITED 非零,它返回子进程的退出码,即子进程的 exit()函数的参数的值,或者 return 返回的值。

    查看系统全部环境变量

    可以用 env 命令查看环境变量。

    exec 函数族

    exec 函数族的组成

    extern char **environ; int execl(const char *path, const char *arg, ...); int execlp(const char *file, const char *arg, ...); int execle(const char *path, const char *arg , ..., char * const envp[]);

    int execv(const char *path, char *const argv[]); int execvp(const char *file, char *const argv[]); int execve(const char * path, char *const argv[], char *const envp[]);

    exec 函数族的作用是运行第一个参数指定的可执行程序。但其工作过程与 fork 完全不同,fork 是在复制一份原进程,而 exec 函数执行第一个参数指定的可执行程序之后,这个新程序运行起来后也是一个进程,而这个进程会覆盖原有进程空间,即原有进程的所有内容都被新运行起来的进程全部覆盖了,所以 exec 函数后面的所有代码都不再执行,但它之前的代码当然是可以被执行的。

      path 是包括执行文件名的绝对路径,file 既可是绝对路径也可是可执行文件名

      arg 是可执行文件的全部命令行参数,可以用多个,注意最后一个参数必须为 NULL。(比如:”ls”,”-l”,NULL)

      argv 是一个字符串的数组 char *argv[]={“full path”,”param1”,”param2”,...NULL};

      envp 指定新进程的环境变量 char *envp[]={“name1=val1”,”name2=val2”,...NULL};

    exec 函数族的参数传递有两种方式:一种是逐个列举的方式,而另一种则是将所有参数整体构造指针数组传递。在这里是以函数名的第 5 位字母来区分的,字母为“l”(list)的表示逐个列举的方式,其语法为 char *arg;字母为“v”(vertor)的表示将所有命令行参数整体构造指针数组传递,其语法为 char *const argv[]。

    以字母 p 结尾的函数通过搜索系统 PATH 这个环境变量来查找新程序的可执行文件的路径。如果可执行程序不在 PATH 定义的路径中,我们就需要把包括目录在内的使用绝对路径的文件名作为参数传递给函数。

    对有参数 envp 的函数调用,其以字母 e 结尾,函数通过传递 envp 传递字符串数组作为新程序的环境变量。

    新进程中的全局变量 environ 指针指向的环境变量数组将会被 envp 中的内容替代。

    注意:对于有参数 envp 的函数,它会使用程序员自定义的环境变量,如果自定义的环境变量中包含了将要执行的可执行程序的路径,那么第一个参数中是不是我们就可以不用写全路径了呢?不是的,必须写全路径。因

    为我们自定义的环境变量不是用来寻找这个可执行程序的,而是在这个可执行程序运行起来之后给新进程用的。

    可以用env命令查看环境变量。

     

     

     

     

     

    ---恢复内容结束---

    转载于:https://www.cnblogs.com/victorlingfeng/p/7376261.html

    展开全文
  • 最近在公司遇到一个很奇葩的问题,nodejs启动的子进程,在运行三天左右会自动退出,没有任何异常。最终发现是nodejs的子进程有一个属性://stdout、stderr 允许的最大输出大小(以 byte 为单位),如果超过了,子...
  • 引言 值此七夕佳节,烟哥放弃了无数妹纸的邀约,坐在电脑面前码字,就是为了给读者带来新的知识,这是一件伟大的事业! 好吧,实际情况是没人约。为了化解尴尬,我决定卖力写文章,嗯,一定是我过于...linux的OOM...
  • 1. linux socket 服务端程序 无缘无故退出 。 2. 客户端大量访问服务端后,出现 Resource temporarily unavailable错误 问题分析: 1. 是否有代码问题出现段错误 发现没有任何错误输出,查看(ulimit -a )并打开...
  • 重启Storm时,启动Supervisor 启动后自动退出在启动storm集群时,某个节点启动Supervisor 进程后后自动退出
  • Linux】线程(thread)

    2018-09-17 11:25:25
    Linux中实际上没有线程,而是用进程来模拟线程。  进程是所有PCB组成的一个线程组。 Linux中的线程是轻量级进程。  协程是轻量级线程,完全由程序控制,也就是在用户态执行。  进程是具有一个或者多个执行流的...
  • 最近一段时间,我linux系统里的tomcat老是无故shutdown,怎么也找不到原因,网上五花八门的野路子都没用.查看log,跟认为shutdown一样的log,这可怪了.我这明明没有关他,怎么显示这个呢. 后来一同事给指出了: shell下...
  • tomcat运行一段时间后,凌晨无缘无故挂掉,看了tomcat日志、项目日志、系统日志,没有发现错误。于是想到写一个shell脚本,每隔2分钟监控一次tomcat 的状态,若挂掉,则重新启动。解决方案参考网络,同时修改成符合...
  • linux系统中多线程同步的方法有? 互斥锁、条件变量、信号量、读写锁 进程间通信的方式及优缺点 a、无名管道:无名管道是一种半双工的通信方式,而且只能在具有亲缘关系的进程间使用,进程的亲缘关系通常是指父子...
  • storm在生产上跑得很欢快,最近突然遇到一个问题,不知道storm是什么原因,无缘无故的自动停止了。查看日志没有任何的报错信息,包括警告信息都没有,反正就是无缘无故的shut down了。问题分析,初步我们判断应该是...
  • linux下tomcat7无故停止服务可能的原因1、如果是out of memory,可以增加tomcat启动参数,在%tomcat%/bin/catalina.sh文件中,在rem Guess CATALINA_HOME if not defined后面增加"set JAVA_OPTS=-Xms256m -Xmx512m -...
  • 表现: 1.前端突然问我,Java服务是不是停了。...根据日志里面显示的pid以及启动保存的pid,去查看linux进程,发现该进程已经不在 分析: 1.先确认java进程是否停止,指令:lsof -i:8080 或者netstat -...
  • 关闭oom killer 最近有位 VPS 客户抱怨 MySQL 无缘无故挂掉,还有位客户抱怨 VPS 经常死机,登陆到终端看了一下...Linux 内核里的 Out of Memory (OOM) killer,OOM killer 会杀掉某个进程以腾出内存留给系统用,不
  • LinuxLinux多线程

    2018-05-02 21:45:24
    谈到线程就必须联系到进程,因为线程就是进程的一个执行流,下来我们深入解析一下: (1)进程是分配资源的最小单位 (2)线程是程序执行或者说调度的最小单位 (3)我们常说线程是在进程内部执行的,更准确的说....
  • 如果是out of memory,可以增加tomcat启动参数,在%tomcat%/bin/catalina.sh文件中,在rem Guess CATALINA_HOME if not defined后面增加"set JAVA_OPTS=-Xms256m -Xmx512m -XX:+...
  • Linux线程知识点总结

    2020-02-15 15:12:35
    了解线程的概念,理解线程与进程的区别与联系。 线程控制,线程创建,线程终止,线程等待。 线程分离与线程安全概念。 线程同步。 互斥量,条件变量,posix信号量,以及读写锁。 理解基于读写锁的读者写者问题。 ...
1 2 3 4 5 ... 8
收藏数 158
精华内容 63
关键字:

linux 进程无缘无故退出