第三章 进程的创建
2016-01-26 00:18
267 查看
Linux下有四类创建子进程的函数:system(),fork(),exec*(),popen()
先执行父进程,后执行子进程
system函数通过调用shell程序/bin/sh -c 来执行string所指定的命令,该函数在内部是通过调用fork()–>execve(“/bin/sh”…)函数来实现的。通过system创建子进程后,原进程和子进程各自运行,相互间关联较少。如果system调用成功,将返回0。
(原理:子进程占据了主进程的PCB区域运行,fork()–>execve()创建拷贝添内容)
示例:
在linux中fork函数时非常重要的函数,它从已存在进程中创建一个新进程。新进程为子进程,而原进程为父进程。它和其他函数的区别在于:它执行一次返回两个值。其中父进程的返回值是子进程的进程号,而子进程的返回值为0.若出错则返回-1.因此可以通过返回值来判断是父进程还是子进程。
fork函数创建子进程的过程为:使用fork函数得到的子进程是父进程的一个复制品,它从父进程继承了进程的地址空间,包括进程上下文、进程堆栈、内存信息、打开的文件描述符、信号控制设定、进程优先级(0-99实时进程100-139非实时进程数字越大优先级越低)、进程组号、当前工作目录、根目录、资源限制、控制终端,而子进程所独有的只有它的进程号(子进程的进程号为父进程+1)、资源使用(物理资源使用,虚拟地址指向同一个物理地址,但操作权限不同,子进程写的时候os重新给子进程分配一块物理地址,是旧的物理地址复制,即写实复制,mmu机制)和计时器等。通过这种复制方式创建出子进程后,原有进程和子进程都从函数fork返回,各自继续往下运行,但是原进程的fork返回值与子进程的fork返回值不同,在原进程中,fork返回子进程的pid,而在子进程中,fork返回0,如果fork返回负值,表示创建子进程失败。(vfork函数)
说明:子进程的pid为父进程的pid+1,vfork把PCB结构体拷贝好一份后就把子进程放到对应的队列中,所以执行路径有两条。
exec函数族的工作过程与fork完全不同,fork是在复制一份原进程,而exec函数是用exec的第一个参数指定的程序覆盖现有进程空间(也就是说执行exec族函数之后,它后面的所有代码不在执行)。
path是包括执行文件名的全路径名 。
arg是可执行文件的命令行参数,多个用,分割注意最后一个参数必须为NULL。
示例:
说明:echo $?语句功能:显示函数返回值
popen函数类似于system函数,与system的不同之处在于它使用管道工作。command为可执行文件的全路径和执行参数;
type可选参数为”r”或”w”,如果为”w”,则popen返回的文件流做为新进程的标准输入流,即stdin,如果为”r”,则popen返回的文件流做为新进程的标准输出流。如果type是“r”,(即command命令执行的输出结果作为当前进程的输入结果)。被调用程序的输出就可以被调用程序使用,调用程序利用popen函数返回的FILE*文件流指针,就可以通过常用的stdio库函数(如fread)来读取被调用程序的输出;如果tpye是“w”,(即当前进程的输出结果作为command命令的输入结果)。调用程序就可以用fwrite向被调用程序发送数据,而被调用程序可以在自己的标准输入上读取这些数据。pclose等待新进程的结束,而不是杀新进程。(popen在创建进程的时候做了原进程的重定向,将两个进程用管道连接,若A、B分别为原进程和新进程,那么B的标准输出1重定向给A的标准输入0,A的标准输出1重定向给B的标准输入0,都通过管道)
示例:
先执行父进程,后执行子进程
一、system()
原型:#include <stdlib.h> int system(const char *string);
system函数通过调用shell程序/bin/sh -c 来执行string所指定的命令,该函数在内部是通过调用fork()–>execve(“/bin/sh”…)函数来实现的。通过system创建子进程后,原进程和子进程各自运行,相互间关联较少。如果system调用成功,将返回0。
(原理:子进程占据了主进程的PCB区域运行,fork()–>execve()创建拷贝添内容)
示例:
#include <stdio.h> #include <stdlib.h> int main() { system("sleep 10"); printf("I am main process\n"); while(1); return 0; }
二、fork()(拷贝PCB结构体)
原型:#include <unistd.h> pid_t fork(void);
在linux中fork函数时非常重要的函数,它从已存在进程中创建一个新进程。新进程为子进程,而原进程为父进程。它和其他函数的区别在于:它执行一次返回两个值。其中父进程的返回值是子进程的进程号,而子进程的返回值为0.若出错则返回-1.因此可以通过返回值来判断是父进程还是子进程。
fork函数创建子进程的过程为:使用fork函数得到的子进程是父进程的一个复制品,它从父进程继承了进程的地址空间,包括进程上下文、进程堆栈、内存信息、打开的文件描述符、信号控制设定、进程优先级(0-99实时进程100-139非实时进程数字越大优先级越低)、进程组号、当前工作目录、根目录、资源限制、控制终端,而子进程所独有的只有它的进程号(子进程的进程号为父进程+1)、资源使用(物理资源使用,虚拟地址指向同一个物理地址,但操作权限不同,子进程写的时候os重新给子进程分配一块物理地址,是旧的物理地址复制,即写实复制,mmu机制)和计时器等。通过这种复制方式创建出子进程后,原有进程和子进程都从函数fork返回,各自继续往下运行,但是原进程的fork返回值与子进程的fork返回值不同,在原进程中,fork返回子进程的pid,而在子进程中,fork返回0,如果fork返回负值,表示创建子进程失败。(vfork函数)
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/types.h> int main() { pid_t pid; pid=fork(); if(pid==0) { printf("my pid is %d ppid=%d\n",getpid(),getppid()); exit(0); } else { printf("my pid is %d ppid=%d\n",getpid(),getppid()); while(1); return 0; } }
说明:子进程的pid为父进程的pid+1,vfork把PCB结构体拷贝好一份后就把子进程放到对应的队列中,所以执行路径有两条。
三、exec函数族(exec*由一组函数组成)
原型:int execl(const char *path, const char *arg, ...)
exec函数族的工作过程与fork完全不同,fork是在复制一份原进程,而exec函数是用exec的第一个参数指定的程序覆盖现有进程空间(也就是说执行exec族函数之后,它后面的所有代码不在执行)。
path是包括执行文件名的全路径名 。
arg是可执行文件的命令行参数,多个用,分割注意最后一个参数必须为NULL。
示例:
//add.c #include <stdio.h> int main(int argc,char* argv[]) { if(argc!=3) { printf("error args\n"); return 0; } printf("the sum is %d\n",atoi(argv[1])+atoi(argv[2])); return 1; } //execl.c,整个代码段被add.c覆盖掉,返回值为add.c的返回值,为1,如下图 #include <unistd.h> #include <stdio.h> int main() { printf("I am start\n"); execl("./add","add","1","2",NULL);//NULL表示输入参数结束 printf("test\n");//此语句将不会被执行 return 0; }
说明:echo $?语句功能:显示函数返回值
四、popen函数
原型:#include <stdio.h> FILE *popen(const char *command, const char *type); int pclose(FILE *stream);
popen函数类似于system函数,与system的不同之处在于它使用管道工作。command为可执行文件的全路径和执行参数;
type可选参数为”r”或”w”,如果为”w”,则popen返回的文件流做为新进程的标准输入流,即stdin,如果为”r”,则popen返回的文件流做为新进程的标准输出流。如果type是“r”,(即command命令执行的输出结果作为当前进程的输入结果)。被调用程序的输出就可以被调用程序使用,调用程序利用popen函数返回的FILE*文件流指针,就可以通过常用的stdio库函数(如fread)来读取被调用程序的输出;如果tpye是“w”,(即当前进程的输出结果作为command命令的输入结果)。调用程序就可以用fwrite向被调用程序发送数据,而被调用程序可以在自己的标准输入上读取这些数据。pclose等待新进程的结束,而不是杀新进程。(popen在创建进程的时候做了原进程的重定向,将两个进程用管道连接,若A、B分别为原进程和新进程,那么B的标准输出1重定向给A的标准输入0,A的标准输出1重定向给B的标准输入0,都通过管道)
示例:
#include <stdio.h> #include <stdlib.h> #include <strings.h> int main() { FILE* fp; fp=popen("ls -l","r");//打开标准流管道,并从管道中读取数据 if(NULL==fp) { perror("popen"); return -1; } char buf[512]; bzero(buf,sizeof(buf)); int ret=fread(buf,1,512,fp); if(ret>0) { printf("%s\n",buf); } return 0; }
//scanf.c #include <stdio.h> int main() { int i; scanf("%d",&i); printf("the i is %d\n",i); return 0; } //popen_w.c #include <stdio.h> #include <stdlib.h> #include <strings.h> #include <string.h> int main() { FILE* fp; fp=popen("./scanf","w"); if(NULL==fp) { perror("popen"); return -1; } char buf[512]; bzero(buf,sizeof(buf)); strcpy(buf,"456"); int ret=fwrite(buf,1,strlen(buf),fp); if(ret<0) { perror("fwrite"); return -1; } pclose(fp); return 0; }
相关文章推荐
- NFS服务器配置
- 4 套接字模式、选择模型
- 以Attention Model为例谈谈两种研究创新模式
- Sqlite3 database file is locked 解决办法 与sqlite_reset()
- swift与OC混编(创建混编工程)
- jquery设置元素的readonly和disabled
- COCOS学习笔记--变速动作Speed和ActionEase
- 蜗牛爱课 -- iOS 设置UIButton的字体的大小、显示位置、大小
- Oracle 12c in memory 组件浅谈
- HDU 1018 Big Number
- JavaScript正则表达式下——相关方法
- NSString设置关键字颜色 凸显关键字
- 软件开发总结
- Kmeans算法(Jfreechart展示+Spring注入)
- 莫队算法详解
- Builder(建造者)模式
- 2012年第三届蓝桥杯C/C++程序设计本科B组省赛
- python小白-day4装饰器
- C语言 百炼成钢16
- JavaScript 正则表达式上——基本语法