教材学习内容总结
fork()函数:
一个进程,包括代码、数据和分配给进程的资源。fork()函数通过系统调用创建一个与原来进程几乎完全相同的进程,也就是两个进程可以做完全相同的事,但如果初始参数或者传入的变量不同,两个进程也可以做不同的事。 一个进程调用fork()函数后,系统先给新的进程分配资源,例如存储数据和代码的空间。然后把原来的进程的所有值都复制到新的新进程中,只有少数值与原来的进程的值不同。fork()函数从已存在的进程中创建一个新进程。父进程→子进程,子进程几乎是父进程的复制,其所独有的只有它的进程号、资源使用和计时器等。子进程继承了父进程的整个地址空间,包括进程上下文、代码段、进程堆栈等。 父子进程的一个重要区别是:fork()的返回值不同。父进程中返回子进程的进程号,子进程中返回0。 出错为-1。
代码实例:
#include <unistd.h>
#include <stdio.h>
int main ()
{ pid_t fpid; //fpid表示fork函数返回的值
int count=0;
fpid=fork();
if (fpid < 0)
printf("error in fork!");
else if (fpid == 0) {
printf("i am the child process, my process id is %d/n",getpid());
count++; }
else {
printf("i am the parent process, my process id is %d/n",getpid());
count++; }
printf("统计结果是: %d/n",count);
return 0;
}
/* * fork_test.c * version 1 * Created on: 2010-5-29 * Author: wangth */ #include <unistd.h> #include <stdio.h> int main () { pid_t fpid; //fpid表示fork函数返回的值 int count=0; fpid=fork(); if (fpid < 0) printf("error in fork!"); else if (fpid == 0) { printf("i am the child process, my process id is %d/n",getpid()); printf("我是爹的儿子/n");//对某些人来说中文看着更直白。 count++; } else { printf("i am the parent process, my process id is %d/n",getpid()); printf("我是孩子他爹/n"); count++; } printf("统计结果是: %d/n",count); return 0; }根据以上实例,在语句fpid=fork()之前,只有一个进程在执行这段代码,但在这条语句之后,就变成两个进程在执行了,这两个进程的几乎完全相同,将要执行的下一条语句都是if(fpid<0)等代码。两个进程的fpid之所以不同,这与fork函数的特性有关。fork调用的特点就是它仅仅被调用一次,却能够返回两次,它可能有三种不同的返回值:
1)在父进程中,fork返回新创建子进程的进程ID;
2)在子进程中,fork返回0;
3)如果出现错误,fork返回一个负值;
在fork函数执行完毕后,如果创建新进程成功,则出现两个进程,一个是子进程,一个是父进程。在子进程中,fork函数返回0,在父进程中,fork返回新创建子进程的进程ID。我们可以通过fork返回的值来判断当前进程是子进程还是父进程。
fork出错可能有两种原因:
1)当前的进程数已经达到了系统规定的上限,这时errno的值被设置为EAGAIN。
2)系统内存不足,这时errno的值被设置为ENOMEM。
创建新进程成功后,系统中出现两个基本完全相同的进程,这两个进程执行没有固定的先后顺序,哪个进程先执行要看系统的进程调度策略。
每个进程都有一个独特(互不相同)的进程标识符(process ID),可以通过getpid()函数获得,还有一个记录父进程pid的变量,可以通getppid()函数获得变量的值。
Exec族函数
说是exec系统调用,实际上在Linux中,并不存在一个exec()的函数形式,exec指的是一组函数,一共有6个,分别是:
#include <unistd.h> int execl(const char *path, const char *arg, ...); int execlp(const char *file, const char *arg, ...); int execle(const char *path, const char *arg, ..., char *const envp[]); int execv(const char *path, char *const argv[]); int execvp(const char *file, char *const argv[]); int execve(const char *path, char *const argv[], char *const envp[]);
|
其中只有execve是真正意义上的系统调用,其它都是在此基础上经过包装的库函数。exec函数族的作用是根据指定的文件名找到可执行文件,并用它来取代调用进程的内容,换句话说,就是在调用进程内部执行一个可执行文件。这里的可执行文件既可以是二进制文件,也可以是任何Linux下可执行的脚本文件。与一般情况不同,exec函数族的函数执行成功后不会返回,因为调用进程的实体,包括代码段,数据段和堆栈等都已经被新的内容取代,只有调用失败了,它们才会返回一个-1,从原程序的调用点接着往下执行。fork()函数用于创建一个子进程,该子进程几乎复制了父进程的所有内容,exec函数族提供了一个在进程中执行另外一个程序的方法。在执行完之后,当前进程除了进程号之外,其他的内容都被替换了。 出错时返回-1。
注意一定要写上错误判断语句,常见的错误有: 找不到文件或者路径,此时error被设置为ENOENT;数组argv和envp忘记用NULL结束,此时error被设置为EFAUTL; 没有对应可执行文件的运行权限,此时error被设置为EACCES;
execlp(),以文件名的方式来查找可执行文件,同时使用参数列表的方式。运行结果与在shell中输入命令ps -ef是一样的。 execl(),使用完整的文件目录来查找对应的可执行文件。注意目录必须以/开头,否则将其视为文件名。if((execlp("/bin/ps","ps","-ef",NULL))<0) 这里必须是ps程序的完整路径,注意目录必须以/开头。
execle()函数将环境变量添加到新建的子进程中,这里的env是查看当前进程环境变量的命令。命令参数列表,必须以NULL结尾 char *envp[]={"PATH=/tmp","USER=harry",NULL};if((execle("/usr/bin/env","env",NULL,envp))<0)需要指出env的完整路径
execve()函数,通过构造指针函数数组的方式来传递函数,注意参数列表一定要以NULL作为结束标识符。
终止程序函数exit()和_exit()函数:
_exit()函数:直接使进程停止运行,清除其使用的内存空间,并销毁其在内核中的各种数据结构;_exit()函数的作用是直接使进程停止运行,使文件读写的速度加快;而exit()函数先会“清理I/O缓冲”,若想要保证数据的完整性,最好使用exit()函数。
void _exit(int status); status用于传递进程结束的状态,一般来说,0表示正常结束,其他数值表示非正常结束。 实际编程时,可以用wait()系统调用接收子进程的返回值,从而针对不同的情况进行不同的处理。
exit()函数则在这些基础上作了一些包装,在执行退出之前加了若干道工序。
exit()函数与_exit()函数最大的区别就在于 exit()函数在调用 exit 系统调用之前要检查文件的打开情况,把文件缓冲区中的内容写回文件。
wait()函数和waitpid( )函数:
wait()函数用于使父进程(也就是调用wait()的进程)阻塞,直到一个子进程结束或者该进程接收到了一个指定的信号为止。如果父进程没有子进程或者它的子进程已经结束,则wait()会立即返回-1. wait()会暂时停止目前进程的执行,直到有信号来到或子进程结束。如果在调用wait()时子进程已经结束,则wait()会立即返回子进程结束状态值。子进程的结束状态值会由参数status 返回,而子进程的进程识别码也会一快返回
wait()函数的语法:
#include<sys/types.h>
#include<sys/wait.h>
pid_wait (int *status);
status指向的整型对象用来保存子进程结束时的状态; 若成功则返回回收的子进程的进程号,失败则返回-1;
waitpid()的作用与wait()一样,但它并不一定等待第一个终止的子进程。waitpid()有若干选项,可提供一个非阻塞版本的wait()功能。实际上,wait()只是 waitpid()函数的一个特例,在linux内部实现wait()函数时直接调用的就是waitpid()函数。waitpid()会暂时停止目前进程的执行,直到有信号来到或子进程结束。如果在调用 waitpid()时子进程已经结束,则 waitpid()会立即返回子进程结束状态值。 子进程的结束状态值会由参数 status 返回,而子进程的进程识别码也会一起返回。
waitpid()函数的语法:
#include<sys/types.h>
#include<sys/wait.h>
pid_waitpid( pid_t pid , int *status, int options );
参数说明:pid:
pid>0,回收进程ID等于pid的子进程
pid=-1,回收任何一个子进程,此时和wait()相同
options:
WNOHANG 若指定的子进程没有结束,则waitpid()不阻塞而立即返回,此时返回值为0;
WUNTRACED 为了实现某一个操作,由pid指定的任一子进程已被暂停,且其状态自暂停以来没有报告过,则返回其状态。
返回值:已经结束的子进程的进程号(>0);