fork函数
一个现有进程可以调用fork函数创建一个新进程。由fork创建的新进程被成为子进程(child process)。fork函数被调用一次,但返回两次,两次返回的唯一区别是子进程的返回值是0,而父进程的返回值是新的子进程的进程ID;子进程可以调用getppid以获取其父进程的进程ID。
其中,子进程获得父进程数据空间、堆和栈的副本,但是父、子进程并不共享存储空间部分,而共享正文段。
在fork之后是父进程先执行还是子进程先执行是不确定的,取决于内核所使用的调度算法。如果要求父、子进程之间互相同步,也要求某种形式的进程间通信。如果父进程在子进程之前终止,则会将子进程的父进程改变为int进程(即由init进程领养)。
exec函数
进程待用exec函数执行另一个程序时,该进程执行的程序完全替换为新程序,而新程序从main函数开始执行,但前后的进程ID不改变。exec只是用一个全新的程序替换了当前进程的正文、数据、堆和栈。
exit函数
进程有5种正常终止方式:
(1)在main函数内执行return语句(等效于调用exit)。
(2)调用exit函数,其操作包括调用各终止处理程序,然后关闭所有标准I/O流等。
(3)调用_exit或_Exit函数,即无需进行终止处理程序或信号处理程序而直接终止。
(4)进程的最后一个线程在其启动例程中执行返回语句。但是该线程的返回值不会用做进程的返回值。当最后一个线程从其启动历程返回时,该进程以终止状态0返回。
(5)进程的最后一个线程调用pthred_exit函数。进程终止状态总是0.
进程的3种异常终止方式:
(1)调用abort,产生SIGABRT信号。
(2)当进程接收到某些信号时,信号可由进程自身、其他进程或内核产生。
(3)最后一个线程对“取消”请求做出响应。
不管进程如何终止,最后都会为相应进程关闭所有打开描述符,释放它所使用的存储器等。
对于三个终止函数(exit、_exit和_Exit),实现这个的方法是,将其退出状态作为参数传递给函数,而在最后调用_exit时,内核将退出状态转换成终止状态。在异常终止情况下,内核产生一个指示其异常终止原因的终止状态。在任意情况下,该终止进程的父进程都能用wait或waitpid函数取得其终止状态。
子进程在父进程之前终止时,内核为每个终止子进程保存了一定量的信息,父进程调用wait或waitpid时,可以得到这个信息(信息至少包括进程ID、终止状态、使用的CPU时间总量)。内核可以释放终止进程所使用的所有存储区,关闭所有其打开文件。一个已经终止、但是父进程尚未对其进行善后处理(获取终止子进程的有关信息,释放它仍占用的资源)的进程被成为僵死进程。ps(l)命令将僵死进程的状态打印为Z。因此,除非父进程等待获得子进程的终止状态,否则子进程终止后就会变成僵死进程。而被init进程领养的进程,终止时init就会调用wait函数获取其终止状态,防止系统有很多僵死进程。
wait函数
当进程正常或异常终止时,内核就向其父进程发送SICHLD信号。父进程可以选择忽略该信号(系统默认该动作),或者提供一个该信号发生时即被调用执行的信号处理程序。
调用wait或waitpid的进程时:
·如果其素有子进程都还在运行,则阻塞。
·如果一个子进程已终止,正等待父进程获取其终止状态,则取得该子进程的终止状态立即返回。
·如果它没有任何子进程,则立即出错返回。
如果进程由于接收到SIGCHLD信号而调用wait,则可期望wait会立即返回。但如果在任意时刻调用wait,则进程可能会阻塞。而wait和waitpid的区别在于waitpid有多个选项,可以控制其等待的进程和是否进行阻塞。
POSIX.1规定终止状态用定义在中的各个宏来查看。有四个互斥的宏可以用来取得进程终止的原因,名字均以WIF开始。
WIFEXITED 若为正常终止子进程返回的状态,则为真;可执行WEXITSTATUS取终止状态的低8位
WIFSIGNALED 若为异常终止子进程返回的状态,则为真;可执行WTERMSIG,取终止进程的信号编号
WIFSTOPPED 若为当前暂停子进程的返回状态,则为真;可执行WSTOPSIG,取暂停进程的信号编号
WIFCONTINUED 若暂停后继续的子进程返回状态,则为真;仅用于waitpid
system函数
因为system在其实现中调用了fork、exec和waitpid,因此有三种返回值:
(1)如果fork失败或者waitpid返回除了EINTR之外的出错,则system返回-1,而且errno中设置了错误类型值。
(2)如果exec失败(表示不能执行shell),则其返回值如同shell执行了exit(127)一样。
(3)否则所有三个函数(fork、exec和waitpid)都执行成功,并且system的返回值是shell的终止状态,其格式已在waitpid中说明。
再看看Linux版本的system函数源代码,以加深对system函数的理解:
- #include <stdio.h>
- #include <sys/types.h>
- #include <sys/wait.h>
- #include <errno.h>
- #include <unistd.h>
- int system(const char * cmdstring)
- {
- pid_t pid;
- int status;
-
- if(cmdstring == NULL)
- {
- return (1);
- }
-
- if((pid = fork())<0)
- {
- status = -1;
- }
- else if(pid == 0)
- {
- execl("/bin/sh", "sh", "-c", cmdstring, (char *)0);
- -exit(127); //子进程正常执行则不会执行此语句
- }
- else
- {
- while(waitpid(pid, &status, 0) < 0)
- {
- if(errno != EINTER)
- {
- status = -1;
- break;
- }
- }
- }
- return status;
- }
备注1:
根据进程退出的定义,只要能够调用到/bin/sh,并且执行shell过程中没有被其他信号异常中断,都算正常结束。即不管shell脚本返回什么值,是0或非0,都算正常结束。即使shell脚本不存在或没有权限执行返回,也算是正常执行结束。
备注2:
正确判断system函数调用shell脚本是否正确执行,需要进行三个条件的判断:
·system函数返回值不为-1
·WIFEXITED为真
·0 == WEXITSTATUS为真
备注3:
如果shell脚本不存在或没有权限执行,虽然正常返回,并且WIFEXITED为真,但WEXITSTATUS为127或126等值,因此调用的shell脚本的返回值最好不要使用127或126等值,最好从1开始递增。
判断shell脚本是否正常执行的完整代码示例:
- #include <stdio.h>
- #include <stdlib.h>
- #include <sys/wait.h>
- #include <sys/types.h>
- int main()
- {
- pid_t status;
-
- status = system("./test.sh");
-
- if (-1 == status)
- {
- printf("system error!");
- }
- else
- {
- printf("exit status value = [0x%x]\n", status);
-
- if (WIFEXITED(status))
- {
- if (0 == WEXITSTATUS(status))
- {
- printf("run shell script successfully.\n");
- }
- else
- {
- printf("run shell script fail, script exit code: %d\n", WEXITSTATUS(status));
- }
- }
- else
- {
- printf("exit status = [%d]\n", WEXITSTATUS(status));
- }
- }
- return 0;
- }
阅读(2067) | 评论(0) | 转发(0) |