zoukankan      html  css  js  c++  java
  • 进程基本-进程创建,僵尸进程,exec系列函数

    Linux系统中,进程的执行模式划分为用户模式内核模式,当进程运行于用户空间时属于用户模式,
    如果在用户程序运行过程中出现系统调用或者发生中断事件,就要运行操作系统(即核心)程序,进程的运行模式就变为内核模式
    在该模式下运行的进程可以执行机器特权指令,而且该进程的运行不受用户的干预

    在Linux操作系统中,通过fork()系统调用来创建子进程

    目标

    创建进程

    头文件

    #include <unistd.h>

    函数原型

    pid_t_result=fork(void)

    参数

    返回值

    -1  如果出错

    0  返回到子进程

    pid 将子进程的进程id返回给父进程

    将执行fork的操作进程称为父进程,被fork()创建的进程称为该进程的子进程,
    当父进程执行fork操作时,操作系统内核执行如下任务完成进程的创建工作:
    1.为子进程分配新的内存块和内核数据结构
    2.复制原来的进程信息到新的进程,即子进程和父进程具有相同的可执行代码
    3.向运行进程集添加新的进程
    4.fork执行结束后,将控制返回给2个进程,此时两个进程可独立执行,执行顺序取决于进程调度

    #include <stdio.h>
    main(){
        int ret_from_fork,mypid;
        mypid=getpid();
        printf("Before :my pid is %d
    ",mypid );
        ret_from_fork=fork();
        sleep(1);
        printf("After:my pid is %5d,ret_from_fork:%5d,parent pid :%5d
    ",getpid(),ret_from_fork,getppid());
    }
    [root@centos1 process]# ./fork
    Before :my pid is 22526
    After:my pid is 22526,ret_from_fork:22527,parent pid :21968
    After:my pid is 22527,ret_from_fork:    0,parent pid :22526

    父进程创建子进程后,子进程除了具有相同的代码段拷贝外,也具有相同的数据段,
    即父进程的全局变量名称和值子进程也会一起拷贝过去,但他们之间是独立的,可独立改变相互不受影响

    #include <sys/types.h>  //pid_t 类型的在此
    #include <unistd.h>
    #include <stdio.h>
    #include <stdlib.h>   //exit 用
    
    int glob=6;  //全局变量
    
    int main(){
        int local;
        pid_t pid;
        local = 8;
        if( (pid=fork()) == 0){
            sleep(2);
        }else if(pid>0){
            glob =60;
            local =80;
            sleep(10);
        }
        printf("glob=%d,local=%d,mypid=%d
    ",glob,local,getpid());
        exit (0);
    }
    [root@centos1 process]# ./var
    glob=6,local=8,mypid=22800
    glob=60,local=80,mypid=22799
    
    第一行输出为子进程,第二行未父进程的输出,说明父子进程由相同的代码,但他们变量之间相互不影响

    一般情况下,父进程创建子进程是为了执行特定的任务,通过执行exec家族系列调用让子进程执行新的任务
    此时,由exec调用提供的命令的指令代码替换子进程的代码,相当于对子进程进行了换脑

    int execl(const char*path,const char* arg0,const char* arg1,.... NULL)
    int execlp(const char*path,const char* arg0,const char* arg1,.... NULL)
    int execv(const char*path,const char* argv[])
    int execvp(const char*path,const char* argv[])

    目标

    在指定路径中查找并执行一个文件

    头文件

    #include <unistd.h>

    函数原型

    result=execvp(const *file,const char *argv[])

    参数

    file 要执行的文件名

    argv 字符串数组

    返回值

    -1 如果出错

    若成功,execvp 没有返回值

    excel,excelp完全相同
    excev,execvp 完全相同
    excel,excev要求提供可执行文件的绝对路径或相对路径名
    带p的 使用$PATH环境变量查找程序
    主要区别是:
    1.可执行文件的查找方式:不是P结尾的都是完整的目录路径,p结尾的可只给出文件名,系统自动从环境变量中进行查找
    2.参数的传递方式:
    有2种方式,逐个列举;将所有的参数整体构造指针数组传递,以函数名第5位字母来区分
    l(list)的表示逐个列举方式 ,语法为char *arg;
    字母为v(vertor)的表示将所有参数整体构造指针数组传递,语法为: *const argv[]
    exec系列调用没找到和执行文件返回-1 ,否者进程用可执行文件替换它的代码、数据和堆栈
    过程:
    1.将制定的程序复制到它的进程
    2.用制定的字符串数组作为argvp[]传给这个程序
    3.运行这个程序

    #include <unistd.h>
    #include <stdio.h>
    #include <stdlib.h> 
    
    main(){
        char *arglist[1];
        pid_t pid;
        arglist[0]="-l";
        arglist[1]="-a";
        printf("parent:%d
    ",getpid());
        pid=fork();
        if( pid==0 ){
            printf("son1  pid is:%d
    ",getpid() );
            execvp("ls",arglist);
            printf("son2  pid is:%d
    ",getpid() );
        }else{
            printf("parnet pid:%d
    ",getpid() );
        }
        printf("in %d
    ",getpid());
        
    }
    /**
    execvp 后的当前进程的代码不再执行
    root@centos1 c]# ./exec
    parent:26307
    parnet pid:26307
    in 26307
    son1  pid is:26308
    [root@centos1 c]# .  ..  exec  exec.c  fork.c  var.c
    
    **/

     僵尸进程

    当父进程还没有结束而子进程结束运行,同时父进程没有使用wait系统调用获取子进程的结束状态时
    子进程就成为僵尸进程
    父进程先结束不会产生僵尸进程。
    僵尸进程没有任何代码、数据和堆栈,占用不了多少资源,但它存在于系统的任务列表中。在进程表里仍占了1个位置占用进程号
    一般要避免出现这种情况
    当使用ps查看进程时 如果进程名称旁边出现defunct,则表明该进程为僵尸进程

    #include <stdio.h>
    #include <unistd.h>
    
    void parent_code(int delay){
        sleep(delay);
    }
    main(){
        pid_t pid;
        int status;
    
        pid=fork();
        if(pid == 0){
    
        }
        if(pid>0){
            parent_code(20);
        }
    }
    /*
    [root@centos1 c]# gcc -o zombie zombie.c 
    [root@centos1 c]# ./zombie&
    [1] 28577
    [root@centos1 c]# ps
       PID TTY          TIME CMD
     25201 pts/0    00:00:00 bash
     28577 pts/0    00:00:00 zombie
     28578 pts/0    00:00:00 zombie <defunct>
     28580 pts/0    00:00:00 ps
     */

    为了防止子进程成为僵尸进程,一般在父进程调用wait()系统调用等待子进程的结束并获取子进程的返回状态
    wait调用做2件事:
    首先暂停调用它的进程直到子进程结束,然后取得子进程结束时传给exit的值

    目标

    等待子进程的结束

    头文件

    #include <sys/wait.h>

    #include <sys/types.h>

    函数原型

    pid_t pid=wait(int *status)

    参数

    status指向一个保存子进程返回状态的整型变量

    返回值

    如果不存在子进程,返回-1

    若有任何一个子进程结束,则返回该子进程的pid并保存其返回状态在status中,同时wait调用也结束

    #include <sys/wait.h>
    #include <sys/types.h>
    #include <unistd.h>
    #include <stdio.h>
    #include <stdlib.h>
    
    void child(int delay){
        sleep(delay);
        exit(0);
    }
    
    void parent(int *status){
        wait(status);
    }
    
    main(){
        pid_t pid;
        int status;
        printf("Before:%d
    ",getpid());
        pid=fork();
        if(pid == 0){
            child(10);
        }
        if(pid >0 ){
            printf("pid =%d
    ",getpid());
            parent(&status);
            printf("status =%d
    ",status);
        }
    }
    /*
    [root@centos1 c]# ./a.out
    Before:14901
    pid =14901
    status =0
    
    */

    上述代码可防止僵尸进程的产生
    waitpid()亦可实现wait()调用类似功能
    多数情况下会使用waitpid()
    他们的主要区别是:
    1.wait()只能得到任何一个子进程结束的状态,当一个父进程有多个子进程时,在某个子进程结束,则wait可得到其结束状态
    此时无法再得到其它子进程的结束状态,此时容易产生其它子进程的僵尸进程
    2.wait()调用属于阻塞调用,父进程执行该指令后,器等待子进程结束之后才能执行它后面的代码,
    而waitpid()可提供非阻塞调用的方式
    3.waitpid()调用可以等待指定的子进程具有比wait多的功能

    目标

    等待某个子进程的结束

    头文件

    #include <sys/wait.h>

    #include <sys/types.h>

    函数原型

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

    参数

    pid=-1 等待任一个子进程.wait等效

    pid>0 等待进程idpid的子进程

    pid=0 等待其组id等于调用进程组id的任一子进程

    pid<-1 等待其组id等于pid绝对值的任一进程

    options选项:

    WNOHANG 表示如果没有任何已经结束的子进程则马上返回,不等待

    WUNTRACED 如果子进程进入暂停执行情况则马上返回,但结束状态不予以理会

    0 作用和wait一样,阻塞父进程,等待子进程结束

     

     

    返回值

    如果不存在子进程,返回-1

    若有指定子进程结束,则返回该子进程的pid并保存其返回状态在status,同时waitpid调用也结束

    子进程退出状态检测的宏

    说明

    WIFEXITED(status)

    如果子进程正常结束,则为非0,此时可调用WEXITSTATUS(status)

    取得子进程exit()返回的结束代码

    WIFSIGNALED(status)

    如果子进程是因为信号则此宏的值为真,

    此时可用WIERMSIG(status)取得子进程因信号而终止的信号代码

    WIFSTOPPED(status)

    如果子进程处于暂停执行情况则此宏为真.采用WSTOPPSIG(status)

    取得引发子进程暂停的信号代码

  • 相关阅读:
    1093 Count PAT's(25 分)
    1089 Insert or Merge(25 分)
    1088 Rational Arithmetic(20 分)
    1081 Rational Sum(20 分)
    1069 The Black Hole of Numbers(20 分)
    1059 Prime Factors(25 分)
    1050 String Subtraction (20)
    根据生日计算员工年龄
    动态获取当前日期和时间
    对计数结果进行4舍5入
  • 原文地址:https://www.cnblogs.com/HKUI/p/9030047.html
Copyright © 2011-2022 走看看