Linux下多进程编程的核心是调用fork()系统调用用来创建一个新的进程:
pid_t fork(void);
由fork()创建的新进程被称为子进程。fork()函数被调用一次,但有两次返回。
返回值=0: 子进程
返回值>0: 父进程,返回值为子进程的进程ID。
返回值<0: 出错
1,子进程可以通过getpid()和getppid()分别获取自己的进程ID和父进程ID;
2,一个父进程可以有很多个子进程,没有一个系统调用可以获取所有的子进程ID,所以需要将子进程的ID通过返回值的形式传递给父进程。
阅读以下代码你需要了解的函数与系统调用:
dup2()
execl()
wait() lseek()
pipe(fd)
popen()
strstr() strchr() strrchr() strncpy() strncpy() memset()
利用多进程编程获取网卡eth0的IP地址
方法一(利用文件I/O):
子进程执行ifconfig eth0,将执行的结果输出重定向到文件(.ipc.log)中,父进程再从该文件中读出ip地址。
/*********************************************************************************
* Copyright: (C) 2018 wangtao
* All rights reserved.
*
* Filename: fork.c
* Description: This file
*
* Version: 1.0.0(05/11/2018)
* Author: WangTao <TAlicer@163.com>
* ChangeLog: 1, Release initial version on "05/11/2018 06:09:56 PM"
*
********************************************************************************/
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define IPC_FILE ".ipc.log"
/********************************************************************************
* Description:
* Input Args:
* Output Args:
* Return Value:
********************************************************************************/
int main (int argc, char **argv)
{
pid_t pid;
int fd = -1;
char buf[512];
FILE *fp = NULL;
fd = open(IPC_FILE, O_RDWR|O_CREAT|O_TRUNC,0644); //打开.ipc.log并获取文件描述符fd
if( fd < 0)
{
printf("open file '%s' failure: %s\n",IPC_FILE,strerror(errno));
return 0;
}
pid=fork(); //调用fork创建子进程
if(pid <0)
{
printf("fork() error: %s\n",strerror(errno));
return 0;
}
else if(pid == 0) //###"子进程"###
{
dup2(fd,1); //在子进程中,将标准输出重定向到.ipc.log文件中(将标准输出(1)关掉,再将.ipc.log的文件描述符改为1)
execl("/sbin/ifconfig","ifconfig","eth0",NULL); //调用execl执行新的程序(命令ifconfig eth0)
printf("####haha\n"); //该句不会打印
}
else if(pid > 0) //###"父进程"###
{
int status;
wait(&status); //等待子进程退出后,再继续向下执行
printf("Parent running: child pid[%ld],parent pid[%ld],grandparent pid[%ld]\n",pid,getpid(),getppid());
lseek(fd,0,SEEK_SET); //将文件偏移量设置为0,不然读到为空
fp = fdopen(fd,"r"); //将文件描述符转换为文件流
// while( read(fd,buf,sizeof(buf))>0 )
while( fgets(buf,sizeof(buf),fp)>0 ) //将.ipc.log里的内容一行一行的读到buf里
{
if( strstr(buf,"inet addr:")) //如果读到的某一行buf含有关键词"inet addr:",则打印该次buf里的内容
{
printf("buf:%s\n",buf);
}
}
}
close(fd);
unlink(IPC_FILE); //删除文件.ipc.log
return 0;
} /* ----- End of main() ----- */
程序执行结果:
方法二(利用管道):
先创建管道,再创建子进程,这样父进程和子进程都拥有了管道的读端与写端,然后关闭子进程管道读端,关闭父进程写端,子进程执行ifconfig eth0,将执行的结果重定向到管道写端,父进程再从管道读端里获取IP地址。
/*********************************************************************************
* Copyright: (C) 2018 wangtao
* All rights reserved.
*
* Filename: fork_pipe.c
* Description: This file
*
* Version: 1.0.0(05/11/2018)
* Author: WangTao <TAlicer@163.com>
* ChangeLog: 1, Release initial version on "05/11/2018 06:09:56 PM"
*
********************************************************************************/
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include<unistd.h>
/********************************************************************************
* Description:
* Input Args:
* Output Args:
* Return Value:
********************************************************************************/
int main (int argc, char **argv)
{
pid_t pid;
int fd[2];
char buf[512];
FILE *fp = NULL;
if( pipe(fd)<0 ) //调用pipe创建管道, fd参数返回两个文件描述符,fd[0]指向管道的读端,fd[1]指向管道的写端。fd[1]的输出是fd[0]的输入。
{
printf("pipe failure: %s\n",strerror(errno));
return -1;
}
pid=fork(); //调用fork创建子进程
if(pid <0)
{
printf("fork() error: %s\n",strerror(errno));
return 0;
}
else if(pid == 0) //###"子进程"###
{
close(fd[0]); //关闭管道读端
dup2(fd[1],1); //在子进程中,将标准输出重定向到管道写端中(将标准输出(1)关掉,再将管道写端的文件描述符改为1)
execl("/sbin/ifconfig","ifconfig","eth0",NULL); //调用execl执行新的程序(命令ifconfig eth0)
printf("####haha\n"); //该句不会打印
}
else if(pid > 0) //###"父进程"###
{
close(fd[1]); //关闭管道写端
int status;
wait(&status);
printf("Parent running: child pid[%ld],parent pid[%ld],grandparent pid[%ld]\n",pid,getpid(),getppid());
fp = fdopen(fd[0],"r"); //将文件描述符转换为文件流
while( fgets(buf,sizeof(buf),fp)>0 ) // 将父进程管道读端里的内容一行一行的读到buf里
{
if( strstr(buf,"inet addr:"))
{
printf("buf:%s\n",buf);
}
}
fclose(fp);
}
return 0;
} /* ----- End of main() ----- */
程序执行结果:

