僵尸进程,孤儿进程,wait,exit,execl等函数使用要点

进程号为1的进程是所有进程的主先进程init

如何查看进程:ps uax

#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

int main()
{
    pid_t pid;
    int a = 8;
    printf("current process id:%d\n",getpid());
    pid = fork();
    if( pid > 0 )//父进程所做之事
    {
        printf("a = %d\n",a);
        printf(" pid = %d\n",pid);//子进程
        printf(" ppid = %d\n",getpid());//父进程
        //while(1);
    }else if( pid == 0 )//子进程所做之万
    {
        a++;
        printf("aa = %d\n",a);
        printf("child pid = %d\n",getpid());//子进程
        printf("parent pid = %d\n",getppid());//父进程
        //while(1);
    }else{
        printf("fork error!\n");
    }

    exit(0);
}
/*
 *    fork函数:
 *    1.fork函数用于创建子进程。子进程
 *    继承父进程之前运行的一切状态,并
 *    且拷贝父进程的地址空间,成为父
 *    进程的一个副本。子进程从fork函数
 *    的下一句话开始运行。
 *    2.fork函数不能保证父进程和子进程
 *    哪个先运行。
 *    这里相当于两个进程在执行,所以出现的结果是六个
 */

结果:

current process id:1995
a = 8
 pid = 1996
 ppid = 1995
aa = 9
child pid = 1996
parent pid = 1995


僵尸进程的产生:

/*
 ============================================================================
 Name        : jianshiprocess.c
 Author      :
 Version     :
 Copyright   : Your copyright notice
 Description : Hello World in C, Ansi-style
 ============================================================================
 */
//长生僵尸进程
#include <stdio.h>
#include <stdlib.h>
#include<unistd.h>
#include<sys/types.h>
//僵尸进程:
//子进程在父进程前退出,此时子进程成为僵尸进程。
//孤儿进程:
//父进程在子进程之前退出,此时子进程成为孤儿进程,此子进程由init进程零养,(也就是说init进程成为子进程的父进程,init进程是所有孤儿进程的父进程)
//着两种都是要避免的


int main(void)
{
    pid_t pid;
    pid=fork();
    if(pid>0)
    {
        sleep(10);//让父进程等在这里,之后被唤醒
    }
    else if(pid==0)
    {
        printf("child%d",getpid());
        printf("p%d",getppid());
        exit(0);//成为僵尸进程后,占用进程好
    }
    else
    {
        puts("wrong");
        exit(0);
    }
    return EXIT_SUCCESS;
}

孤儿进程:


/*
 ============================================================================
 Name        : guerprocess.c
 Author      :
 Version     :
 Copyright   : Your copyright notice
 Description : Hello World in C, Ansi-style
 ============================================================================
 */

#include <stdio.h>
#include <stdlib.h>
#include<unistd.h>
#include<sys/types.h>
int main(void) {
    int i;
    pid_t pid;
    printf("current process id:%d\n",getpid());
    pid=fork();

    if(pid>0)
    {
        exit(0);//成为僵尸进程后,占用进程好
    }
    else if(pid==0)
    {
        for(i=0;i<20;i++)
        {
            printf("child:%d\n",getpid());
            printf("parent:%d\n",getppid());
        }
    }
    else
    {
        puts("wrong");
        exit(0);
    }
    return EXIT_SUCCESS;
}


奇怪的现象:如果在这个进程的执行过程中,按ctrl+c阻止程序的进行,会发现阻止不了,因为此时的子进程成了孤儿进程,成了主进程的守护进程,因为它的父进程是init进程,而ctrl+c只能阻止前台运行的程序



2:

return 与exit的区别:

return 用于函数返回,只有main函数调用时,进程才退出

而exit函数被调用,此进程直接退出


exit与_exit的相同点:

都是用于进程退出。

不同点:

exit函数在进程退出之前,会刷新文件缓冲区到文件中。

_exit在进程退出之前,不会刷新缓冲区,也就说直接丢弃缓冲区里面的内容


fork与vfork

