zoukankan      html  css  js  c++  java
  • 2019年8月8日星期四(系统编程)

    201988日星期四

    . linux系统编程学习大纲

    1. 进程的概念,进程诞生与死亡,进程函数接口,进程的意义。

    2. 进程之间通信方式:有名管道,无名管道,信号,消息队列,共享内存,信号量

    3. linux进程的信号集,设置信号的阻塞状态。

    4. 线程的概念,线程与进程的区别?线程诞生与死亡,函数接口。

    5. 线程的同步互斥方式,有名信号量,无名信号量,互斥锁,读写锁,条件变量

    6. 线程池 -> 为了同时处理多个任务。

    . 进程的概念?

    1. 什么是程序?什么是进程?

    程序就是一堆待执行的代码。  -> 静态的文本数据。 例如: project.c(C语言程序) /  project(可执行程序)。

    进程就是当程序被CPU加载,根据每一行代码做出相应的效果,才能形成一个动态的过程,这个过程就称之为进程。

    2. linux下,如何开启一个新的进程?

    直接在linux下执行程序即可。

    例如: ./project   -> 开启一个新的进程!

    3. 当进程开启,系统会为进程分配什么资源?

    1)会分配进程对应内存空间。

    2)任务结构体 -> struct task_struct   -> linux下,任何进程就像一个任务。

    结构体在哪里?

    Ubuntu: /usr/src/linux-headers-3.5.0-23/include/linux/sched.h

    struct task_struct {

           volatile long state; /* -1 unrunnable, 0 runnable, >0 stopped */  -> 进程运行状态。

           void *stack;

           ....

           struct uprobe_task *utask;

           int uprobe_srcu_id;

    #endif

    };

    . 关于查看进程信息的linux命令。

    1)查看整个linux系统的ID  -> ps -ef(静态)

    gec@ubuntu:~$ ps -ef

    用户名     PID   PPID    创建时间          持续时间   进程名字   

    root         1     0  0 16:37    ?        00:00:00  /sbin/init          -> 祖先进程

    gec       2272     1  0 16:37    ?        00:00:01  gnome-terminal      -> linux终端

    gec       2278  2272  0 16:37    pts/1    00:00:00  bash         -> linux终端的子进程,叫bash进程

    gec       2720  2278  0 18:54    pts/1    00:00:00  ps -ef              -> bash进程的子进程,shell命令

    2)查看进程CPU使用率  -> top(动态)

    gec@ubuntu:~$ top

    Tasks: 150 total  -> 当前系统有150个进程  

           2 running     -> 2个在运行态

           148 sleeping  -> 148个睡眠态

           0 stopped     -> 0个暂停态

           0 zombie      -> 0个僵尸态

     PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND 

    1049 root      20   0 96160  26m 6700 S  5.3  2.6   0:18.50 Xorg  

    S  -> state当前进程状态

    %CPU -> 当前瞬间CPU占用率

    %MEM -> 当前瞬间内存占用率

    3)查看整个系统的关系图  -> pstree

    gec@ubuntu:~$ pstree

    init─┬─NetworkManager───{NetworkManager}

          ├─accounts-daemon───{accounts-daemon}

          ├─acpid

          ├─gnome-terminal─┬─bash───pstree

    . 进程的诞生与死亡。

    1.进程有什么状态?

    进程生老病死就是从进程开启到进程结束经历所有状态。

    就绪态:不占用CPU资源,不运行代码。

    运行态:占用CPU资源,运行代码。

    暂停态:占用CPU资源,不运行代码,可以切换到就绪态/运行态

    睡眠态:占用CPU资源,运行代码,可以切换到就绪态/运行态

    僵尸态:占用CPU资源,不运行代码,不可以切换到就绪态/运行态

    死亡态:不占用CPU资源,不运行代码。

    2. 什么是占用CPU资源?

    就是进程自身的资源(任务结构体)没有释放。

    3. 什么是僵尸态?

    进程结束时,一定会切换到僵尸态。所谓僵尸态,就是本进程已经结束,但是自身的资源还没有释放掉。

    详细见:"进程的生老病死.jpg"

    4. 需要注意的地方:

    1)进程在暂停态时,如果收到继续的信号,是切换到就绪态,而不是运行态。

    2)进程退出时,一定会变成僵尸态。

    3)进程不可以没有父进程,也不能同时拥有两个父进程。

    4)孤儿进程特点:当自己的父进程退出后,会马上寻找继父,而不是等到变成僵尸再找。

    5init祖先进程特点:一定会帮所有的孤儿回收资源。

    . 进程的函数接口?

    单进程程序  -> 只能一行一行代码去执行。

    创建子进程意义: 同时处理多个任务。

    1. 在进程内部创建一个新的子进程?  -> fork()  -> man 2 fork

    功能: fork - create a child process  -> 创建子进程

    使用格式:

          #include <unistd.h>

          pid_t fork(void);

       参数:无

       返回值:  pid_t  -> 进程PID号数据类型  %d

           成功: 

                  父进程 -> 子进程的PID号  >0

                  子进程 -> 0

           失败:

                  父进程 -> -1

                  没有创建出子进程。

      例题: 在进程内部创建一个新的子进程,看看会不会同时处理两件事情。

    #include <unistd.h>

    #include <stdio.h>

     

    int main(int argc,char *argv[])

    {

           /* 现在只有一个进程,就是父进程 */

           printf("hello ");

           printf("world ");

           fork();

           /* 到这里为止,就有两个进程 */

           printf("appletree ");

           return 0;

    }

    结果1  父进程先运行,子进程后执行。

    gec@ubuntu:/mnt/hgfs/GZ1934/09 系统编程/01/code$ ./fork_test

    hello

    world

    appletree  -> 父进程打印

    gec@ubuntu:/mnt/hgfs/GZ1934/09 系统编程/01/code$ appletree   -> 子进程打印

    结果2  子进程先运行,父进程后执行。

    gec@ubuntu:/mnt/hgfs/GZ1934/09 系统编程/01/code$ ./fork_test

    hello

    world

    appletree  -> 子进程打印

    appletree  -> 父进程打印

    gec@ubuntu:/mnt/hgfs/GZ1934/09 系统编程/01/code$

    结论:

    1)父子进程谁先运行,是随机的。
    2fork()后的代码,两个进程都会执行。

      练习1: 写一个程序,使得子进程先打印apple,父进程再打印hello。

    #include <unistd.h>

    #include <stdio.h>

    int main()

    {

           /* 父进程 */

           pid_t x;

           x = fork();

                     /* 父进程 */     /* 子进程 */

    //返回值情况:       x > 0          x = 0

           if(x > 0)  //父进程

           {

                  usleep(5000);

                  printf("hello! ");

           }

           if(x == 0) //子进程

           {

                  printf("apple! ");

           }

           return 0;

    }

    2. 查看自身的ID号以及查看父进程的ID

         getpid()       getppid()   -> man 2 getpid

    功能: getpid, getppid - get process identification  -> 获取进程的PID

    使用格式:

           #include <sys/types.h>

           #include <unistd.h>

           pid_t getpid(void);

           pid_t getppid(void);

           参数:无

           返回值:

              getpid()

                  成功: 当前进程的ID号

                  失败: 不存在的!

              getppid()

                  成功: 当前进程的PID号

                  失败: 不存在的!

       练习2: 在子进程中打印自己与父进程的ID号,在父进程中打印自己与子进程的ID,通过ps -ef命令查看ID是否一致!

    #include <stdio.h>

    #include <sys/types.h>

    #include <unistd.h>

    int main()

    {

           pid_t x;

           x = fork();

           if(x > 0) //父

           {

                  usleep(10000);

                  printf("parent pid = %d ",getpid());

                  printf("child pid = %d ",x);

           }

           if(x == 0) //子

           {

                  printf("child pid = %d ",getpid());

                  printf("parent pid = %d ",getppid());

           }

           return 0;

    }

       练习3: 验证孤儿进程会马上寻找继父,而不是等到孤儿进程结束才找。

    #include <stdio.h>

    #include <sys/types.h>

    #include <unistd.h>

    int main(int argc,char *argv[])

    {

           pid_t x;

           x = fork();

          

           if(x > 0)

           {

                  sleep(2);

           }

           if(x == 0)

           {

                  printf("parent pid = %d ",getppid());

                  sleep(3);

                  printf("parent pid = %d ",getppid());

                  printf("helloworld! ");

                  sleep(1);

                  printf("appletree! ");

           }

           return 0;

    }

    . 如何解决僵尸问题?

    1. 父进程主动回收子进程的资源。  -> wait()   -> man 2 wait

    功能: wait for process to change state

    使用格式:

           #include <sys/types.h>

           #include <sys/wait.h>

           pid_t wait(int *status);

           status:储存子进程退出状态的指针。

                  填NULL,代表父进程不关心子进程的退出状态。

                  不填NULL,代表父进程想知道子进程的退出状态。

          返回值:

                  成功: 退出的子进程的ID号

                  失败: -1

      练习4: 验证wait()可以帮子进程回收资源。

    #include <stdio.h>

    #include <sys/types.h>

    #include <unistd.h>

    #include <sys/wait.h>

    int main()

    {

           pid_t x;

           x = fork();

           if(x > 0)

           {

                  sleep(10);  //有1个僵尸

                  wait(NULL);  //僵尸态 -> 死亡态

                 

                  sleep(8);  //有0个僵尸

           }

           if(x == 0)

           {

                  printf("hello! ");  //运行态 -> 僵尸态

           }

           return 0;

    }

    2. 父进程还在,但是不主动调用wait()去回收资源。

    举例子。

    #include <stdio.h>

    #include <sys/types.h>

    #include <unistd.h>

    int main()

    {

           pid_t x;

           x = fork();

          

           if(x == 0)

           {

                  printf("parent pid = %d ",getppid());

                  printf("child helloworld! ");

                  //printf("parent pid = %d ",getppid());

           } //子进程: 运行态 -> 僵尸态

           if(x > 0)

           {

                  sleep(15); //1个僵尸态

                  printf("parent helloworld! ");

                  sleep(3);

                  printf("parent exit! ");  //子进程就会寻找继父,帮自己回收资源。

           }

           return 0;

    }

    3. 父进程比子进程先退出,子进程就会马上寻找继父,等待自身变成僵尸态时,就会让继父帮自己收尸!

    举例子。

    #include <stdio.h>

    #include <sys/types.h>

    #include <unistd.h>

    int main(int argc,char *argv[])

    {

           pid_t x;

           x = fork();

           if(x > 0)

           {

                  sleep(2);

           }

           if(x == 0)

           {

                  printf("parent pid = %d ",getppid());

                  sleep(3);

                  printf("parent pid = %d ",getppid());

                  printf("helloworld! ");

                  sleep(10);   -> 在这10秒内,子进程继父就是祖先进程。

                  printf("appletree! ");

                         -> 子进程结束,让继父帮子进程回收资源。

           }

           return 0;

    }

    . 进程的退出 

        exit()   -> 查询: man 3 exit

    exit()函数特点:清洗缓冲区的数据,再退出!

    使用格式:

            #include <stdlib.h>

            void exit(int status);

           status: 退出的状态

                   0  -> 正常退出

                  非0 -> 异常退出

           返回值:无。

        _exit()  -> 查询: man 2 _exit

        _Exit()  -> 查询: man 2 _Exit

    _exit()_Exit是一样的,特点就是不清洗缓冲区数据,直接退出!

           #include <unistd.h>

           void _exit(int status);

           #include <stdlib.h>

           void _Exit(int status);

           status: 退出的状态

                   0  -> 正常退出

                  非0 -> 异常退出

           返回值:无。

    1. 缓冲区问题。

    #include <stdlib.h>

    #include <stdio.h>

    #include <unistd.h>

    int main()

    {

           printf("hello");

           exit(0); //输出hello,再退出!

           _exit(0); //不输出hello,直接退出!

          

           printf("world"); //无论什么退出函数,都不会运行到这里。

           return 0;

    }

    2. 进程退出状态。   -> 只要程序中调用exit()/_exit(),都一定会从运行态变成僵尸态。

    #include <stdlib.h>

    #include <stdio.h>

    #include <unistd.h>

    #include <sys/wait.h>

    int main()

    {

           pid_t x;

           int state;

           x = fork();

          

           if(x > 0)

           {

                  wait(&state);

                  printf("state = %d ",state);

           }

          

           if(x == 0)

           {

                  sleep(5);

                  exit(0); //这个exit(0)只是说明子进程退出。

           }

           return 0;

    }

    3. exit()return区别?

    举例子。

    void fun()

    {

           //return;  -> 效果:就会打印hello

           exit(0);   -> 效果:不会打印hello

    }

    int main()

    {

           fun();

           printf("hello! ");

           return 0;

    }

    结论:

    return  -> 只是代表函数的结束,返回到函数调用的地方。
    exit()  -> 代表整个进程的结束,无论当前执行到哪一行代码,只要遇到exit(),这个进程就会马上结束!

    . 从内存角度分析父子进程资源问题。

    举例子。

    #include <stdlib.h>

    #include <stdio.h>

    #include <unistd.h>

    #include <sys/wait.h>

     

    int main(int argc,char *argv[])

    {

           int a = 100;

           pid_t x;

           x = fork();

          

           if(x > 0)

           {

                  //int a = 100;

                  sleep(1);

                  printf("parent a = %d ",a); //100

           }

          

           if(x == 0)

           {

                  a = 50;

                  printf("child a = %d ",a); //50

           }

          

           return 0;

    }

     

    结论:

    1fork()之后,父进程会复制一份几乎与父进程一模一样的资源给子进程(PID号除外)

    2)父子进程拥有独立的空间,在其中一个进程中修改数据,不会影响到另外一个进程的数据。

    . exec函数族接口。 -> man 3 execl

        #include <unistd.h>

      int execl(const char *path, const char *arg, ...);

       path: 需要执行的那个程序的名字,例如: /home/gec/project

       arg: 需要运行时传递的参数,例如: "project","aaa",NULL

      返回值:

           成功: 非-1

           失败: -1

      例子: 产生一个子进程,让子进程执行"ls -l"这个程序。

    #include <stdlib.h>

    #include <stdio.h>

    #include <unistd.h>

    #include <sys/wait.h>

     

    int main(int argc,char *argv[])

    {

           pid_t x;

          

           x = fork();

           if(x > 0)

           {

                  sleep(2);

                  printf("I am parent! ");

           }

          

           if(x == 0)

           {

                  printf("apple tree! ");

                  execl("/bin/ls","ls","-l",NULL);

                  printf("helloworld! ");  -> exec函数族替换掉一个程序,子进程之后的代码都不会执行。

           }

          

          

           return 0;

    }

     

    补充:在GEC6818平台播放mp3歌曲命令

    [root@GEC6818 /]#madplay jay.mp3

    MPEG Audio Decoder 0.15.2 (beta) - Copyright (C) 2000-2004 Robert Leslie et al.

              Title: ····

             Artist: ···

              Album: ········

  • 相关阅读:
    嵌入式开发-基础理论笔记
    如果有,说明你正在堕落!
    不愿意主动联系别人的6大理由
    WCF 服务的ABC之契约(七)
    WCF 服务的ABC之绑定(六)
    WCF 服务的ABC之地址(五)
    WCF 服务与终结点(四)
    WCF 配置文件(三)
    [转载][新手教程] 【小白教程】第一期:谁动了我的Grub?——Grub 修复
    [转载]反无人机企业DroneShield利用声音识别侦测无人机
  • 原文地址:https://www.cnblogs.com/zjlbk/p/11322760.html
Copyright © 2011-2022 走看看