Linux 进程及其创建

来源:互联网 发布:老外吃中国菜知乎 编辑:程序博客网 时间:2024/04/24 15:53

1、Unix中的父子进程,unix是多进程操作系统,进程的启动也是具有先后顺序的,一般的情况下是系统先启动0进程 ,然后由0进程启动进程1和进程2,在由进程1和进程2启动其他进程。


2、unix中进程由进程PID唯一标识;

     函数getpid()可以获取当前进程PID;

     函数getppid()用于获取当前进程父进程的PID;

     函数getuid()用于获取当前用户的ID。


3、fork()函数创建子进程

fork()是通过赋值父进程的资源来创建子进程,子进程将会复制父进程除了代码区之外的所有区域,包括缓存。

pid_t fork()函数成功执行范围子进程PID或者0,而失败返回-1,所以可以通过返回值来隔离出父子进程。


用fork()创建一个子进程,子进程会从fork()当前位置执行代码,fork()之前的代码只有父进程执行;


fork()函数会返回两次,父进程返回的是子进程的PID,而子进程返回0.

#include <stdio.h>#include <stdlib.h>#include <unistd.h>void main(){   printf("begin\n");   pid_t pid = fork();   printf("end%d\n",pid);}
后面的 printf("end%d\n",pid)父子进程都会执行,打印出各自的返回值,一个是子进程的PID,一个是子进程返回的0.


4.根据返回值隔离出父子进程

//捕捉父子进程#include <stdio.h>#include <unistd.h>void main(){//父子进程使用不同的分支执行不同的事   pid_t pid = fork();   //printf("%d\n",pid);   if (!pid){//父子进程都会执行判断,但是只有子进程可以进来      printf("我是子进程%d,我的父进程是%d\n",getpid(),getppid());   }   else{//父进程执行的分支,子进程不会执行      printf("我是父进程%d,我的子进程是%d\n",getpid(),pid);   }}

上面的代码根据返回值隔离出了父子进程,可以在各自的返回内执行各自的代码。


5、父子进程出出现了对文件的操作

用fork()创建子进程时,如果有文件描述符存在,则子进程会赋值文件描述符,但是不会赋值文件表,此时父子进程会公用一个文件表,意思就是说父子进程的文件描述符对应的是同一张文件表,所以操作的是同一个文件。

//进程的文件测试#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <fcntl.h>void main(){   //pid_t pid = fork();//如果这样会出现什么效果,父进程和子进程都有自己的描述符,和文件表。   int fd = open("a.txt",O_RDWR|O_CREAT|O_TRUNC,0666);   if (fd == -1) perror("open"),exit(-1);   pid_t pid = fork();   if (pid == 0){      write(fd,"hello",5);      close(fd);      exit(0);   }   write(fd,"12345",5);   close(fd);}
上面代码的执行结果表现为子进程首先写进了hello到a.txt中,随后父进程将12345追加到hello后面(这里要说明的一点是父子进程的执行先后是随机的),为什么父进程不是将原来的hello覆盖掉呢?这是因为父子进程的文件描述符对应了一张文件表,而子进程写完之后,它的文件偏移等信息会反应到文件表中,父进程进行写操作的时候,子进程造成的文件偏移是保留下来了的。如果把fork()函数放到最开始执行,则文件描述符和文件表都会被复制,此时的执行才表现为12345覆盖掉hello。


6、fork创建的子进程,父子进程随机运行,如果子进程先结束,就会给父进程发送消息,让父进程回收子进程的资源,如果父进程没哟收到子进程发送的消息,则子进程的资源无法回收,编程僵尸进程。


如果父进程先于子进程结束,则子进程会编程孤儿进程,在unix进程管理中,此时孤儿进程会被进程1 init接管,此时init进程 就是新的父进程,但是在我的系统下Ubuntu14.04LTS中,表现出来的却是被一个叫做init-user的进程接管了。

