目录
fork()/vfork()系统调用
exec*()执行另外一个程序
wait()与waitpid()
system()与popen()函数
fork()/vfork()系统调用
pid_t fork(void)
功能描述:创建子进程
返回值:>0 父进程运行 =0 子进程运行 <0 函数系统调用失败(系统中有太多进程、该用户的进程总数超过了系统限制)
有两次返回 ①返回给父进程 返回值是子进程的PID ②返回给子进程 返回值是0
PS:父子进程谁先运行没有规定,由 *** 作系统决定。 并且fork会将父进程的地址空间完全复制到子进程中
pid_t vfork(void)
PS:用法与fork相同,但是vfork并不将父进程的地址空间复制到子进程中,因为子进程会立即调用exec或exit(),因此不会引用该地址空间。 但在子进程调用exec或exit()之前,它会在父进程的空间中进行,但如果子进程想要尝试修改数据域(数据段、堆、栈)都会带来未知的结果,因为它会影响父进程空间的数据可能会导致父进程执行异常。 此外,vfork()会保证子进程先运行,在其调用exec或exit()之后父进程才可能被调度运行。 如果子进程依赖父进程进一步动作,会导致死锁。
pid_t getpid(void) pid_t getppid(void)
功能描述:getpid返回当前进程PID,getppid返回其父进程PID
包含的头文件:#include
pid_t 是个宏定义 实质是int 被定义在头文件#include
下面以一个程序讲解一下进程的创建过程。
#include
#include
#include
#include
int main(int argc, char **argv)
{
pid_t pid;
printf("Parent process PID[%d] start running....\n", getpid() );
pid = fork();
if( pid < 0 )
{
printf("fork() create child process failure: %s\n", strerror(errno) );
return -1;
}
else if( pid == 0 )
{
printf("Child process PID[%d] start running, my parent PID is [%d]\n", getpid(), getppid() );
return 0;
}
else
{
printf("Parent process PID[%d] continue running, and child process PID is [%d]\n", getpid(), pid);
return 0;
}
}
执行结果如下:
Parent process PID[2676] start running...
Parent process PID[2676] continue running, and child process PID is [2677]
Child process PID[2677] start running, my parent PID is [2676]
exec*()执行另外一个程序 通常情况下,我们创建子进程是让该进程去执行另外一个程序。
这时我们会在fork()之后紧接着调用exec*()系列的函数来让子进程去执行另外一个程序。
其中exec*()是一些列的函数,其原型为:
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[]); ... ..以上这些函数我们选择一个实现即可,在这里我主要介绍execl()函数,因为这个函数相对比别的函数参数较为简单。
int execl(const char *path, const char *arg, ...)
功能描述:执行另一个程序。 通常和fork()一起使用,让子进程去执行另一个程序
参数解析:①path字符指针指向要执行的文件路径
②接下来参数代表执行该文件时传递的参数列表(l):argv[0],argv[1]...最后一个参数用空指针NULL作结束
返回值:成功则无返回值,失败返回-1
包含的头文件:#include
PS:用execl执行另一个程序,会抛弃父进程的文本段、数据段和堆栈等并加载另一个程序
wait()与waitpid() 当一个进程正常或异常退出时,内核就会向其父进程发送SIGCHLD信号。
因为子进程退出是一个异步事件,所以这种信号也 是内核向父进程发送的一个异步通知。
父进程可以选择忽略该信号,或者提供一个该信号发生时即将被执行的函数,父进程可以调用wait()或waitpid()可以用来查看子进程退出的状态。
pid_t wait(int *status) 参数解析:
- 参数status如果不是一个空指针,则终止进程的终止状态就存放在statloc所指向的单元。
- 参数status如果是一个空指针,则表示父进程不关心子进程的终止状态
WNOHANG | 若由pid指定的子进程未发生状态改变(没有结束),则waitpid()不阻塞,立即返回0 |
---|---|
WUNTRACED | 返回终止子进程信息和因信号停止的子进程信息 |
WCONTINUED | 返回收到SIGCONT信号而恢复执行的已停止子进程状态信息 |
waitpid并不等待在其调用的之后的 第一个终止进程,他有若干个选项,可以控制他所等待的进程。
所以,我们在编写多进程程序时,最好调用wait()或waitpid()来解决僵尸进程的问题。
system()与popen()函数
int system(const char *command)
功能描述:Liunx库函数,可以快速创建一个进程来执行相应的命令,在Linux/Unix系统中,system函数会调用fork函数产生子进程,由子进程来执行command命令,命令执行完后随即返回原调用的进程。
参数解析:command字符指针指向一条命令,如"ping -c 4 -I eth0 4.2.2.2"
返回值:成功则返回0,失败则返回-1
包含头文件:#include
FILE *popen(const char *command, const char *type)
功能描述:popen()会调用fork()产生子进程,然后从子进程中调用/bin/sh -c来执行参数command的指令
参数解析:①command字符指针指向一条命令,如"ping -c 4 -I eth0 4.2.2.2"
②参数type可使用“r”代表读取(对应标准输出),“w”代表写入(对应标准输入)
返回值:若成功则返回文件指针,否则返回NULL,错误原因存于errno中。
包含头文件:#include
PS:popen()返回一个基于管道(pipe)的文件流,这样我们可以从文件流中一行一行解析
下面是一个程序是关于popen()的使用获取IP地址:
#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]
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)