wait()与waitpid()
当一个进程正常或异常退出时,内核就会向其父进程发送SIGCHLD信号。因为子进程退出是一个异步事件,所以这种信号也是内核向父进程发送的一个异步通知。父进程可以选择忽略该信号,或者提供一个该信号发生时即将被执行的函数,父进程可以调用wait()或waitpid()可以用来查看子进程退出的状态。
pid_t wait(int *status); pid_t waitpid(pid_t pid, int *status, int options);
在一个子进程终止前,wait使其调用者阻塞,而waitpid有一选项可使调用者不用阻塞。 waitpid并不等待在其调用的之后的第一个终止进程,他有若干个选项,可以控制他所等待的进程。 如果一个已经终止、但其父进程尚未对其调用wait进行善后处理(获取终止子进程的有关信息如CPU时间片、释放它锁占用的资源如文件描述符等)的进程被称僵死进程(zombie),ps命令将僵死进程的状态打印为Z。如果子进程已经终止,并且是一个僵死进程,则wait立即返回该子进程的状态。所以,我们在编写多进程程序时,最好调用wait()或waitpid()来解决僵尸进程的问题。
此外,如果父进程在子进程退出之前退出了,这时候子进程就变成了孤儿进程。当然每一个进程都应该有一个独一无二的父进程,init进程就是这样的一个“慈父”,Linux内核中所有的子进程在变成孤儿进程之后都会被init进程“领养”,这也意味着孤儿进程的父进程最终会变成init进程。
system()与popen()函数
如果我们在程序中,想执行另外一个Linux命令时,可以调用fork()然后再exec执行相应的命令即可,但这样相对比较麻烦。Linux系统提供了一个system()库函数,该库函数可以快速创建一个进程来执行相应的命令。
int system(const char *command);
譬如我们想执行ping命令,则可以使用下面的程序片段:
system("ping -c 4 -I eth0 4.2.2.2");
如果这里的 eth0、4.2.2.2 等是一个变量参数,我们则可以使用snprintf()格式化生成该命令:
char cmd_buf[256];
int count = 4;
char *interface="eth0";
char *dst_ip = "4.2.2.2";
snprintf(cmd_buf, sizeof(buf), "ping -c %d -I %s %s", count, interface, dst_ip);
system(cmd_buf);
对于之前我们使用fork()+execl()函数来执行ifconfig命令,并将该命令执行的结果写入到文件后再来读取的实现,这个过程相对比较麻烦,另外涉及到了创建文件和读取文件的过程。其实也有另外一个函数popen()可以执行一条命令,并返回一个基于管道(pipe)的文件流,这样我们可以从该文件流中一行样解析了。相应的代码如下:
vim popen.c
#include
#include
#include
#include
#include
#include
#include
#include
int get_ipaddr(char *interface, char *ipaddr, int ipaddr_size);
int main(int argc, char **argv)
{
char ipaddr[16];
char *interface="eth0";
memset(ipaddr, 0, sizeof(ipaddr));
if( get_ipaddr(interface, ipaddr, sizeof(ipaddr)) < 0 )
{
printf("ERROR: get IP address failure\n");
return -1;
}
printf("get network interface %s IP address [%s]\n", interface, ipaddr);
return 0;
}
int get_ipaddr(char *interface, char *ipaddr, int ipaddr_size) {
char buf[1024];
char *ptr;
char *ip_start;
char *ip_end;
FILE *fp;
int len;
int rv;
if( !interface || !ipaddr || ipaddr_size<16 )
{
printf("Invalid input arguments\n");
return -1;
}
memset(buf, 0, sizeof(buf));
snprintf(buf, sizeof(buf), "ifconfig %s", interface);
if( NULL == (fp=popen(buf, "r")) )
{
printf("popen() to excute command \"%s\" failure: %s\n", buf, strerror(errno));
return -2;
}
rv = -3; /* Set default return value to -3 means parser failure */
while( fgets(buf, sizeof(buf), fp) )
{
if( strstr(buf, "netmask") )
{
ptr=strstr(buf, "inet");
if( !ptr )
{
break;
}
ptr += strlen("inet");
while( isblank(*ptr) )
ptr++;
ip_start = ptr;
while( !isblank(*ptr) )
ptr++;
ip_end = ptr;
memset(ipaddr, 0, sizeof(ipaddr));
len = ip_end-ip_start;
len = len>ipaddr_size ? ipaddr_size : len;
memcpy(ipaddr, ip_start, len);
rv = 0; /* Parser IP address OK and set rv to 0 */
break;
}
}
return rv;
}
运行结果如下:
get network interface eth0 IP address [192.168.2.17]