Linux 进程及其创建

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));
   } 
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值