分类: LINUX
2008-10-08 14:32:51
execl(执行文件) 相关函数: fork,execle,execlp,execv,execve,execvp 表头文件: #include 定义函数: int execl(const char * path,const char * arg,....); 函数说明: execl()用来执行参数path字符串所代表的文件路径,接下来的参数代表执行该文件时传递过去的argv(0)、argv[1]……,最后一个参数必须用空指针(NULL)作结束。 返回值: 如果执行成功则函数不会返回,执行失败则直接返回-1,失败原因存于errno中。 范例: #include 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 and waitpid 当父进程通过fork创建了子进程之后, 父进程和子进程的执行顺序是无法控制的. 如果想控制, 有两种办法: 使用vfork创建或者父进程调用等待函数wait. 1. 当进程终止时, 会向其父进程发送SIGCHLD信号, 这个异步事件可以在父进程运行的任何时候发生, 包括正常和异常终止两种. 调用wait和waitpid的进程可能会有以下三种情况: 1. 阻塞(如果其所有的子进程都还在运行) 2. 带子进程的终止状态正常返回(其中一个子进程终止) 3. 出错返回(没有子进程) 原型如下: #include #include pid_t wait(int *statloc); 函数返回: 若成功则为进程ID,若出错则为-1. 参数说明: statloc: 如果是不是空指针, 则子进程的返回状态保存在该指针指向的整型变量中; 如果是空指针, 则忽 略返回状态. 2. waitpid函数: 如果一个进程有若干个子进程, 那么只要有一个子进程返回, wait就返回. 如果要等待一个指定的子进程, 有两种方法. 第一个种, 早期的UNIX必须调用wait, 然后把返回的pid和期望pid做比较, 如果不是期望的, 把该pid保存起来, 继续调用wait, 直到进程终止. 第二种是使用waitpid. #include #include pid_t waitpid(pid_t pid, int *statloc, int options); 函数返回: 若成功则为进程ID, 若出错则为-1. 参数说明: pid: * pid == -1 等待任一子进程. 这个时候waitpid与wait等效. * pid > 0 等待其ID与pid相等的子进程. * pid == 0 等待其组ID与调用进程的组ID的任一子进程. * pid < -1 等待其组ID等于pid绝对值的任一子进程. 对于waitpid函数, 如果指定的进程或进程组不存在, 或者调用进程没有子进程都会出错. options: 这个一参数可以让我们进一步控制waitpid的操作, 此参数或者是0, 或者是下列的逐位或常数之一: WNOHANG: 若由pid指定的子进程并不立即可用, 则waitpid不阻塞, 此时其返回值为0. WUNTRACED: 若某实现支持作业控制, 则由pid指定的任一子进程状态已暂停, 且其状态自暂停以来还 未报告过, 则返回其状态. WIFSTOPPED宏确定返回值是否对应于一个暂停子进程. 3. waitpid和wait的区别: waitpid提供了wait函数不能实现的3个功能: * waitpid等待特定的子进程, 而wait则返回任一终止状态的子进程; * waitpid提供了一个wait的非阻塞版本; * waitpid支持作业控制(以WUNTRACED选项). 4. 用于检查wait和waitpid两个函数返回终止状态的宏: 这两个函数返回的子进程状态都保存在statloc指针中, 用以下3个宏可以检查该状态: * WIFEXITED(status): 若为正常终止, 则为真. 此时可执行 o WEXITSTATUS(status): 取子进程传送给exit或_exit参数的低8位. * WIFSIGNALED(status): 若为异常终止, 则为真. 此时可执行 o WTERMSIG(status): 取使子进程终止的信号编号. * WIFSTOPPED(status): 若为当前暂停子进程, 则为真. 此时可执行 o WSTOPSIG(status): 取使子进程暂停的信号编号. ====================================================================== Advanced Programming in the UNIX® Environment: Second Edition 8.6. wait and waitpid Functions When a process terminates, either normally or abnormally, the kernel notifies the parent by sending the SIGCHLD signal to the parent. Because the termination of a child is an asynchronous eventit can happen at any time while the parent is runningthis signal is the asynchronous notification from the kernel to the parent. The parent can choose to ignore this signal, or it can provide a function that is called when the signal occurs: a signal handler. The default action for this signal is to be ignored. We describe these options in Chapter 10. For now, we need to be aware that a process that calls wait or waitpid can Block, if all of its children are still running Return immediately with the termination status of a child, if a child has terminated and is waiting for its termination status to be fetched Return immediately with an error, if it doesn 't have any child processes If the process is calling wait because it received the SIGCHLD signal, we expect wait to return immediately. But if we call it at any random point in time, it can block. #include pid_t wait(int *statloc); pid_t waitpid(pid_t pid, int *statloc, int options); Both return: process ID if OK, 0 (see later), or 1 on error The differences between these two functions are as follows. The wait function can block the caller until a child process terminates, whereas waitpid has an option that prevents it from blocking. The waitpid function doesn 't wait for the child that terminates first; it has a number of options that control which process it waits for. If a child has already terminated and is a zombie, wait returns immediately with that child 's status. Otherwise, it blocks the caller until a child terminates. If the caller blocks and has multiple children, wait returns when one terminates. We can always tell which child terminated, because the process ID is returned by the function. For both functions, the argument statloc is a pointer to an integer. If this argument is not a null pointer, the termination status of the terminated process is stored in the location pointed to by the argument. If we don 't care about the termination status, we simply pass a null pointer as this argument. Traditionally, the integer status that these two functions return has been defined by the implementation, with certain bits indicating the exit status (for a normal return), other bits indicating the signal number (for an abnormal return), one bit to indicate whether a core file was generated, and so on. POSIX.1 specifies that the termination status is to be looked at using various macros that are defined in We 'll discuss how a process can be stopped in Section 9.8 when we discuss job control. 8.5. exit Functions Figure 8.4. Macros to examine the termination status returned by wait and waitpid Macro Description WIFEXITED(status) True if status was returned for a child that terminated normally. In this case, we can execute WEXITSTATUS (status) to fetch the low-order 8 bits of the argument that the child passed to exit, _exit,or _Exit. WIFSIGNALED (status) True if status was returned for a child that terminated abnormally, by receipt of a signal that it didn 't catch. In this case, we can execute WTERMSIG (status) to fetch the signal number that caused the termination. Additionally, some implementations (but not the Single UNIX Specification) define the macro WCOREDUMP (status) that returns true if a core file of the terminated process was generated. WIFSTOPPED (status) True if status was returned for a child that is currently stopped. In this case, we can execute WSTOPSIG (status) to fetch the signal number that caused the child to stop. WIFCONTINUED (status) True if status was returned for a child that has been continued after a job control stop (XSI extension to POSIX.1; waitpid only). =============================================================================== wait 函数的参数 statuswait 函数原型#include #include pid_t wait(int *status) 进程一旦调用了wait,就立即阻塞自己,由wait自动分析是否当前进程的子进程已经退出,如果让它找到了这样一个变成僵尸的子进程,wait就会收集这个子进程的信息,并把它彻底销毁会返回。如果没有找到这样的进程,wait就会一直等待,直到找到为止。 参数status用来保存被收集进程再退出时的一些状态,它是一个指向int类型的指针,但如果对之个子进程如何死掉毫不在意,只想把僵尸进程消灭掉(一般都是这样),就把它设为null pid = wait (NULL) 如果status的值不为null,wait就会把子进程推出时的状态取出并存入其中。指出了子进程是正常退出还是非正常退出,以及正常退出是的返回值,或者被那一个信号结束 等信息。 这些信息存放在一个整数的不同二进制数中,所以常规办法很难读取, 所以设计了一套专门的宏(macro)来完成这项工作。 WIFXITED(status) 这个宏指出子进程是否位正常推出的,如果是 他将返回一个非零值。 WEXITATUS(status) ($:/usr/include/sys $ grep -i wexit wait.h # define WEXITSTATUS(status) __WEXITSTATUS(__WAIT_INT(status))) 当WIFEXITED 返回非零值时,可以用这个宏来提取子进程的返回值,如果子进程调用exit(5)退出,WIFEXITED(status)就会返回5.如果子进程不是正常退出的, WIFEXITED就返回0 实例: #include #include #include main(){ int status; pid_t pc,pr; pc=fork(); if(pc<0); printf("error ocurred!\n"); else if(pc==0){ pritnf("this is a child\n",getpid()); exit(3); } else { pr=wait(&status); if(WIFEXITED(status)) { /*如果WIFEXITED(status)返回非零值*/ printf(this child process %d exit normally\n",pr); printf(the reuurn code is %d\n", WEXITATUS(status)); } else /*如果WIFEXITED(status)返回零值*/ printf("the child process %d exit abnormally\n",pr); } } 结果: $./wait2 this is child with pid of 4244 the child process 4244 exit noramally the return code is 3 =================================================================================== WIFEXITED/WEXITSTATUS/WIFSIGNALED f the exit status value (*note Program Termination::) of the child process is zero, then the status value reported by `waitpid' or `wait' is also zero. You can test for other kinds of information encoded in the returned status value using the following macros. These macros are defined in the header file `sys/wait.h'. -- Macro: int WIFEXITED (int STATUS) -- Macro: int WEXITSTATUS (int STATUS) -- Macro: int WIFSIGNALED (int STATUS) 子进程的结束状态返回后存于status,底下有几个宏可判别结束情况 ================================================================================== APUE--僵尸进程 wait() waitpid() 前边文章提到fork与vfork可以产生一个新进程,当子进程使命结束时会调用exit函数。但是调用exit并不会使子进程完全消失,而是转为一个僵 尸进程(zombie)。僵尸进程已将原进程占用的绝大部分内存空间释放,也几乎不占用CPU,它不错任何事情,只是等待父进成来获取原子进程的结束信 息。僵尸进程的存在有其实际意义,它可以为程序员提供许多重要信息,如子进程结束时的状态(正常结束还是异常结束),子进程占用的CPU时间等等,下边的 代码将演示如何产生一个zombie /***zombie.c***/ #include #include #include #include int main() { pid_t pid; if((pid=fork())<0) {printf("fork error"); exit(0); } else if(pid==0) /*child process exit ant wait its parent to fetch its exit status*/ exit(0); else if(pid>0) sleep(10); /*parten process sleep for 10s so thar we can see the zombie*/ if(wait(NULL)==-1) printf("wait error"); printf("parent exit"); exit(0); } 编译,运行 $gcc -o zombie zombie.c $./zombie & 此时在终端运行ps -l 查看进程信息有 $ps -l F S UID PID PPID C PRI NI ADDR SZ WCHAN TTY TIME CMD 4 S 0 10460 10139 0 75 0 - 1419 wait pts/0 00:00:00 bash 0 S 0 24484 10460 0 78 0 - 346 - pts/0 00:00:00 zombie 1 Z 0 24485 24484 0 78 0 - 0 exit pts/0 00:00:00 zombie 0 R 0 24821 10460 0 76 0 - 1273 - pts/0 00:00:00 ps 可以看到 1 Z 0 24485 24484 0 78 0 - 0 exit pts/0 00:00:00 zombie 中第二列(即进程状态)为Z(zombie) zombie不占用内存也不占用CPU,表面上我们可以不用在乎它们的存在,然而事实上UNIX系统限制了某一时刻能同时存在的进程的最大数目。如果程序不及时清理系统中的zombie,最终会导致进程数过多,当再次需要产生新进程时就会出错。 鉴于上边的原因,我们需要在子进程调用exit后在父进成中调用wait或waipid #include #include pid_t wait(int &statloc); pid_t waitpid(pid_t pid,int *statloc, int options); Both return:process ID if OK,-1 on error 它们被父进程调用以获取子进程结束信息、清除zombie。当父进成调用这两个函数时 a 阻塞(如果它的子进程还在运行) b 立即返回子进程结束信息(如果一个子进程已经结束并等待父进程获取信息) c 返回错误(如果不存在子进程) 两个函数的不同在于wait会令调用者阻塞直至某个子进程终止而waitpid则可以通过设置一个选项来设置为非阻塞,另外waitpid并不是等待第一个结束的进程而是等待参数中pid指定的进程。 两个函数中的变量statloc是一个指向int型数据的指针。如果此变量不是NULL,则结束的进程的termination status会被保存在statiloc所指向的内存的区域;如果我们不关心termination status,则可以把statloc置为NULL。 传统的实现中这两个函数返回的整数中特定的比特位被赋予了特定的含义。POSIX.1指定了一些包含在头文件 Macro Description WIFEXITED(status) 如果status是由一个正常结束的进程产生的则值为真, 此时我们可以继续使用宏WEXITSTATUS(status)来 获取exit或_exit的参数 WIFSIGNALED(status) 如果status是由一个异常结束(接受到一个信号)的进 程产生的则值为真,此时使用宏WTERMSIG(status)来 获取信号数。 WIFSTOPPED(status) 如果status是由一个接受到信号目前终止的进程产生的 则值为真,此时可以继续调用宏WSTOPSIG(status)来 查看是哪个信号导致进程终止。 waitpid的option常量 WNOHANG waitpid将不阻塞如果指定的pid并未结束 WUNTRACED 如果子进程进入暂停执行情况则马上返回,但结束状态不予以理会。 waitpid中pid的含义依据其具体值而变 pid==-1 等待任何一个子进程,此时waitpid的作用与wait相同 pid >0 等待进程ID与pid值相同的子进程 pid==0 等待与调用者进程组ID相同的任意子进程 pid<-1 等待进程组ID与pid绝对值相等的任意子进程 waitpid提供了wait所没有的三个特性: 1 waitpid使我们可以等待指定的进程 2 waitpid提供了一个无阻塞的wait 3 waitpid支持工作控制 以下代码来自APUE page202 Exp: #include #include #include "ourhde.h" int main(void) { pid_t pid; if( (pid=fork() )<0) err_sys("fork error"); else if(pid==0){ if((pid=fork())<0) err_sys("fork error"); else if(pid>0) exit(0); /*parent from second fork == first child */ /* We're the second child;our parent becomes init as soon as our real parent calls exit() in the statement above. Here's where we'd continue executing,knowing that when we're done,init wil reap our status*/ sleep(2); printf("second child ,parent pid=%d\n",getppid()); exit(0); } if(waitpid(pid,NULL,0)!=pid) err_sys("waitpid error"); /* We're the parent (the original process);we continue executing,knowing that we're not the parent of the second child.*/ exit(0); } $a.out $second child, parent pid = 1 上述代码通过两次fork防止zombie的产生 |