1,pcb:进程控制块结构体:/usr/src/linux-headers-4.15.0-29/include/linux/sched.h

  • 进程id:系统中每个进程有唯一的id,在c语言中用pid_t类型表示,是个非负整数。

  • 进程状态:就绪,运行,挂起,停止等状态

  • 描述虚拟地址空间的信息

  • 描述控制终端的信息

  • 进程执行时的当前工作目录(current working directory)

  • umask掩码

  • 文件描述符表,包含很多指向file结构体的指针

  • 和信号相关的信息

  • 用户id和组id

  • 会话(session)和进程组

  • 进程可以使用的资源上限(Resource Limit)

    用【ulimit -a】查看:

    ys@ys:~$ ulimit -a
    core file size (blocks, -c) 0
    data seg size (kbytes, -d) unlimited
    scheduling priority (-e) 0
    file size (blocks, -f) unlimited
    pending signals (-i) 7743
    max locked memory (kbytes, -l) 16384
    max memory size (kbytes, -m) unlimited
    open files (-n) 1024
    pipe size (512 bytes, -p) 8
    POSIX message queues (bytes, -q) 819200
    real-time priority (-r) 0
    stack size (kbytes, -s) 8192
    cpu time (seconds, -t) unlimited
    max user processes (-u) 7743
    virtual memory (kbytes, -v) unlimited
    file locks (-x) unlimited

2,环境变量

查看所有的环境变量:env

环境写法:等号前后没有空格

KEY=VALUE

常用的环境变量:

  • PATH:可执行文件的搜索路径。比如可以直接敲ls,那是因为ls的可执行文件在环境变量PATH里,所以系统找得到。但是比如a.out执行文件,就不能直接敲a.out,因为a.out不在PATH里。
  • SHELL:当前使用的shell程序,通常是:/bin/bash
  • TERM:当前使用的终端类型,如果是图像界面的话,一般为:xterm-256color
  • LANG:当前的语言和字符编码,一般为:en_US.UTF-8
  • HOME:当前用户所在的绝对路径。

获取环境变量:getenv

#include <stdlib.h>
char *getenv(const char *name);

返回值:

  • 成功:返回指针

  • 失败:返回NULL

设置环境变量:setenv

#include <stdlib.h>
int setenv(const char *name, const char *value, int overwrite);

删除环境变量:unsetenv

#include <stdlib.h>
int unsetenv(const char *name);

设置环境变量和删除环境变量一般不用系统函数,而是编辑【~/.bashrc】文件。

使用【exprot key=value】。

3,进程概念

创建进程:fork

#include <sys/types.h>
#include <unistd.h>
pid_t fork(void);
  • 返回值

    • 失败:-1
    • 成功:返回2次
      • 父进程中返回:子进程的ID
      • 子进程返回:0

获得当前进程自己id:getpid

获得当前进程的父进程的id:getppid

ss
#include <unistd.h> pid_t getpid(void);
pid_t getppid(void);

理解fork的小例子:

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h> int main(){
//char* val = getenv("HOME");
printf("begin...\n");
pid_t pid = fork();
printf("end...\n");
}

执行结果:

ys@ys:~/test$ ./en
begin...
end...
ys@ys:~/test$ end...

从执行结果发下:

1,【begin】被打印出1次;【end】被打印出2次。

2,第二个【end】跑到了shell的后面。

分析:

  • 从执行结果1可以看出来,fork后,从1个进程,又分出了一个子进程。并且子进程不是从程序的开头开始执行,而是从fork后面开始执行,这个理解至关重要。
  • 执行结果2现象的解释,根本原因是父进程比子进程先执行结束了,导致了子进程成为了孤儿进程。最开始shell进程把执行权交给程序,也就是父进程,当父进程结束后,shell又接管终端,可是这个时间点,子进程还没结束,子进程的打印语句就把【end】打印到了shell后面。

下面的例子可以了解,getpid和getppid的用法:

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h> int main(){
//char* val = getenv("HOME");
printf("begin...\n");
pid_t pid = fork(); //child process
if(pid == 0){
printf("child: pid=%d, ppid=%d\n", getpid(), getppid());
}
//parent proces
else if(pid > 0){
printf("parent: pid=%d, child id:%d\n", getpid(), pid);
sleep(1);//为了让子进程先结束
}
printf("end...\n");
}

查看进程的命名:ps

  • 选项a:显示更多信息
  • 选项u:显示用户信息
  • 选项x:和选项a一起使用
  • 选项j:能看到父进程
