Linux系统编程之进程

1、什么是程序,什么是进程

程序是静态的概念,gcc xxx.c –o pro 磁盘中生成pro文件,叫做程序
进程是程序的一次运行活动,通俗点意思是程序跑起来了,系统中就多了一个进程

2、什么是进程标识符

每个进程都有一个非负整数表示的唯一ID,叫做pid,类似身份证
Pid=0: 称为交换进程(swapper)
作用—进程调度
Pid=1:init进程
作用—系统初始化
编程调用getpid函数获取自身的进程标识符;getppid获取父进程的进程标识符

3、fork()创建进程

  fork用来创建一个子进程。一个程序一调用fork函数,系统就为一个新的进程准备了前述三个段,首先,系统让新的进程与旧的进程使用同一个代码段,因为它们的程序还是相同的,对于数据段和堆栈段,系统则复制一份给新的进程,这样,父进程的所有数据都可以留给子进程,但是,子进程一旦开始运行,虽然它继承了父进程的一切数据,但实际上数据却已经分开,相互之间不再有影响了,也就是说,它们之间不再共享任何数据了。而如果两个进程要共享什么数据的话,就要使用另一套函数(shmget,shmat,shmdt等)来操作。现在,已经是两个进程了,对于父进程,fork函数返回了子程序的进程号,而对于子程序,fork函数则返回零,这样,对于程序,只要判断fork函数的返回值,就知道自己是处于父进程还是子进程中。

头文件:

#include <sys/types.h>
#include <unistd.h>

使用形式:

pid_t fork(void);

fork函数调用成功,返回两次
返回值为0, 代表当前进程是子进程
返回值为子进程的进程ID,代表当前进程为父进程
调用失败,返回-1

学习实例:

#include<stdio.h>
#include<string.h>
#include <sys/types.h>
#include <unistd.h>
#include<stdlib.h>
int main()
{
        pid_t retpid;
        int i = 0;
        retpid = fork();
        if(retpid == 0){
                while(1){
                        printf("this is child pid=%d\n",getpid());
                        sleep(1);
                        i++;
                        if(i==3){
                                exit(-1);
                        }
                }
        }else{
                while(1){
                printf("this is father pid=%d retpid=%d i=%d\n",getpid(),retpid,i);
                sleep(1);
                }
        }
        return 0;
}


运行结果:

this is father pid=2875 retpid=2876 i=0
this is child pid=2876
this is father pid=2875 retpid=2876 i=0
this is child pid=2876
this is father pid=2875 retpid=2876 i=0
this is child pid=2876
this is father pid=2875 retpid=2876 i=0
this is father pid=2875 retpid=2876 i=0
this is father pid=2875 retpid=2876 i=0
this is father pid=2875 retpid=2876 i=0
this is father pid=2875 retpid=2876 i=0
this is father pid=2875 retpid=2876 i=0
this is father pid=2875 retpid=2876 i=0
this is father pid=2875 retpid=2876 i=0
this is father pid=2875 retpid=2876 i=0
this is father pid=2875 retpid=2876 i=0
this is father pid=2875 retpid=2876 i=0
^C

4、vfork()创建进程

头文件:

#include <sys/types.h>
#include <unistd.h>

使用形式:

pid_t vfork(void);

返回值:
创建成功:子进程中返回 0,父进程中返回子进程 ID。
创建失败:返回 -1

注意:
1.vfork()创建子进程,在调用exec()之前或exit()之前,子进程与父进程共享数据段(与fork()不同,fork要拷贝父进程的数据段,堆栈段)
2.调用vfork()后,子进程先执行,父进程被挂起,直到子进程调用了exec或exit之后,父进程才执行。

学习例程:

#include<stdio.h>
#include<string.h>
#include <sys/types.h>
#include <unistd.h>
#include<stdlib.h>
int main()
{
        pid_t  pid1;
        pid_t  pid2;
        pid_t retpid;
        int i = 0;
        retpid = vfork();
        if(retpid == 0){
                while(1){
                        printf("this is child pid=%d\n",getpid());
                        sleep(1);
                        i++;
                        if(i==3){
                                exit(-1);
                        }
                }
        }else{
                while(1){
                printf("this is father pid=%d retpid=%d i=%d\n",getpid(),retpid,i);
                sleep(1);
                }
        }
        return 0;
}

运行结果;

this is child pid=2757
this is child pid=2757
this is child pid=2757
this is father pid=2756 retpid=2757 i=3
this is father pid=2756 retpid=2757 i=3
this is father pid=2756 retpid=2757 i=3
this is father pid=2756 retpid=2757 i=3
this is father pid=2756 retpid=2757 i=3
this is father pid=2756 retpid=2757 i=3
this is father pid=2756 retpid=2757 i=3
this is father pid=2756 retpid=2757 i=3
this is father pid=2756 retpid=2757 i=3
this is father pid=2756 retpid=2757 i=3
this is father pid=2756 retpid=2757 i=3
this is father pid=2756 retpid=2757 i=3
this is father pid=2756 retpid=2757 i=3
this is father pid=2756 retpid=2757 i=3
this is father pid=2756 retpid=2757 i=3
^C