执行fork创建子进程时,子进程将拷贝父进程的数据段、堆段和栈段,此时父子进程的数据一致当互相独立,各不影响,子进程对数据的处理不影响父进程。(其实当父子进程都不对三个段进行写操作时,父子进程仍然共享数据段、堆段和栈段,当有任何一方有写操作时,才产生副本,这种方式成为写是复制)

vfork与fork区别有两点:1、vfork创建的子进程与父进程共享数据段,2、vfork创建的子进程优先于父进程执行,当子进程执行时,父进程进入阻塞状态。

所以vfork产生的子进程对数据的修改一定可以影响到父进程的数据

 

exit与_exit

exit:当执行exit时,终止处理程序,执行标准的I/O清楚操作(将缓存中文件写入),调用atexit,调用_exit。由此可见,exit是加强版的_exit.

_exit:通知内核,进程结束

代码区别:

printf("hello");

exit(0);//

结果:输出hello,刷新缓冲区

printf("hello");

_exit(0)

结果,不输出,直接丢弃缓冲区里面的内容

3.wait函数:

wait(等待子进程中断或结束)
相关函数
waitpid,fork
表头文件
#include<sys/types.h>
#include<sys/wait.h>
定义函数
pid_t wait (int * status);
函数说明
wait()会暂时停止目前进程的执行,直到有信号来到或子进程结束。如果在调用wait()时子进程已经结束,则wait()会立即返回子进程结束状态值。子进程的结束状态值会由参数status 返回,而子进程的进程识别码也会一快返回。如果不在意结束状态值,则
参数
status可以设成NULL。子进程的结束状态值请参考waitpid()。
返回值
如果执行成功则返回子进程识别码(PID),如果有错误发生则返回-1。失败原因存于errno中。
使用时注意的几个地方:

1.调用到wait函数的进程直接阻塞

2.一般由父进程调用,用于回收子进程退出时,其所占有的资源,避免其成为僵尸进程

3.如果一个进程调用到此函数,且其没有子进程,wait函数直接返回

4.处理SIGCHL信号(当子进程状态改变时,内核会向其父进程发送SIGCHL信号,用户进程对于此信号的默认处理方式为忽略)

5.调用到wait函数进程的任何一个子进程退出,此函数返回。(或者搜到SIGCHL函数则也退出)

6.获取子进程的状态值wait(&status)

如:exit(7),7是整形常量,占32位,取整形常量的低八位,放在status的32位的高八位,如果把status里面的值右移八位,就又变成7(status是个整形变量),因为它的低八位要被其它信号量占用.(exit里面的值只能是0-255)

一个wait只能回收一个子进程的空间

7.无法确定回收某个指定的子进程



j进程一旦调用了wait,就立即阻塞自己,由wait自动分析是否当前进程的某个子进程已经退出,如果让它找到了这样一个已经变成僵尸的子进程,wait就会收集这个子进程的信息,并把它彻底销毁后返回;如果没有找到这样一个子进程,wait就会一直阻塞在这里,直到有一个出现为止。

参数status用来保存被收集进程退出时的一些状态,它是一个指向int类型的指针。但如果我们对这个子进程是如何死掉的毫不在意,只想把这个僵尸进程消灭掉,(事实上绝大多数情况下,我们都会这样想),我们就可以设定这个参数为NULL,就象下面这样

  1. pid = wait(NULL);  

如果成功,wait会返回被收集的子进程的进程ID,如果调用进程没有子进程,调用就会失败,此时wait返回-1,同时errno被置为ECHILD。

如果参数status的值不是NULL,wait就会把子进程退出时的状态取出并存入其中,这是一个整数值(int),指出了子进程是正常退出还是被非正常结束的(一个进程也可以被其他进程用信号结束,我们将在以后的文章中介绍),以及正常结束时的返回值,或被哪一个信号结束的等信息。由于这些信息被存放在一个整数的不同二进制位中,所以用常规的方法读取会非常麻烦,人们就设计了一套专门的宏(macro)来完成这项工作,下面我们来学习一下其中最常用的两个:

1,WIFEXITED(status)这个宏用来指出子进程是否为正常退出的,如果是,它会返回一个非零值。

(请注意,虽然名字一样,这里的参数status并不同于wait唯一的参数--指向整数的指针status,而是那个指针所指向的整数,切记不要搞混了。)