ys@ys:~$ ps aux
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.0 0.3 159928 7536 ? Ss 13:53 0:02 /sbin/init spla
root 2 0.0 0.0 0 0 ? S 13:53 0:00 [kthreadd]
ys 4029 0.0 0.0 4508 812 pts/0 S+ 16:30 0:00 ./en
ys 4030 0.0 0.0 4508 80 pts/0 S+ 16:30 0:00 ./en
ys 4043 0.1 0.2 29560 4936 pts/1 Ss 16:30 0:00 bash
ys 4051 0.0 0.1 44472 3700 pts/1 R+ 16:30 0:00 ps aux

从下图可以看出子进程4030的父进程是4029,父进程4029的父进程是1671,进程1671是bash(shell)程序。所以的进程都是从进程1:【/sbin/init splash】衍生出来的。

ys@ys:~$ ps ajx
PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
0 1 1 1 ? -1 Ss 0 0:02 /sbin/init splash
1661 1671 1671 1671 pts/0 4029 Ss 1000 0:00 bash
1671 4029 4029 1671 pts/0 4029 S+ 1000 0:00 ./en
4029 4030 4029 1671 pts/0 4029 S+ 1000 0:00 ./en
1661 4043 4043 4043 pts/1 4055 Ss 1000 0:00 bash
4043 4055 4055 4043 pts/1 4055 R+ 1000 0:00 ps ajx

给进程发送信号:kill

使用【kill -l】查看信号列表,发现9号信号就杀死进程的信号。

ys@ys:~$ kill -l
1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP
6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR1
11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM
16) SIGSTKFLT 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP
21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ
26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO 30) SIGPWR
31) SIGSYS 34) SIGRTMIN 35) SIGRTMIN+1 36) SIGRTMIN+2 37) SIGRTMIN+3
38) SIGRTMIN+4 39) SIGRTMIN+5 40) SIGRTMIN+6 41) SIGRTMIN+7 42) SIGRTMIN+8
43) SIGRTMIN+9 44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13
48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12
53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9 56) SIGRTMAX-8 57) SIGRTMAX-7
58) SIGRTMAX-6 59) SIGRTMAX-5 60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-2
63) SIGRTMAX-1 64) SIGRTMAX

由父进程创建5个子进程的例子:

#include <sys/types.h>
#include <unistd.h>
#include <stdio.h> int main(){
int i = 0;
pid_t pid = 0;
for(i = 0; i < 5; ++i){
pid = fork();
if(pid == 0){
//child process
//printf("child: pid=%d, ppid=%d\n", getpid(), getppid());
break;
}
else if (pid > 0){
//parent process
//printf("parent: pid=%d, ppid=%d\n", getpid(), getppid());
}
} sleep(i);
printf("proces:%d will die:pid=%d,ppid=%d\n", i,getpid(), getppid()); }

执行结果:

ys@ys:~/test$ ./fo
proces:0 will die:pid=2139,ppid=2138
proces:1 will die:pid=2140,ppid=2138
proces:2 will die:pid=2141,ppid=2138
proces:3 will die:pid=2142,ppid=2138
proces:4 will die:pid=2143,ppid=2138
proces:5 will die:pid=2138,ppid=1704

要点在【break】和【sleep(i)】这2个地方。

不加break的话,子进程也会创建子进程。

观察【sleep(i);]这行,执行到这行的时间点,如果i=0,则说明是第一创建的子进程;

如果i=4,说明是最后创建的子进程,如果i=5,说明是父进程。所以根据i的值,进行sleep,就能够实现先创建的子进程先执行完,后创建的后执行完,最后才是父进程执行完。

在当进程调用别的程序:execl和execlp

#include <unistd.h>
int execl(const char *path, const char *arg, .../* (char *) NULL */);
int execlp(const char *file, const char *arg, .../* (char *) NULL */);
  • execl

    • path:目标程序的所在路径和程序名。例:【/bin/ls】
    • arg:目标程序的参数。注意第一个参数必须是程序自己的名字。
    • NULL:最后一个参数必须是NULL,告诉它参数列表到这里结束了。
    • 返回值:
      • 成功:就不返回了,直接走了,也就是说,调用execl的语句之后的代码都不被执行了。
      • 失败:-1
  • execlp:多了个p的含义就是:这个函数自己会去搜索环境变量PATH。
    • file:目标程序名,不用加路径。例:【ls】
    • arg:目标程序的参数。注意第一个参数必须是程序自己的名字。
    • NULL:最后一个参数必须是NULL,告诉它参数列表到这里结束了。
    • 返回值:
      • 成功:就不返回了,直接走了,也就是说,调用execl的语句之后的代码都不被执行了。
      • 失败:-1

