进程号为1的进程是所有进程的主先进程init
如何查看进程:ps uax
结果:
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,就象下面这样
如果成功,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,这个值就毫无意义。
/*
|
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 来判断后才使用此宏。 |
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 |