zoukankan      html  css  js  c++  java
  • Linux系统编程——进程控制

    在学习Linux系统编程总结了笔记,并分享出来。有问题请及时联系博主:Alliswell_WP,转载请注明出处。

    09-linux-day05(进程控制)

    目录:
    一、学习目标
    二、进程
    1、进程和程序
    2、单道和多道程序设计
    3、进程的状态转化
    4、MMU 的作用
    5、PCB的概念
    6、获取环境变量
    7、进程控制函数fork
    8、fork创建子进程
    9、进程控制的命令
    10、创建n个子进程
    11、循环创建n个子进程控制顺序
    12、父子进程共享的内容
    13、父子进程不共享全局变量
    14、execlp函数介绍
    15、exec函数规律
    16、exel实现自定义程序
    17、孤儿进程与僵尸进程
    18、wait函数简单使用和说明
    19、wait回收并且查看死亡原因
    20、waitpid回收子进程
    21、用wait回收多个子进程
    22、waitpid回收多个子进程

    一、学习目标

    1、了解进程相关的概念

    2、掌握fork/getpid/getppid函数的使用

    3、熟练掌握ps/kill命令的使用

    4、熟练掌握execl/execlp函数的使用

    5、说出什么是孤儿进程什么是僵尸进程

    6、熟练掌握wait函数的使用

    7、熟练掌握waitpid函数的使用

    二、进程

    1、进程和程序

    什么是程序?编译好的二进制文件。

    什么是进程?运行着的程序。

      站在程序员的角度:运行一系列指令的过程。

      站在操作系统角度:分配系统资源的基本单位。

    区别:

      程序占用磁盘,不占用系统资源。

      进程占用系统资源。

      一个程序对应多个进程,一个进程对应一个程序。

       程序没有生命周期,进程有生命周期。

    2、单道和多道程序设计

    3、进程的状态转化

    进程的状态切换

    4、MMU 的作用

    》MMU作用:1)虚拟内存和物理内存的映射;2)修改内存访问级别

    用户空间映射到物理内存是独立的。

    5、PCB的概念

    Linux内核的进程控制块是task_struct结构体。

    查找结构体:

    > sudo grep -rn "struct task_struct {" /usr/

    技巧:光标停留在{上,按%,可以到结构体的结尾。(400多行)

    查看进程的资源上限:

    >ulimit -a

    6、获取环境变量

    查看所有的环境变量:(写法:key=value,且等号两端不能有空格

    >env

    常见的环境变量:

    >echo $HOME

    >echo $PATH

    》getenv函数——获取环境变量

    man 2 getenv

    char *getenv(const char *name);

    >touch getenv.c

    >vi getenv.c

    1 #include<stdio.h>
    2 #include<stdlib.h>
    3 
    4 int main()
    5 {
    6     printf("homepath is [%s]
    ", getenv("HOME"));
    7     return 0;
    8 }

    >make

    >./getenv

    》setenv函数——设置环境变量的值

    man 2 setenv

    int setenv(const char *name, const char *value, int overwrite);

      参数overwrite取值:1-覆盖原环境变量;0-不覆盖(该参数常用于设置新环境变量。)

    一般配置(.bashrc)文件!(如:export key=val;)

    》unsetenv函数——删除环境变量name的定义

    man 2 unsetenv

    int unsetenv(const char *name);

      注意事项:name不存在仍返回0(成功),当name命名为“ABC=”时则会出错!

    7、进程控制函数fork

    >touch fork.c

    >vi fork.c

     1 #include<stdio.h>
     2 #include<unistd.h>
     3 
     4 int main()
     5 {
     6     printf("Begin ....
    ");//此处缺少“
    ”结果会不一样,这是由于printf有缓冲区机制,往屏幕上输出,会有行缓冲
     7     pid_t pid = fork();
     8     printf("End ....
    ");
     9     return 0;
    10 }

    >make

    >./fork

    (可以知道结束了两个进程。)

    》getpid获得当前进程的PID和进程ID,,getppid——获得当前进程父进程的ID

    man getpid

    pid_t getpid(void);

    pid_t getppid(void);

    8、fork创建子进程

    >vi fork.c

     1 #include<stdio.h>
     2 #include<unistd.h>
     3 #include<stdlib.h>
     4 
     5 int main()
     6 {
     7     printf("Begin ....
    ");
     8     pid_t pid = fork();
     9     if(pid < 0){
    10         perror("fork err");
    11         exit(1);
    12     }
    13     if(pid == 0){
    14         //子进程
    15         printf("I am a child,pid = %d,ppid = %d
    ",getpid(), getppid());
    16     }
    17     else if(pid > 0){
    18         //父进程的逻辑
    19         printf("childpid=%d,self=%d,ppid=%d
    ",pid,getpid(),getppid());
    20         sleep(1);//让父进程等待一段时间,再死去。
    21     }
    22     
    23     printf("End ....
    ");
    24     return 0;
    25 }

    >make

    >./fork

    9、进程控制的命令

    >vi fork.c

     1 #include<stdio.h>
     2 #include<unistd.h>
     3 #include<stdlib.h>
     4 
     5 int main()
     6 {
     7     printf("Begin ....
    ");
     8     pid_t pid = fork();
     9     if(pid < 0){
    10         perror("fork err");
    11         exit(1);
    12     }
    13     if(pid == 0){
    14         //子进程
    15         printf("I am a child,pid = %d,ppid = %d
    ",getpid(), getppid());
    16         while(1){
    17             printf("I am a child
    ");
    18             sleep(1);
    19         }
    20     }
    21     else if(pid > 0){
    22         //父进程的逻辑
    23         printf("childpid=%d,self=%d,ppid=%d
    ",pid,getpid(),getppid());
    24         while(1){
    25             sleep(1);
    26         }
    27     }
    28     
    29     printf("End ....
    ");
    30     return 0;
    31 }

    >make

    >./fork

    让子进程一直保持运行,打开另一个终端查看PPID和PID分析:

    》ps aux——查看进程相关信息的指令

    》ps ajx——可以查看更多进程相关的信息,从而追溯进程之间的血缘关系

    可以看到PPID、PID,可以看出shell进程是所有进程的父亲,继续追溯,可以看到最终父进程是init

    init进程是所有进程的祖先!

    >kill -9 2890

    >ps ajx

    杀死了父进程,init成为子进程2891新的父亲。

    >kill 2891

    杀死子进程

    》kill指令——给进程发送一个信号

      kill -l 查看信号相关的信息

      SIGKILL 9号信号

      kill -9 pid——杀死进程

    10、创建n个子进程

    需求:让父进程创建n个子进程

    >touch nfork.c

    >vi nfork.c

     1 #include<stdio.h>
     2 #include<unistd.h>
     3 #include<stdlib.h>
     4 
     5 int main()
     6 {
     7     int n = 5;
     8     int i = 0;
     9     pid_t pid = 0;
    10     for(i = 0; i < 5; i++){//父进程循环结束
    11         pid = fork();
    12         if(pid == 0){
    13             //son
    14             printf("I am child, pid=%d,ppid=%d
    ",getpid(),getppid());
    15             //break;//子进程退出循环的接口
    16         }
    17         else if(pid > 0){
    18             //father
    19             printf("I am father, pid=%d,ppid=%d
    ",getpid(),getppid());
    20         }
    21     }
    22     while(1){
    23         sleep(1);
    24     }
    25     return 0;
    26 }

    >gcc nfork.c

    >./a.out

    >ps aux | grep a.out |grep -v grep | wc -l

    (创建了32个进程!)

    11、循环创建n个子进程控制顺序

    》需求:精确控制各个子进程

    >touch nfork1.c

    >vi nfork1.c

     1 #include<stdio.h>
     2 #include<unistd.h>
     3 #include<stdlib.h>
     4 
     5 int main()
     6 {
     7     int n = 5;
     8     int i = 0;
     9     pid_t pid = 0;
    10     for(i = 0; i < 5; i++){//父进程循环结束
    11         pid = fork();
    12         if(pid == 0){
    13             //son
    14             printf("I am child, pid=%d,ppid=%d
    ",getpid(),getppid());
    15             break;//子进程退出循环的接口
    16         }
    17         else if(pid > 0){
    18             //father
    19             //printf("I am father, pid=%d,ppid=%d
    ",getpid(),getppid());
    20         }
    21     }
    22     
    23     sleep(i);
    24     if(i < 5){
    25         printf("I am child, will exit,pid=%d,ppid=%d
    ",getpid(),getppid());
    26     }
    27     else{
    28         //father
    29         printf("I am parent, will out,pid=%d,ppid=%d
    ",getpid(),getppid());
    30     }
    31     
    32     return 0;
    33 }

    >gcc nfork1.c

    >./a.out

    (创建了5个进程!)

    12、父子进程共享的内容

    13、父子进程不共享全局变量

    >touch shared.c

    >vi shared.c

     1 #include<stdio.h>
     2 #include<unistd.h>
     3 #include<stdlib.h>
     4 
     5 int var = 100;
     6 
     7 int main()
     8 {
     9     pid_t pid = fork();
    10     if(pid == 0){
    11         //son
    12         printf("var = %d,child,pid=%d,ppid=%d
    ",var,getpid(),getppid());
    13         var = 1001;
    14         printf("var = %d,child,pid=%d,ppid=%d
    ",var,getpid(),getppid());
    15     }
    16     else if(pid > 0){
    17         //parent
    18         sleep(1);//保证子进程能够修改var的值成功
    19         printf("var = %d,parent,pid=%d,ppid=%d
    ",var,getpid(),getppid());
    20     }
    21     
    22     return 0;
    23 }

    (让子进程改,看结果)

    >gcc shared.c

    >./a.out

    (让父进程改,看结果)

    >vi shared.c

     1 #include<stdio.h>
     2 #include<unistd.h>
     3 #include<stdlib.h>
     4 
     5 int var = 100;
     6 
     7 int main()
     8 {
     9     pid_t pid = fork();
    10     if(pid == 0){
    11         //son
    12         printf("var = %d,child,pid=%d,ppid=%d
    ",var,getpid(),getppid());
    13         var = 1001;
    14         printf("var = %d,child,pid=%d,ppid=%d
    ",var,getpid(),getppid());
    15         sleep(3);
    16         printf("var = %d,child,pid=%d,ppid=%d
    ",var,getpid(),getppid());
    17     }
    18     else if(pid > 0){
    19         //parent
    20         sleep(1);//保证子进程能够修改var的值成功
    21         printf("var = %d,parent,pid=%d,ppid=%d
    ",var,getpid(),getppid());
    22         var = 2000;
    23         printf("var = %d,parent,pid=%d,ppid=%d
    ",var,getpid(),getppid());
    24     }
    25     
    26     return 0;
    27 }

    >gcc shared.c

    >./a.out

    》结论:即使是全局变量,父子进程也不共享。(读时共享,写时复制。)

    14、execlp函数介绍

    >man execl

    》execl作用:执行其他程序

    int execl(const char *path,const char *arg,...) /*(char *)NULL*/

    》exelp作用:执行程序的时候,使用PATH环境变量,执行的程序可以不用加路径

    int execlp(const char *file,const char *arg,...) /*(char *)NULL */

      file 要执行的程序

      arg 参数列表

      参数列表最后需要一个NULL作为结尾(哨兵)

      返回值:只有失败才返回

    >touch execl.c

    >vi execl.c

     1 #include<stdio.h>
     2 #include<unistd.h>
     3 
     4 int main()
     5 {
     6     //int execlp(const char *file,const char *arg,...) /*(char *)NULL */
     7     execlp("ls", "ls", "-l", "--color=auto",NULL);//如果使其执行错误,返回,把第一个ls改为非法的lsxxx
     8     //不需要判断返回值
     9     perror("exec err");
    10     return 0;
    11 }

    >gcc execl.c

    >./a.out

    >vi execl.c

     1 #include<stdio.h>
     2 #include<unistd.h>
     3 
     4 int main()
     5 {
     6     //int execlp(const char *file,const char *arg,...) /*(char *)NULL */
     7     //execlp("ls", "ls", "-l", "--color=auto",NULL);
     8     execl("/bin/ls", "ls", "-l", "--color=auto",NULL);
     9     //不需要判断返回值
    10     perror("exec err");
    11     printf("hello
    ");
    12     return 0;
    13 }

    >gcc execl.c

    >./a.out

    15、exec函数规律

    16、exel实现自定义程序

    >touch fpe.c

    >vi fpe.c

    1 #include<stdio.h>
    2 #include<unistd.h>
    3 
    4 int main()
    5 {
    6     int a = 10;
    7     int b = a/0;
    8     return 0;
    9 }

    >make

    >./fpe

    (报错:浮点数例外(核心已转储))

    >touch execl.c

    >vi execl.c

     1 #include <stdio.h>
     2 #include <unistd.h>
     3 
     4 int main()
     5 {
     6     execl("./fpe", "fpe", NULL);
     7     
     8     printf("bye bye!
    ");
     9     return 0;
    10 }

    >gcc execl.c

    >./a.out

    (报错:浮点数例外(核心已转储))

    17、孤儿进程与僵尸进程

    》孤儿进程:父亲死了,子进程被init进程领养。

    》僵尸进程:子进程死了,父进程没有回收子进程的资源(PCB)。

    >touch orphan.c

    >vi orphan.c

     1 #include<stdio.h>
     2 #include<unistd.h>
     3 
     4 int main()
     5 {
     6     pid_t pid = fork();
     7     if(pid == 0){
     8         //子进程
     9         while(1){
    10             printf("I am a child,pid=%d,ppid=%d
    ",getpid(), getppid());
    11             sleep(1);
    12         }
    13     }
    14     else if(pid > 0){
    15         //父进程的逻辑
    16         printf("I am parent,pid=%d,ppid=%d
    ",getpid(),getppid());
    17         sleep(5);
    18         printf("I am parent,I will die!
    ")
    19     }
    20     
    21     printf("End ....
    ");
    22     return 0;
    23 }

    >gcc orphan.c

    >./a.out

    (Ctrl+c无法结束子进程,因为子进程脱离shell,只能打开另一个终端,ps ajx查看pid,然后用kill -9 pid杀死)

    >touch zoombie.c

    >vi zoombie.c

     1 #include<stdio.h>
     2 #include<unistd.h>
     3 
     4 int main()
     5 {
     6     pid_t pid = fork();
     7     if(pid == 0){
     8         //子进程
     9         printf("I am child,pid=%d,ppid=%d
    ",getpid(), getppid());
    10         sleep(2);
    11         printf("I am child,I will die
    ");
    12     }
    13     else if(pid > 0){
    14         //父进程的逻辑
    15         while(1){
    16             printf("I am parent,pid=%d
    ",getpid());
    17             sleep(1);    
    18         }
    19     }
    20     return 0;
    21 }

    >gcc orphan.c

    >./a.out

    (打开另一个终端查看 >ps aux | grep a.out)

    (如果想研究下,使用man ps查看,然后用/zombie定位到)

    》如何回收僵尸进程资源?

    杀死父亲,init领养,负责回收。

    18、wait函数简单使用和说明

    》wait作用:1)阻塞等待;2)回收子进程资源;3)知道子进程的死亡原因

    man 2 wait

    pid_t wait(int *status);

      status 传出参数

      返回值:成功返回终止的子进程ID,失败返回-1

    子进程的死亡原因:

      正常死亡 WIFEXITED

        如果WIFEXITED为真,使用WEXITSTATUS得到退出状态。

      非正常死亡WIFSIGNALED

        如果WIFSIGNALED为真,使用WTERMSIG得到信号

    19、wait回收并且查看死亡原因

    >touch wait.c

    >vi wait.c

     1 #include<stdio.h>
     2 #include<unistd.h>
     3 #include<sys/types.h>
     4 #include<sys/wait.h>
     5 
     6 int main()
     7 {
     8     pid_t pid = fork();
     9     if(pid == 0){
    10         //子进程
    11         printf("I am child,I will die
    ");
    12         sleep(2);
    13         while(1){//迁怒,等待kill杀死,非正常死亡
    14             printf("I am child,guo lai da wo!
    ");
    15             sleep(1);
    16         }
    17         //正常死亡的两种情况
    18         //return 101;
    19         //exit(101);
    20     }
    21     else if(pid > 0){
    22         //父进程的逻辑
    23         printf("I am parent,wait for child die!
    ");
    24         int status;
    25         
    26         pid_t wpid = wait(&status);
    27         printf("wait ok,wpid=%d,pid=%d
    ",wpid,pid);
    28         if(WIFEXITED(status)){
    29             printf("child exit with %d
    ",WEXITSTATUS(status));
    30         }
    31         if(WIFSIGNALED(status)){
    32             printf("child killed by %d
    ",WTERMSIG(status));
    33         }
    34         while(1){
    35             sleep(1);    
    36         }
    37     }
    38     return 0;
    39 }

    >gcc wait.c

    >./a.out

    (打开另一个终端,使用ps aux|grep a.out查看pid,然后用kill -9 pid杀死子进程,原终端将会收到:child killed by 9;用kill pid杀死子进程,原终端将会收到:child killed by 15)

    20、waitpid回收子进程

    man waitpid

    pid_t waitpid(pid_t pid, int *status, int options);

      pid

        <-1 组id

        1 回收任意

        0 回收和调用进程组id相同组内的子进程

        >0 回收指定的pid

      options

        0与wait相同,也会阻塞

        WNOHANG 如果当前没有子进程退出的,会立刻返回

      返回值

        如果设置了WNOHANG,那么如果没有子进程退出,返回0

          如果有子进程退出,返回退出的pid

        失败返回-1(没有子进程)

    >touch waitpid.c

    >vi waitpid.c

     1 #include<stdio.h>
     2 #include<unistd.h>
     3 #include<sys/types.h>
     4 #include<sys/wait.h>
     5 #include<stdlib.h>
     6 
     7 int main()
     8 {
     9     pid_t pid = fork();
    10     if(pid == 0){
    11         //子进程
    12         printf("I am child,pid=%d
    ",getpid());
    13         sleep(2);
    14     }
    15     else if(pid > 0){
    16         //父进程的逻辑
    17         printf("I am parent,pid=%d
    ",getpid());
    18         
    19         int ret;
    20         while((ret = = waitpid(-1,NULL,WNOHANG)) == 0){
    21             sleep(1);
    22         }
    23         printf("ret = %d
    ",ret);
    24         ret = waitpid(-1,NULL,WNOHANG);
    25         if(ret < 0){
    26             perror("wait err");
    27         }
    28         while(1){
    29             sleep(1);
    30         }
    31     }
    32     return 0;
    33 }

    >gcc waitpid.c

    >./a.out

    21、用wait回收多个子进程

    >touch nfork_wait.c

    >vi nfork_wait.c

     1 #include<stdio.h>
     2 #include<unistd.h>
     3 #include<sys/types.h>
     4 #include<sys/wait.h>
     5 #include<stdlib.h>
     6 
     7 int main()
     8 {
     9     int n = 5;
    10     int i = 0;
    11     pid_t pid;
    12     for(i = 0; i < 5; i++){
    13         pid = fork();
    14         if(pid == 0){
    15             printf("I am child,pid=%d
    ",getppid());
    16             break;
    17         }
    18     }
    19     sleep(i);
    20     if(i == 5){
    21         for(i = 0; i < 5; i++){
    22             pid_t wpid = wait(NULL);
    23             printf("wpid = %d
    ",wpid);
    24         }
    25         while(1){
    26             sleep(1);
    27         }
    28     }
    29     return 0;
    30 }

    >gcc nfork_wait.c

    >./a.out

    (打开另一个终端,使用ps aux|grep a.out查看pid,看回收是否成功)

    22、waitpid回收多个子进程

    >touch nfork_waitpid.c

    >vi nfork_waitpid.c

     1 #include<stdio.h>
     2 #include<unistd.h>
     3 #include<sys/types.h>
     4 #include<sys/wait.h>
     5 #include<stdlib.h>
     6 
     7 int main()
     8 {
     9     int n = 5;
    10     int i = 0;
    11     pid_t pid;
    12     for(i = 0; i < 5; i++){
    13         pid = fork();
    14         if(pid == 0){
    15             printf("I am child,pid=%d
    ",getppid());
    16             break;
    17         }
    18     }
    19     if(i == 5){
    20         //parent
    21         printf("I am parent!
    ");
    22         //如何使用waitpid回收?-1代表子进程都死了,都回收了
    23         while(1){
    24             pid_t wpid = waitpid(-1, NULL, WNOHANG);
    25             if(wpid == -1){
    26                 break;
    27             }
    28             else if(wpid > 0){
    29                 printf("waitpid wpid=%d
    ",wpid);
    30             }
    31         }
    32         while(1){
    33             sleep(1);
    34         }
    35     }
    36     if(i < 5){
    37         //sleep(i);
    38         printf("I am child,i = %d,pid=%d
    ",i,getpid());
    39     }
    40     return 0;
    41 }

    >gcc nfork_waitpid.c

    >./a.out

    (打开另一个终端,使用ps aux|grep a.out查看pid,看回收是否成功)

    》作业

    >touch fork_write.c

    >vi fork_write.c

     1 #include<stdio.h>
     2 #include<unistd.h>
     3 #include<sys/types.h>
     4 #include<sys/wait.h>
     5 #include<stdlib.h>
     6 #include<fcntl.h>
     7 #include<string.h>
     8 
     9 int main(int argc,char *argv[])
    10 {
    11     if(argc != 2){
    12         printf("./a.out filename
    ");
    13         return -1;
    14     }
    15     int fd = open(argv[1],O_RDWR);
    16     if(fd < 0){
    17         perror("open err");
    18         exit(1);
    19     }
    20     pid_t pid = fork();
    21     if(pid == 0){
    22         //son
    23         write(fd,"hello
    ",6);
    24     }
    25     else if(pid > 0){
    26         //parent
    27         sleep(1);
    28         write(fd,"world
    ",6);
    29         wait(NULL);
    30     }
    31     
    32     return 0;
    33 }

    >touch 11

    >make

    >ls -lrt

    >./fork_write 11

    >cat 11

    1 hello
    2 world

    (没有覆盖,说明写时共享文件描述符,读写位置一样,一个进程写了,读写位置改变了,另一个进程受影响,所以可以跟着写。)

    在学习Linux系统编程总结了笔记,并分享出来。有问题请及时联系博主:Alliswell_WP,转载请注明出处。

  • 相关阅读:
    LinkedList源码解析
    HashMap源码解析
    HashMap和Hashtable区别
    arcgis api for js 之网络分析服务发布
    arcgis api for js 之发布要素服务
    arcis api for js 值 3.17 本地部署
    ArcGIS 产品体系结构
    layui select下拉框选项不显示
    windows10企业版2016长期服务版激活
    PHP常见的输出语句
  • 原文地址:https://www.cnblogs.com/Alliswell-WP/p/CPlusPlus_Linux_05.html
Copyright © 2011-2022 走看看