调用ls的例子:

#include <unistd.h>
#include <stdio.h>
int main(){
execl("/bin/ls", "ls", "-l", "--color=auto", NULL);
perror("ls");
printf("not back\n");
}

孤儿进程和僵尸进程

孤儿进程:父进死了,被init进程领养,变成孤儿进程。

僵尸进程:子进程死了,但父进程没有回收子进程的资源(pcb(大结构体)),变成僵尸进程。

回收僵尸进程的方法:不能再用kill去杀,杀死父进程,让init领养,init负责回收。

查看僵尸进程:

 PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
1704 2683 2683 1704 pts/0 2683 S+ 1000 0:00 ./zo
2683 2684 2683 1704 pts/0 2683 Z+ 1000 0:00 [zo] <defunct>

发现有【Z+】和《defunct》就是僵尸进程。

回收子进程:wait函数

#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(int *wstatus);
  • 作用:

    • 阻塞等待(如果子线程还没有死亡,则一直等到它死亡)
    • 回收子进程的资源(PCB结构体)
    • 查看死亡的原因
  • wstatus:传出参数,里面有子进程死亡的原因。

    查看wstatus的方法:使用下面2组宏。

    WIFEXITED(wstatus)
    WEXITSTATUS(wstatus) WIFSIGNALED(wstatus)
    WTERMSIG(wstatus)
    • 正常死亡:WIFEXITED(wstatus)返回真,使用WEXITSTATUS(wstatus)得到退出状态。

      退出状态的具体含义:return 后面的数字;或者exit 括号里的数字。

    • 非正常死亡(被信号杀死):WIFSIGNALED(wstatus)返回真,使用WTERMSIG(wstatus)得到杀死它的信号(kill -l 显示出来的数字)。

  • 返回值:

    • 成功:返回死亡子进程的pid
    • 失败:-1

wait的例子:

#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <wait.h>
#include <stdlib.h> int main(){
int i = 0;
pid_t pid = 0; pid = fork();
if(pid == 0){
//child process
while(1){
printf("child: pid=%d, ppid=%d\n", getpid(), getppid());
sleep(3);
//return 101;
//exit(111);
//sleep(2);
}
}
else if (pid > 0){
//parent process
printf("parent: pid=%d, ppid=%d\n", getpid(), getppid());
int wstatus;
pid_t pid = wait(&wstatus);
if(WIFEXITED(wstatus)){
printf("child die pid=%d, status=%d\n",pid, WEXITSTATUS(wstatus));
}
if(WIFSIGNALED(wstatus)){
printf("child die pid=%d, status=%d\n",pid, WTERMSIG(wstatus));
}
}
}

分析:

  • 正常死亡,代码用return和exit来模拟的
  • 被信号杀死:在另一终端里使用【kill pid】后,返回的WTERMSIG(wstatus)为15(SIGTERM);如果用【kill -9 pid】则WTERMSIG(wstatus)为9(SIGKILL)

回收子进程:waitpid

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

    • < -1:回收组ID为-pid的子进程
    • -1:回收所有的子进程
    • 0:回收和调用进程的组id相同的组id的所有子进程。
    • 》0:回收指定pid的子进程
  • wstatus:和wait一样
  • options:
    • 0:与wait功能相同。
    • WNOHANG:如果执行waitpid的时点,子进程还没结束,会立刻返回,不阻塞。
  • 返回值:返回状态发生了变化的子进程的id。
    • 调用时设置了WNOHANG并且还指定了1个或者多个子进程

      • 如果有子进程退出了,返回退出了的子进程的pid
      • 如果没有子进程退出,返回0
    • 失败:-1(没有子进程了,子进程都被回收完了)

c/c++ 学习互助QQ群:877684253

本人微信:xiaoshitou5854

