zoukankan      html  css  js  c++  java
  • 进程控制编程


    进程的定义
    进程是一个具有一定独立功能的程序的一次运行活动,同时也是资源分配的最小单元;

    进程与程序
    程序是放到磁盘的可执行文件
    进程是指程序执行的实例
    进程是动态的,程序是静态的:程序是有序代码的集合;进程是程序的执行。通常进程不可在计算机之间迁移;而程序通常对应着文件、静态和可以复制
    进程是暂时的,程序使长久的:进程是一个状态变化的过程,程序可长久保存
    进程与程序组成不同:进程的组成包括程序、数据和进程控制块(即进程状态信息)
    进程与程序的对应关系:通过多次执行,一个程序可对应多个进程;通过调用关系,一个进程可包括多个程序。

    进程的生命周期
    创建:每个进程都是由其父进程创建,进程可以创建子进程,子进程又可以创建子进程的子进程
    运行:多个进程可以同时存在,进程间可以通信
    撤销:进程可以被撤销,从而结束一个进程的运行

    进程的状态
    执行状态:进程正在占用CPU
    就绪状态:进程已具备一切条件,正在等待分配CPU的处理时间片
    等待状态:进程不能使用CPU,若等待事件发生则可将其唤醒

    Linux进程
    Linux系统是一个多进程的系统,它的进程之间具有并行性、互不干扰等特点。
    也就是说,每个进程都是一个独立的运行单位,拥有各自的权利和责任。其中,各个进程都运行在独立的虚拟地址空间,因此,即使一个进程发生异常,它也不会影响到系统中的其他进程。

    Linux下进程地址空间
    Linux中的进程包含3个段,分别为“数据段”、“代码段”和“堆栈段”。
    “数据段”存放的是全局变量、常数以及动态数据分配的数据空间;
    “代码段”存放的是程序代码的数据。
    “堆栈段”存放的是子程序的返回地址、子程序的参数以及程序的局部变量等。

    进程ID
    进程ID(PID):标识进程的唯一数字
    父进程的ID(PPID)
    启动进程的用户ID(UID)

    进程互斥
    进程互斥是指当有若干进程都要使用某一共享资源时,任何时刻最多允许一个进程使用,其他要使用该资源的进程必须等待,直到占用该资源者释放了该资源为止

    临界资源
    操作系统中将一次只允许一个进程访问的资源称为临界资源

    临界区
    进程中访问临界资源的那段程序代码称为临界区,为实现对临界资源的互斥访问,应保证诸进程互斥地进入各自的临界区

    进程同步
       一组并发进程按一定的顺序执行的过程称为进程间的同步
       具有同步关系一组并发进程称为合作进程,
      合作进程间互相发送的信号称为消息或事件

    进程调度
    概念:
    按一定算法,从一组待运行的进程中选出一个来占有CPU运行。
    调度方式:
     抢占式
     非抢占式

    调度算法
    先来先服务调度算法
    短进程优先调度算法
    高优先级优先调度算法
    时间片轮转法

    死锁
    多个进程因竞争资源而形成一种僵局,若无外力作用,这些进程都将永远不能再向前推进

    获取ID
    #include <sys/types.h>
    #include <unistd.h>
    pid_t getpid(void)     获取本进程ID。
    pid_t getppid(void)        获取父进程ID

    例:getpid.c (演示)

    #include <stdio.h>
    #include <unistd.h>
    #include <stdlib.h>
    int main(void)
    {
       printf( "PID = %d
    ", getpid() );
       printf( “PPID = %d
    ”, getppid() );
       return 0;
    }

    进程创建
    #include <unistd.h>
    pid_t fork(void)
    功能:创建子进程
          fork的奇妙之处在于它被调用一次,却返回两次,它可能有三种不同的返回值:
    返回值:
    0:  子进程
    子进程ID(大于0):父进程
    -1: 出错

    #include <sys/types.h>
    #include <unistd.h>
    int main()
     {
      pid_t pid;
      /*此时仅有一个进程*/
      pid=fork();
      /*此时已经有两个进程在同时运行*/
      if(pid<0)
       printf("error in fork!");
      else if(pid==0)
       printf("I am the child process, ID is %d
    ",getpid());
      else
       printf("I am the parent process,ID is %d
    ",getpid());
    }

    进程创建
    在pid=fork()之前,只有一个进程在执行,但在这条语句执行之后,就变成两个进程在执行了,这两个进程的共享代码段,将要执行的下一条语句都是if(pid==0).
    两个进程中,原来就存在的那个进程被称作“父进程”,新出现的那个进程被称作“子进程”,父子进程的区别在于进程标识符(PID)不同.

    #include <unistd.h>
    #include <stdio.h>
    int main(void)
    {
           pid_t pid;
         int count=0;
            pid = fork();
         count++;
         printf( “count = %d
    ", count );
         return 0;
    }


    输出:
       count = 1
       count = 1
      
    子进程的数据空间、堆栈空间都会从父进程得到一个拷贝,而不是共享。
    在子进程中对count进行加1的操作,并没有影响到父进程中的count值,父进程中的count值仍然为0

    进程创建-vfork
    #include <sys/types.h>
    #include <unistd.h>
    pid_t vfork(void)
    功能:创建子进程

    vfork(建立一个新的进程)
    表头文件: #include<unistd.h>定义函数: pid_t vfork(void);函数说明:
    vfork()会产生一个新的子进程,其子进程会复制父进程的数据与堆栈空间,并继承父进程的用户代码,组代码,环境变量、已打开的文件代码、工作目录和资源限制等。
    子进程不会继承父进程的文件锁定和未处理的信号。
    注意,Linux不保证子进程会比父进程先执行或晚执行,因此编写程序时要留意死锁或竞争条件的发生。

    #include <unistd.h>
    #include <stdio.h>
    int main(void)
    {
        pid_t pid;
        int count=0;
        pid = vfork();
        count++;  
        printf( “count = %d
    ", count ); 
        return 0;
    }


    区别:
    1.fork:子进程拷贝父进程的数据段
      vfork:子进程与父进程共享数据段
    2.fork:父、子进程的执行次序不确定
      vfork:子进程先运行,父进程后运行

    exec函数族
    exec用被执行的程序替换调用它的程序。
    区别:
       fork创建一个新的进程,产生一个新的PID。
       exec启动一个新程序,替换原有的进程,因此进程的PID不会改变
    #include<unistd.h>
    int execl(const char * path,const char * arg1, ...)
    参数:
    path:被执行程序名(含完整路径)。
    arg1 – argn: 被执行程序所需的命令行参数,含程序名。以空指针(NULL)结束。
    例:execl.c (演示)

      #include<unistd.h>
      int main()
     {
       execl(“/bin/ls”,”ls”, “-al”,”/etc/passwd”,NULL);
     }


    #include<unistd.h>
    int execv (const char * path, char * const argv[ ])
    参数:
       path:被执行程序名(含完整路径)。
     argv[]: 被执行程序所需的命令行参数数组。
    例:execv.c (演示)

    #include <unistd.h>
    int main()
    {
       char * argv[ ]={“ls”,”-l”,”/etc/passwd”,(char*)0}; execv(“/bin/ls”,argv);
    }


    #include <stdlib.h>
          int system( const char* string )
    功能:
       调用fork产生子进程,由子进程来调用/bin/sh -c string来执行参数string所代表的命令
    例:system.c (演示)

    #include <stdlib.h>
    int main()
    {
      system(“ls -al /etc/passwd”);
    }

    进程等待
    #include <sys/types.h>
    #include <sys/wait.h>
    pid_t wait (int * status)
    功能:阻塞该进程,直到其某个子进程退出。

    #include <sys/types.h>
    #include <sys/wait.h>
    #include <unistd.h>
    #include <stdlib.h>
    int main()
    {   pid_t pc,pr;
         pc = fork();
         if(pc == 0){
          printf(“This is child process with pid of %d
    ”,getpid());
          sleep(10); /* 睡眠10秒钟 */
          }
          else if(pc > 0){
           pr=wait(NULL); /* 等待 */
           printf("I catched a child process with pid of %d
    "),pr);
           }
           exit(0);
    }

    #include <sys/types.h>
    #include <sys/wait.h>
    pid_t waitpid (pid_t pid, int * status, int options)
    功能:
    会暂时停止目前进程的执行,直到有信号来到或子进程结束
    参数:如果不在意结束状态值,则参数status可以设成NULL。
    参数pid为欲等待的子进程识别码:pid<-1 等待进程组识别码为pid绝对值的任何子进程。pid=-1 等待任何子进程,相当于wait()。pid=0 等待进程组识别码与目前进程相同的任何子进程。pid>0 等待任何子进程识别码为pid的子进程。
    参数option可以为0 或下面的OR 组合WNOHANG:如果没有任何已经结束的子进程则马上返回,不予以等待。WUNTRACED :如果子进程进入暂停执行情况则马上返回,但结束状态不予以理会。返回值:
    如果执行成功则返回子进程识别码(PID),如果有错误发生则返回-1。失败原因存于errno中。

    进程退出
    exit,_exit用于终止进程
    区别:
    _exit: 直接使进程停止,清除其使用的内存,并清除缓冲区中内容
    exit与 _exit的区别:在停止进程之前,要检查文件的打开情况,并把文件缓冲区中的内容写回文件才停止进程。

    僵尸进程
    僵尸进程指的是那些虽然已经终止的进程,但仍然保留一些信息,等待其父进程为其收尸。
    如何产生?
    如果一个进程在其终止的时候,自己就回收所有分配给它的资源,系统就不会产生所谓的僵尸进程了

    僵尸进程产生的过程:
    1. 父进程调用fork创建子进程后,子进程运行直至其终止,它立即从内存中移除,但进程描述符仍然保留在内存中(进程描述符占有极少的内存空间)。
    2. 子进程的状态变成EXIT_ZOMBIE,并且向父进程发送SIGCHLD 信号,父进程此时应该调用 wait() 系统调用来获取子进程的退出状态以及其它的信息。在 wait 调用之后,僵尸进程就完全从内存中移除。
    3. 因此一个僵尸存在于其终止到父进程调用 wait 等函数这个时间的间隙,一般很快就消失,但如果编程不合理,父进程从不调用 wait 等系统调用来收集僵尸进程,那么这些进程会一直存在内存中。

    exit(正常结束进程)
    表头文件: #include<stdlib.h> 定义函数: void exit(int status); 函数说明:
    exit()用来正常终结目前进程的执行,并把参数status返回给父进程,而进程所有的缓冲区数据会自动写回并关闭未关闭的文件。

    _exit(结束进程执行)
    表头文件: #include<unistd.h>
    定义函数: void _exit(int status);
    函数说明
     _exit()用来立刻结束目前进程的执行,并把参数status返回给父进程,并关闭未关闭的文件。
     此函数调用后不会返回,并且会传递SIGCHLD信号给父进程,父进程可以由wait

  • 相关阅读:
    1063. Set Similarity
    A1047. Student List for Course
    A1039. Course List for Student
    最大公约数、素数、分数运算、超长整数计算总结
    A1024. Palindromic Number
    A1023. Have Fun with Numbers
    A1059. Prime Factors
    A1096. Consecutive Factors
    A1078. Hashing
    A1015. Reversible Primes
  • 原文地址:https://www.cnblogs.com/kutoli/p/7994797.html
Copyright © 2011-2022 走看看