2, WEXITSTATUS(status)当WIFEXITED返回非零值时,我们可以用这个宏来提取子进程的返回值,如果子进程调用exit(5)退出,WEXITSTATUS(status)就会返回5;如果子进程调用exit(7),WEXITSTATUS(status)就会返回7。请注意,如果进程不是正常退出的,也就是说,WIFEXITED返回0,这个值就毫无意义。

/*
 ============================================================================
 Name        : wait.c
 Author      :
 Version     :
 Copyright   : Your copyright notice
 Description : Hello World in C, Ansi-style
 ============================================================================
 */
//多用于父进程和多进程中
#include <stdio.h>
#include <stdlib.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<unistd.h>

int main(void) {
    pid_t pid;
    int status;
    pid=fork();
    if(pid>0)
    {
        wait(&status);
        printf("this is a parent process\n");
        if(WIFEXITED(status))
        {
            printf("status is %d \n",WEXITSTATUS(status));
        }
        exit(0);
    }
    else if(pid==0)
    {
        printf("this is a child process!\n");
        exit(7);
    }
    else
    {
        printf("fork error!\n");
        exit(1);
    }
    return EXIT_SUCCESS;
}
4.wait_pid



waitpid(等待子进程中断或结束)
相关函数
wait,fork
表头文件
#include<sys/types.h>
#include<sys/wait.h>
定义函数
pid_t waitpid(pid_t pid,int * status,int options);
函数说明
waitpid()会暂时停止目前进程的执行,直到有信号来到或子进程结束。如果在调用wait()时子进程已经结束,则wait()会立即返回子进程结束状态值。子进程的结束状态值会由参数status返回,而子进程的进程识别码也会一快返回。如果不在意结束状态值,则参数status可以设成NULL。参数pid为欲等待的子进程识别码,其他数值意义如下:
pid<-1 等待进程组识别码为pid绝对值的任何子进程。
pid=-1 等待任何子进程,相当于wait()。
pid=0 等待进程组识别码与目前进程相同的任何子进程。
pid>0 等待任何子进程识别码为pid的子进程。
参数option可以为0 或下面的OR 组合
WNOHANG 如果没有任何已经结束的子进程则马上返回,不予以等待。
WUNTRACED 如果子进程进入暂停执行情况则马上返回,但结束状态不予以理会。
子进程的结束状态返回后存于status,底下有几个宏可判别结束情况
WIFEXITED(status)如果子进程正常结束则为非0值。
WEXITSTATUS(status)取得子进程exit()返回的结束代码,一般会先用WIFEXITED 来判断是否正常结束才能使用此宏。
WIFSIGNALED(status)如果子进程是因为信号而结束则此宏值为真
WTERMSIG(status)取得子进程因信号而中止的信号代码,一般会先用WIFSIGNALED 来判断后才使用此宏。
WIFSTOPPED(status)如果子进程处于暂停执行情况则此宏值为真。一般只有使用WUNTRACED 时才会有此情况。
WSTOPSIG(status)取得引发子进程暂停的信号代码,一般会先用WIFSTOPPED 来判断后才使用此宏。
           wait_pid(pid,&status,0);//等待指定的子进程,回收指定的子进程

execl:



execl(执行文件)
相关函数
fork,execle,execlp,execv,execve,execvp
表头文件
#include<unistd.h>
定义函数
int execl(const char * path,const char * arg,....);
函数说明
execl()用来执行参数path字符串所代表的文件路径,接下来的参数代表执行该文件时传递过去的argv(0)、argv[1]……,最后一个参数必须用空指针(NULL)作结束。
返回值
如果执行成功则函数不会返回,执行失败则直接返回-1,失败原因存于errno中。
范例
#include<unistd.h>
main()
{
execl(“/bin/ls”,”ls”,”-al”,”/etc/passwd”,(char * )0);
}
执行
/*执行/bin/ls -al /etc/passwd */
-rw-r--r-- 1 root root 705 Sep 3 13 :52 /etc/passwd
执行之后的语句很有可能被擦除掉,而之前的还在

你可能感兴趣的:(僵尸进程,孤儿进程,wait,exit,execl等函数使用要点)