linux 进程概念的更多相关文章

  1. linux:进程概念

    Linux进程概念 一.实验介绍1.1 实验内容Linux 中也难免遇到某个程序无响应的情况,可以通过一些命令来帮助我们让系统能够更流畅的运行. 而在此之前,我们需要对进程的基础知识有一定的了解,才能 ...

  2. linux 进程(一)---基本概念

    一.进程的定义         进程是操作系统的概念,每当我们执行一个程序时,对于操作系统来讲就创建了一个进程,在这个过程中,伴随着资源的分配和释放.可以认为进程是一个程序的一次执行过程.   二.进 ...

  3. Linux进程管理(一、 基本概念和数据结构)

    被问到两个问题, 后来想了下如果要讲明白还不太容易,需要对进程的概念,进程管理有清晰的认识: 1. 父进程打开了一个文件,然后通过fork创建一个子进程, 子进程是否共享父进程的文件描述符? 2. 在 ...

  4. Linux系统编程之进程概念

    注:本文部分图片来源于网络,如有侵权,请告知删除 1. 什么是进程? 在了解进程概念之前,我们需要先知道程序的概念. 程序,是指编译好的二进制文件,这些文件在磁盘上,并不占用系统资源. 进程,指的是一 ...

  5. Linux 进程与信号的概念和操作

    进程 主要参考: http://www.bogotobogo.com/Linux/linux_process_and_signals.php 信号与进程几乎控制了操作系统的每个任务. 在shell中输 ...

  6. Linux 进程与信号的概念和操作 linux process and signals

    进程 主要参考: http://www.bogotobogo.com/Linux/linux_process_and_signals.php 译者:李秋豪 信号与进程几乎控制了操作系统的每个任务. 在 ...

  7. 【Linux大系】Linux的概念与体系

    感谢原作者:Vamei 出处:http://www.cnblogs.com/vamei 我在这一系列文章中阐述Linux的基 本概念.Linux操作系统继承自UNIX.一个操作系统是一套控制和使用计算 ...

  8. 如何灵活运用Linux 进程资源监控和进程限制

    导读 每个 Linux 系统管理员都应该知道如何验证硬件.资源和主要进程的完整性和可用性.另外,基于每个用户设置资源限制也是其中一项必备技能. 在这篇文章中,我们会介绍一些能够确保系统硬件和软件正常工 ...

  9. Linux的概念与体系

    作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明.谢谢! 我在这一系列文章中阐述Linux的基本概念.Linux操作系统继承自UNIX.一个 ...

随机推荐

  1. MySQL 数据库 Query 的优化

    理解MySQL的Query Optimizer MySQL Optimizer是一个专门负责优化SELECT 语句的优化器模块,它主要的功能就是通过计算分析系统中收集的各种统计信息,为客户端请求的Qu ...

  2. posix,perl正则表达式区别

    1.正则表达式(Regular Expression,缩写为regexp,regex或regxp),又称正规表达式.正规表示式或常规表达式或正规化表示法或正规表示法,是指一个用来描述或者匹配一系列符合 ...

  3. JavaScript-通过原型继承一个对象

    <script> //通过原型继承一个对象 //inherit()返回了一个继承原自原型对象P的属性的新对象 //這裡使用ECMAScript5中的object.create()函數(如果 ...

  4. width和max-width的用处

    width默认是auto啊,你设置max-width相当于没设置width,它按默认值auto自然就是图片宽度咯.max-width很多的场景都是和width配合用的:比如设置一个标签,width是( ...

  5. js中函数的写法

    js提供了灵活的函数写法,我们常见的函数写法和调用可能是: function ask(){ console.log(1); } ask(); 这样就完成了函数的定义和调用,司空见惯. 还有js里面的匿 ...

  6. Android 增强版百分比布局库 为了适配而扩展

    转载请标明出处: http://blog.csdn.net/lmj623565791/article/details/46767825: 本文出自:[张鸿洋的博客] 一 概述 上周一我们发布了Andr ...

  7. python 3.6 lxml标准库lxml的安装及etree的使用注意

    据我所知,python 3.5之后的lxml模块里面不再包含etree,那么要怎么解决这个问题呢? lxml模块下的etree函数的使用问题,部分lxml模块不再支持etree方法,因此只能想办法下载 ...

  8. git fatal: 远程 origin 已经存在。

    不小心将git远程地址配错了,再次配置提示以下错误: fatal: 远程 origin 已经存在. 此时只需要将远程配置删除,重新添加即可: git remote rm origin git remo ...

  9. mysql binlog格式

    Binlog Event 对于一个 Binlog Event 来说,它分为三个部分,header,post-header 以及 payload.MySQL 的 Binlog Event 有很多版本,我 ...

  10. centos-7 yum装docker-ce后启动失败

    相关版本: centos-7:   CentOS Linux release 7.0.1406 (Core) docker-ce: Docker version 18.03.0-ce, build 0 ...