方法三(利用popen函数)推荐:
popen()会调用fork()产生子进程,然后从子进程中调用/bin/sh -c来执行ifconfig eth0 第二个参数“r”代表读取
/*********************************************************************************
* Copyright: (C) 2018 Wang Tao
* All rights reserved.
*
* Filename: popen.c
* Description: This file
*
* Version: 1.0.0(2018年05月12日)
* Author: wang tao <TAlicer@163.com>
* ChangeLog: 1, Release initial version on "2018年05月12日 21时14分17秒"
*
********************************************************************************/
#include <stdio.h>
#include <string.h>
#include <errno.h>
/********************************************************************************
* Description:
* Input Args:
* Output Args:
* Return Value:
********************************************************************************/
int main (int argc, char **argv)
{
FILE *fp;
char buf[512];
char *p1,*p2,*p3;
char ipaddr[16];
char netmask[16];
fp = popen("ifconfig eth0","r");
if( fp == NULL)
{
printf("popen failure: %s\n",strerror(errno));
return 0;
}
while( fgets(buf,sizeof(buf),fp)>0)
{
if( (p1=strstr(buf,"inet addr:")) !=NULL) //p1指向含有"inet addr:"关键词这一行的首地址
{
//printf("%s",buf);
p2=strchr(p1,':'); //p2指向p1行第一个冒号处
p3=strchr(p2,' '); //p2指向p2以后第一个空格处
memset(ipaddr,0,sizeof(ipaddr));
strncpy(ipaddr,p2+1,p3-p2); //截取IP地址到ipaddr数组中
printf("IP address: %s\n",ipaddr);
p2=strrchr(p1, ':'); //p2指向p1行最后一个冒号处
p3=strrchr(p2, '\n'); //p3指向p2以后最后一个换行符处(在p1行中)
memset(netmask, 0, sizeof(netmask));
strncpy(netmask, p2+1, p3-p2); //截取Mask地址到netmask数组中
printf("Netmask address: %s\n", netmask);
}
}
pclose(fp);
return 0;
} /* ----- End of main() ----- */
程序执行结果:
