您的位置:首页 > 其它

进程的创建和控制

2018-03-24 18:27 218 查看
我们已经了解了进程的基本概念,今天我们来看下怎样创建和控制进程吧!
1. 进程的创建
(1)fork()函数
在Linux中,fork 是非常重要的系统函数,它从已经存在的进程中创建一个新的进程。新进程为子进程,原进程为父进程。
函数原型:



返回值:子进程返回0,父进程返回子进程的id ,出错返回-1.
我们先利用fork 函数创建一个进程; 1 #include <stdio.h>
2 #include <sys/types.h>
3 #include <unistd.h>
4
5 int main()
6 {
7 pid_t pid;
8
9 printf("before: pid is %d\n",getpid());
10
11 pid=fork();
12 if(pid==-1)
13 { perror("fork");
14 exit(1);
15 }
16 printf("after:pid is %d,fork return %d\n" ,getpid(),pid);
17 sleep(1);
18 return 0;
19 }
20
看运行结果:



首先,我们可以看到这里有三行输出,一行before,两行after。before先打印了进程2544的信息,然后after又打印了进程2544的信息,同时也打印了进程2545的信息。这是为什么呢?因为fork之前父进程独立运行,fork之后,父子进程两个执行流分别执行。如下图:



其次,我们可以看到父进程返回的是子进程的id,子进程返回的是0.
fork的常规用法:
(1)一个父进程希望复制自己,使父子进程同时执行不同的代码段。例如:父进程等待客户端请求,生成子进程处理请求。
(2)一个进程要执行不同的程序,例如:子进程从fork返回后,调用exec函数。
fork调用失败的原因
(1)系统中的进程太多。
(2)实际用户的进程数超出了限制。
注意:通常,使用fork之后,操作系统会给子进程分配PCB, 虚拟地址空间,用户级页表。而父进程和子进程的代码共享,数据也是共享的;但是当任意一方开始写入时,便以写时拷贝的方式各自拥有数据。
(2)vfork()函数



功能:创建一个子进程
返回值:父进程返回子进程的id,子进程返回0;出错返回-1;
我们利用vfork函数创建进程: 5 int glob=100;
6 int main()
7 {
8 pid_t pid;
9 pid=fork();
10 if(pid==-1)
11 { perror("fork");
12 exit(1);
13 }
14 if(pid==0)
15 {
16 sleep(5);
17 glob=200;
18 printf("child glob %d\n",glob);
19 exit(0);
20 }
21 else
22 printf("parent glob %d\n",glob);
23
24 return 0;
25 }
运行结果:



我们可以看到,子进程直接改变了父进程的值,这是因为子进程在父进程的虚拟地址空间运行。
(3)fork 和 vfork 的区别
1)vfork 用于创建一个子进程,而子进程和父进程共享地址空间。fork的子进程具有独立的虚拟地址空间。
2)vfork 保证子进程先运行,在它调用exec或者(exit)之后父进程才可能被调度执行。fork 的子进程和父进程谁先执行完全由调度器决定。
2. 进程终止
(1)进程退出场景
代码运行完毕,结果正确;
代码运行完毕,结果不正确;
代码异常终止。
(2)进程常见退出方法
1)正常终止:
从main函数返回;调用 exit 函数和 _exit 函数 。
2)异常退出:
ctrl + c,信号终止;
3)return 退出:
return 退出是一种常见的退出进程方法,执行return (n)等于执行exit (n),因为调用main 的运行时函数会将 main 的返回值当作exit 的参数。
(3)exit 函数和 _exit 函数exit 函数

函数原型:#include <unistd.h>

void exit( int status);

参数:status 定义了进程的终止状态,父进程通过wait 来获取该值。
_exit 函数

函数原型:#include <unistd.h>

void _exit (int status);

exit 函数最后也会调用 _exit 函数,但在调用exit 之前,还做了以下工作:

1.执行用户通过atexit 或者on_exit 定义的清理函数;

2.关闭所有打开的流,所有的缓存数据均被写入;