#include <stdio.h>#include <stdlib.h>#include <unistd.h>int main(){   pid_t pid = fork();   if (!pid){//子进程      printf("子进程是%d,父进程是%d\n",getpid(),getppid());      sleep(3);      printf("子进程是%d,父进程是%d\n",getpid(),getppid());      exit(0);   }   sleep(1);   if (pid){      printf("pid = %d\n",getpid());   }   exit(0);}
程序中有意设置延迟让父进程先结束,此时表现出来的是父进程结束之后子进程被init-user接管,而不是init进程接管。

而这个进程PID4173不是固定的,重新执行就变成了其他的数字。我给这个进程发送信号9,想杀死这个进程,指令如下:

kill -9 4173,它的效果就是一个注销的效果,立马回到刚启动好系统的登录页面。我想这是linux的进程优化吧。让一个叫init--user的进程来接管孤儿进程,而不是init进程来接管。


7、进程的退出

进程的正常退出表现为:

a、return退出;b、exit()退出;c、执行_exit()或_Exit()退出;d、最后一个线程退出;e、主线程退出

进程的非正常退出表现为:

a、被信号打断

b、最后一个线程被取消

//观察fa函数会被几次调用//理论:可以使用atexit注册一些函数,这些函数会在执行exit或者return之前调用一次。#include <stdio.h>#include <stdlib.h>void fa(void){   printf("fa called\n");}int main(){   atexit(fa);//注册退出前的调用函数   //printf("begin\n");   pid_t pid=fork();   if (!pid){//取子进程   printf("begin\n");      //_exit(0);   }   //exit(0);   //_exit(0);不会打印fa函数的内容,代表立即退出   //else{     // printf("end\n");      //exit(0);//父进程结束,将不会执行下面的return   //}   printf("end\n");   return 0;//调用fa函数}

8、wait(),waitpid()

两个函数均可让父进程等待子进程的结束,并且取得子进程的退出状态和退出码(就是return后面的值或者exit()括号中的值)。

两者的区别在于wait()比较固定,等待任意子进程的结束即可,然后返回结束子进程PID,而waitpid()表现更为灵活,可以选择等待某一个子进程。

wait函数如下:

pid_twait(int *statuc),其中statuc参数是一个传出参数,用来带出结束子进程的退出码和退出状态;

//进程阻塞#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <sys/wait.h>int main(){   pid_t pid = fork();   if (!pid){      sleep(2);      printf("子进程%d即将结束",getpid());      exit(100);   }   int status;   pid_t wpid = wait(&status);//如果子进程不结束,父进程将会阻塞   printf("等待到了%d的退出\n",wpid);   if (WIFEXITED(status)){      printf("正常退出,退出码:%d\n",WEXITSTATUS(status));   }}

宏函数WIFWXITED(status)可以判断正常退出;

宏函数WEXITSTATUS(status)可以取出退出码;


pid_twaitpid(pid_t pid,int *status,int option)

参数status和wait()一样,pid可以设定等待哪些子进程,option可以设定是否等待。

 

pid 的值可能是:

==-1   等待任意子进程,与wait()等效;

>0     等待指定子进程

==0    等待本进程组任意子进程

<-1    等待进程组ID等于pid绝对值的任一子进程

option的值:

0    阻塞,父进程等待;

WNOHANG  不阻塞,直接返回0

返回值:有子进程结束时返回子进程pid

出错返回-1

如果阻塞方式,没有子进程结束继续等待;

如果是WNOHANG,没有子进程返回0;

//waitpid函数#include <stdio.h>#include <stdlib.h>#include <sys/wait.h>#include <unistd.h>int main(){   pid_t pid = fork();   if (pid == -1) perror("fork"),exit(-1);   if (!pid){      sleep(1);      printf("子进程%d即将结束\n",getpid());      exit(100);   }   pid_t pid2 = fork();   if (!pid2){      sleep(3);      printf("子进程%d即将结束\n",getpid());      exit(200);   }   int status;   pid_t wpid = waitpid(pid2,&status,/*WNOHANG*/0);   if(WIFEXITED(status)){      printf("等待到了%d子进程,退出码:%d\n",wpid,WEXITSTATUS(status));   } }

1 0
原创粉丝点击