5、进程退出

正常退出:

(1)main函数调用return
(2)进程调用exit(),标准c库
(3)进程调用_exit()或者_Exit(),属于系统调用
使用方法:

NAME
exit -cause normal process termination
SYNOPSIS
#include<stdlib. h>
void exit(int status);
NAME
_exit, _Exit -terminate the calling process
SYNOPSIS
#include <unistd. h>
void -exit(int status);
#include <stdlib. h>
void -Exit(int status);

异常退出:

(1)调用abort
(2)当进程收到某些信号时,如ctrl+C
(3)最后一个线程对取消(cancellation)请求做出响应

  不管进程如何终止,最后都会执行内核中的同一段代码。这段代码为相应进程关闭所有打开描述符,释放它所使用的存储器等。
  对上述任意一种终止情形、我们都希望终止进程能够通知其父进程它是如何终止的。对于三个终止函数(exit、_exit和_Exit),实现这一点的方法是,将其退出状态( exit status)作为参数传送给函数。在异常终止情况下,内核(不是进程本身)产生一个指示其异常终止原因的终止状态( termination status)。在任意一种情况下,该终止进程的父进程都能用wait或waitpid函数取得其终止状态。

6、wait、waitpid等待子进程退出

为啥要等待子进程退出:

僵尸进程

  父进程等待子进程退出,并收集子进程的退出状态,子进程退出状态不被收集,变成僵死进程(僵尸进程)

使用man查询等待函数:

NAME
wait, waitpid, waitid-wait for process to change state
SYNOPSIS
#include <sys/types. h>
#include <sys/wait. h>
pid_t wait(int *status);
pid_t waitpid(pid_t pid, int *status, int options);

wait

status:是一个整型数指针,指向终止进程的终止状态,如果不关心终止状态可指定为空指针
WEXITSTATUS(status) 当子进程是正常退出时,我们可以用这个宏来提取子进程的返回值,如果子进程调用exit(5)退出,WEXITSTATUS(status) 就会返回5;如果子进程调用exit(7),WEXITSTATUS(status)就会返回7。请注意,如果进程不是正常退出的,也就是说, WIFEXITED返回0,这个值就毫无意义。

waitpid

  从本质上讲,系统调用waitpid和wait的作用是完全相同的,但waitpid多出了两个可由用户控制的参数pid和options,从而为我们编程提供了另一种更灵活的方式。下面我们就来详细介绍一下这两个参数:
pid有四种情况:
1.pid==-1 等待任意子进程
2.pid>0 等待进程ID与pid相等的子进程
3.pid==0 等待组ID等于调用进程组ID的任意子进程
4.pid<-1 等待组ID等于pid绝对值的任意子进程

options控制waitpid的操作:
1.WCONTINUED,支持作业控制
2.WUNTRACED,支持作业控制
3.WNOHANG waitpid不阻塞,返回值为0

孤儿进程:

  父进程如果不等待子进程退出,在子进程之前就结束了自己的“生命”,此时子进程叫做孤儿进程
  Linux避免系统存在过多孤儿进程,会让init进程收留孤儿进程,变成孤儿进程的父进程

wait函数使用实例:

#include<stdio.h>
#include<string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include<stdlib.h>



int main()
{
        pid_t retpid;
        int i=0;
        int status = 10;
        retpid = fork();
        if(retpid == 0){
                while(1){

                        printf("this is child pid=%d\n",getpid());
                        sleep(1);
                        i++;
                        if(i==3){
                                exit(5);
                        }
                }
        }else{
                wait(&status);
                while(1){
                printf("this is father pid=%d retpid=%d i=%d status = %d\n",getpid(),retpid,i,WEXITSTATUS(status));
                //WEXITSTATUS(status)返回status的低八位
                sleep(1);
                }
        }
        return 0;
}

运行结果:

this is child pid=2865
this is child pid=2865
this is child pid=2865
this is father pid=2864 retpid=2865 i=0 status = 5
this is father pid=2864 retpid=2865 i=0 status = 5
this is father pid=2864 retpid=2865 i=0 status = 5
this is father pid=2864 retpid=2865 i=0 status = 5
this is father pid=2864 retpid=2865 i=0 status = 5
this is father pid=2864 retpid=2865 i=0 status = 5
this is father pid=2864 retpid=2865 i=0 status = 5
this is father pid=2864 retpid=2865 i=0 status = 5
^C

7、exec族函数、system()、popen()

这三个函数的功能都是去执行另外一段可执行程序,具体的区别参考:Linux中fork+exec、system和popen的区别.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值