3.调用_exit 函数;
3. 进程等待
(1)为什么要有进程等待呢?
1)在前面讲过,子进程如果退出,父进程不管不顾,子进程就成为僵尸进程,就会造成内存泄漏的问题。
2)而僵尸进程是刀枪不入的,就连kill -9(9号信号)也无能为力,没有办法杀死。
3)父进程派发给子进程的任务是否被完成,父进程需要知道。例如:子进程在运行过程中,结果是对是错,或者是否正常退出。
4)父进程通过进程等待的方式,回收子进程资源,获取子进程的退出信息。
(2)进程等待的方法wait 方法

函数原型:#include <sys/types.h>

#include <sys/wait.h>

pid_t wait ( int *status);

参数:输出
a8b4
型参数,获取子进程的退出状态,不关心则可以设置为NULL;

返回值:成功返回被等待的进程pid ,失败返回 -1.代码实现wait 1 #include <sys/wait.h>
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <string.h>
5 #include <errno.h>
6
7 int main()
8 {
9 pid_t pid;
10 pid =fork();
11 if(pid ==-1)
12 {
13 perror("fork\n");
14 exit(1);
15 }
16
17 if(pid ==0)
18 {
19 sleep(20);
20 exit(10);
21 }
22 else{
23 int st;
}
16
17 if(pid ==0)
18 {
19 sleep(20);
20 exit(10);
21 }
22 else{
23 int st;
24 int ret=wait(&st);
25
26 if(ret>0&&(st&0x7F)==0){
27 printf("child exit code:%d\n",(st>>8)&0xFF);
28 }
29 else if(ret>0)
30 {
31 printf("sig code:%d\n",st&0x7F);
32 }
33 }
34 }
运行结果:


waitpid 方法

函数原型:pid_t waitpid (pid_t pid, int* status,int options);

参数: pid :

pid=-1,等待任一个子进程,与wait 等效;

pid > 0,等待其进程id 与pid 相等的子进程。

status:

WIFEXITED(status): 若为正常终止子进程的状态,则为真。(查看进程是否是正常退出的)

WEXITSTATUS(status ):若WIFEXITED非0,提取子进程退出码。(查看进程退出码 )
options:

WNOHANG: 若pid 指定的子进程没有结束,则wait_pid函数返回0,不予以等待。若正常结束,返回该子进程的id。
waitpid代码实现进程阻塞等待方式
1 #include <stdio.h>
2 #include <unistd.h>
3 #include <stdlib.h>
4 #include <sys/types.h>
5
6 int main()
7 {
8     pid_t pid=fork();
9     if(pid<0)
10     {
11         perror("fork");
12         return 1;
13     }
14     else if(pid==0){
15         printf("child is run,pid is :%d\n",getpid());
16         exit(257);}
17     else{
18         int status=0;
19         pid_t ret =waitpid(-1,&status,0);
20         printf("this is test for wait\n");
21         if(WIFEXITED(status)&& ret==pid){
22             printf("wait child 5s success,child return code is:%d\n",WEXITST    ATUS(status));
23           }
24         else
25         {
26             printf("wait child failed,return\n");
27             return 1;
28         }
29     }
30     return 0;
31 }
运行结果:



4. 进程程序替换
(1) 替换函数
有六种以exec开头的替换函数,统称为exec函数:#include <unistd.h>

int execl (const char* path,const char* arg, ...); // l(list) 表示参数采用列表;

int execlp( const char* path,const char *arg, ...); //p (path) 有 p 自动搜索环境变量PATH;

int execle( const char* path, const char *arg, ... ,char *const envp[ ] ); //e(env) 表示自己维护环境变量;

int execv ( const char* path , char *const argv[ ] );//v( vector ),参数用数组;
int execvp( const char* file , char *const argv[ ] );
int execve ( const char* path , char *const argv[ ] , char *const envp[ ] );
注:这些函数如果调用成功,则加载新的程序从启动代码开始执行,不再返回。
      如果调用出错,则返回 -1;
      exec 函数只有出错返回值没有成功返回值。



(2)exec 调用举例 int main()
4 {
5 char* const argv[]={"ps","-ef",NULL};
6 char* const envp[]={"PATH=/bin:/usr/bin","TERM=console",NULL};
7
8 execl("/bin/ps","ps","-ef",NULL);
9
10 execlp("ps","ps","-ef",NULL);
11
12 execle("ps","ps","-ef",NULL,envp);
13
14 execv("/bin/ps",argv);
15
16 execvp("ps",argv);
17
18 execve("/bin/ps",argv,envp);
19
20 exit(0);
21 }
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: