进程的基本操作及应用。


一。进程创建:

主要用fork()函数创建进程,当调用fork时,内核会做:

(1)分配新的内存块和内核数据结构给子进程。

(2)将父进程部分数据内容拷贝至子进程。

(3)添加子进程到系统进程列表当中。

(4)fork返回,开始调度器调度。

举例:

 #include<stdio.h>
  2 #include<unistd.h>
  3 #include<stdlib.h>
  4 #include<sys/types.h>
  5 
  6 int main(void)
  7 {
  8         pid_t pid;
  9         printf("before: pid is %d\n",getpid());
 10         if( (pid=fork())==-1)perror("fork()"),exit(1);
 11         printf("after:pid is %d,fork return %d\n",getpid(),pid);
 12         sleep(10);
 13 }

所以,fork之前父进程独立执行,fork之后,父子两个执行流分别执行。

fork的返回值:子进程返回0,父进程返回的是子进程的id。

fork调用失败的原因:(1)系统中有太多的进程。(2)实际用户的进程数超越了限制。

Linux中还有一个vfork函数:

(1)它用于创建一个子进程,而子进程与父进程共享地址空间,fork的子进程具有独立地址空间。

(2)vfork保证子进程先运行,在它调用exec之后父进程才可能被调用。

注:fork不能通过更改子进程中的变量值来改变父进程中的变量值,而vfork可以,因为它的子进程在父进程的地址空间运行。

举例:

#include<stdio.h>
  2 #include<stdlib.h>
  3 #include<unistd.h>
  4 
  5 int flag=10;
  6 int main(void)
  7 {
  8     pid_t pid;
  9     if((pid=vfork())==-1)perror("fork()"),exit(1);
 10     if(pid==0)
 11     {
 12         sleep(3);
 13         flag=1;
 14         printf("child flag is %d\n",flag);
 15         exit(0);
 16         }
 17     else{
 18     printf("parent flag=%d\n",flag);
 19     }
 20 }

这是vfork函数应用,结果是:

所以它将父进程的变量值也改变了。

再来看看fork函数:

#include<stdio.h>
  2 #include<stdlib.h>
  3 #include<unistd.h>
  4 
  5 int flag=10;
  6 int main(void)
  7 {
  8     pid_t pid;
  9     if((pid=fork())==-1)perror("fork()"),exit(1);
 10     if(pid==0)
 11     {
 12         sleep(3);
 13         flag=1;
 14         printf("child flag is %d\n",flag);
 15         exit(0);
 16         }
 17     else{
 18     printf("parent flag=%d\n",flag);
 19     }
 20 }

[ymk@localhost d3]$ gcc 3.c
[ymk@localhost d3]$ ./a.out
parent flag=10
[ymk@localhost d3]$ child flag is 1


fork没有改变父进程的变量值,而且子进程也是在父进程运行完之后才运行的。

二。进程终止

1.退出场景:

(1)代码运行完毕,结果正确。

(2)代码运行完毕,结果不正确。

(3)代码异常终止。

2.退出方法:

(1)正常终止:从main返回;调用exit;_exit

(2)异常退出:ctrl+c,信号终止;abort();kill;

#include<stdio.h>
  2 #include<stdlib.h>
  3 #include<unistd.h>
  4 
  5 int main(void)
  6 {
  7     while(1)
  8     {
  9         printf("haha\n");
 10         sleep(1);
 11     }
 12 }

[ymk@localhost d3]$ ps -el | grep a.out
0 S  1000  39619  37604  0  80   0 -  1042 hrtime pts/0    00:00:00 a.out
[ymk@localhost d3]$ kill 39619
[ymk@localhost d3]$ ps -el | grep a.out
[ymk@localhost d3]$ 

用Kill终止进程。

三。进程等待

1.父进程通过进程等待的方式,回收子进程资源。获取子进程退出信息。

2.wait:回收僵尸进程。

3.waitpi:

 pid _t waitpid(pid_t pid,int *status,int options);

(1)pid:pid=-1:等待任何一个子进程死亡,与wait一样。;pid>0:本进程的子进程;pid<-1:|pid|进程组的任一个子进程死亡

               pid=0:调用者进程所在进程组的任一个子进程死亡。

(2)status:

WIFEXITED:若为正常终止子进程返回的状态,则为真。

WEXITSTATUS:若WIFEXITED非0,提取子进程退出码。

(3)options:

WNOHANG:若Pid指定的子进程没有结束,则waitpid()函数返回0,不予以等待,若正常结束,则返回子进程id。

注:如果子进程已经退出,调用wait时,会立即返回,释放资源,获得子进程退出信息。

如果在任意时刻调用wait/waited,子进程存在正常运行,则进程可能阻塞。

如果不存在该子进程,则立即出错返回。

4.举例:

#include<stdio.h>
  2 #include<stdlib.h>
  3 #include<sys/wait.h>
  4 #include<unistd.h>
  5 
  6 int main()
  7 {
  8     pid_t pid;
  9     pid=fork();
 10     if(pid<0){
 11     printf("fork error\n");
 12     return 1;
 13     }
 14     else if(pid==0){
 15     printf("child is run,pid is %d\n",getpid());
 16     sleep(2);
 17     exit(257);
 18     }
 19     else{
 20     int status=0;
 21     pid_t r=waitpid(-1,&status,0);
 22     printf("this is test for wait\n");
 23     if(WIFEXITED(status) && r==pid){
                                 printf("wait child 5s success, child return code is %d\n",WEXITSTATUS(st    atus));
 25     }
 26     else{
 27     printf("wait child failed,return \n");
 28     return 1;
 29     }
 30     }
 31     return 0;
 32 }
  

[ymk@localhost d3]$ ./a.out
child is run,pid is 40492
this is test for wait
wait child 2s success, child return code is 1


这个父进程等待了2秒才运行。

四。进程替换。

1.exec函数:execvp:不会创建进程,会用参数给定的程序替换当前的进程。

2.举例:

#include<stdio.h>
  2 #include<unistd.h>
  3 int main(void)
  4 {
  5         char *argv[]={
  6                 "ls","-l",NULL};
  7         printf("before execvp\n");
  8         execvp("ls",argv);
  9         printf("after execvp");
 10 }

[ymk@localhost day07]$ ./a.out
before execvp
total 40
-rw-rw-r--. 1 ymk ymk  483 Jul  8 20:30 a.c
-rwxrwxr-x. 1 ymk ymk 8656 Jul  8 20:30 a.out
-rw-rw-r--. 1 ymk ymk  425 Jul  8 20:13 i.c
-rw-rw-r--. 1 ymk ymk  304 Jun 30 22:57 o.c
-rw-rw-r--. 1 ymk ymk  278 Jul  1 01:12 p.c
-rw-rw-r--. 1 ymk ymk  933 Jun 30 05:46 s.c
-rw-rw-r--. 1 ymk ymk  170 Jul  8 20:30 x.c
-rw-rw-r--. 1 ymk ymk  299 Jul  8 20:30 z.c

用ls -l代替了输出after execvp 。

五。制作一个简单的shell:

#include<stdio.h>
  2 #include<stdlib.h>
  3 #include<unistd.h>
  4 #include<sys/wait.h>
  5 #include<ctype.h>
  6 #include<string.h>
  7 
  8 #define MAXLINE 1024
  9 char cmdline[MAXLINE+1];
 10 int argc=0;
 11 char *argv[8];
 12 
 13 void init(void)
 14 {
 15         argc=0;
 16         memset(cmdline,0x00,sizeof(cmdline));
 17 }
 18 
 19 int read_cmd()
 20 {
 21         return fgets(cmdline,MAXLINE,stdin)==NULL?0:1;
 22 }
int prase_cmd()
 25 {
 26         int flag=0;
 27         int i;
 28         for(i=0;cmdline[i]!='\0';i++)
 29         {
 30                 if(flag==0 && !isspace(cmdline[i])){
 31                         flag=1;
 32                         argv[argc]=cmdline+i;
 33                         argc++;
 34                 }
 35                 else if(isspace(cmdline[i])){
 36                         flag=0;
 37                         cmdline[i]='\0';
 38                 }
 39         }
 40         argv[argc]=NULL;
 41 }
int execute_cmd()
 44 {
 45         if(fork()==0)
 46         {
 47                 execvp(argv[0],argv);
 48                 exit(1);
 49         }
 50 }
 51 
 52 void print_cmd()
 53 {
 54         int i;
 55         for(i=0;i<argc;i++)
 56         {
 57                 printf("targv[%d]=%s\n",i,argv[i]);
 58         }
 59 }
int main(void)
 62 {
 63         while(1){
 64                 init();
 65                 printf("shell >");
 66                 if(read_cmd()==0)
 67                         break;
 68                 prase_cmd();
 69                 print_cmd();
 70                 execute_cmd();
 71         }
 72 }


六。fopen,system与fork区别:

(1)system:在执行期间调用进程会一直等待shell命令执行完成。执行shell命令最后返回是否执行成功,
 (2) popen:无须等待shell命令执行完成就返回     (并行执行)。执行命令并且通过管道和shell命令进行通信。
  (3)popen后需要调用pclose防止子进程变成”僵尸”状态。 
  (4)fork :执行期间父进程等待子进程的退出码。

七。封装fork/wait等操作, 编写函数 process_create(pid_t* pid, void* func, void* arg), func回调函数就是子进程执行的入口函数, arg是传递给func回调函数的参数.

#include<stdio.h>
  2 #include<unistd.h>
  3 #include<sys/wait.h>
  4 #include<stdlib.h>
  5 
  6 void process_create(pid_t* pid,void* func,void* argv)
  7 {
  8     if((*pid=fork())<0){
  9     perror("fork()"),exit(1);
 10     }
 11 
 12     else if(*pid==0){
 13     ((int(*)())func) (((char**)argv)[0],(char**)argv);
 14     perror("func");
 15     }
 16     else{
 17     int st;
 18     while(wait(&st)!=*pid);
 19     }
 20 }
 21 int main()
 22 {
 23     pid_t pid;
pid_t pid;
 24     char *argv[]={"ls","-l",NULL};
 25     process_create(&pid,execvp,argv);
 26     return 0;
 27 }


   


[ymk@localhost d3]$ ./a.out
total 56
-rw-rw-r--. 1 ymk ymk  274 Jul  8 19:13 1.c
-rw-rw-r--. 1 ymk ymk  282 Jul  8 19:38 2.c
-rw-rw-r--. 1 ymk ymk  282 Jul  8 19:46 3.c
-rw-rw-r--. 1 ymk ymk  124 Jul  8 19:52 4.c
-rw-rw-r--. 1 ymk ymk  602 Jul  8 20:27 5.c
-rw-rw-r--. 1 ymk ymk  148 Jul  8 20:35 6.c
-rw-rw-r--. 1 ymk ymk  933 Jul  8 21:13 7.c
-rw-rw-r--. 1 ymk ymk  434 Jul  8 21:27 8.c
-rwxrwxr-x. 1 ymk ymk 8792 Jul  8 21:27 a.out
-rw-rw-r--. 1 ymk ymk 1277 Jul  7 23:17 p.c
-rw-rw-r--. 1 ymk ymk  933 Jul  8 20:58 s.c
-rw-rw-r--. 1 ymk ymk  982 Jul  8 21:12 shell.c



以上就是进程的各种操作及应用。











猜你喜欢

转载自blog.csdn.net/ymk1507050118/article/details/80965826