Linux 进程基本函数

Linux 进程基本函数

  Linux系统中,进程的执行模式划分为用户模式内核模式,当进程运行于用户空间时属于用户模式,如果在用户程序运行过程中出现系统调用或者发生中断事件,就要运行操作系统(即核心)程序,进程的运行模式就变为内核模式,在该模式下运行的进程可以执行机器特权指令,而且该进程的运行不受用户的干预

① 在Linux操作系统中,通过fork()系统调用来创建子进程

目标

创建进程

头文件

#include <unistd.h>

函数原型

pid_t_result=fork(void)

参数

返回值

-1  如果出错

0  返回到子进程

pid 将子进程的进程id返回给父进程

将执行fork的操作进程称为父进程,被fork()创建的进程称为该进程的子进程,当父进程执行fork操作时,操作系统内核执行如下任务完成进程的创建工作:

       1.为子进程分配新的内存块和内核数据结构

       2.复制原来的进程信息到新的进程,即子进程和父进程具有相同的可执行代码[A1]

       3.向运行进程集添加新的进程

       4.fork执行结束后,将控制返回给2个进程,此时两个进程可独立执行,执行顺序取决于进程调度

 

/*

  相同的可执行代码是指哪部分?

  从例子的结果看应该是fork()之后的可执行代码

  Fork执行结束后,将控制返回给2个进程,此时两个进程可独立执行,执行顺序取决于进程调度

*/

#include <stdio.h>

main(){

    int ret_from_fork,mypid;

    mypid=getpid();

    printf("Before :my pid is %d\n",mypid );

    ret_from_fork=fork();

    sleep(1);

    printf("After:my pid is %5d,ret_from_fork:%5d,parent pid :%5d\n",getpid(),ret_from_fork,getppid());

}

[root@centos1 process]# ./fork

Before :my pid is 22526

After:my pid is 22526,ret_from_fork:22527,parent pid :21968

After:my pid is 22527,ret_from_fork: 0,parent pid :22526

 

父进程创建子进程后,子进程除了具有相同的代码段拷贝外,也具有相同的数据段,

即父进程的全局变量名称和值子进程也会一起拷贝过去,但他们之间是独立的,可独立改变相互不受影响

#include <sys/types.h>  //pid_t 类型的在此

#include <unistd.h>

#include <stdio.h>

#include <stdlib.h>   //exit 用

int glob=6;  //全局变量

int main(){

    int local;

    pid_t pid;

    local = 8;

    if( (pid=fork()) == 0){

        sleep(2);

    }else if(pid>0){

        glob =60;

        local =80;

        sleep(10);

    }

    printf("glob=%d,local=%d,mypid=%d\n",glob,local,getpid());

    exit (0);

}

[root@centos1 process]# ./var

glob=6,local=8,mypid=22800

glob=60,local=80,mypid=22799

第一行输出为子进程,第二行未父进程的输出,说明父子进程由相同的代码,但他们变量之间相互不影响

一般情况下,父进程创建子进程是为了执行特定的任务,通过执行exec家族系列调用让子进程执行新的任务

此时,由exec调用提供的命令的指令代码替换子进程的代码,相当于对子进程进行了换脑

int execl(const char*path,const char* arg0,const char* arg1,.... NULL)

int execlp(const char*path,const char* arg0,const char* arg1,.... NULL)

int execv(const char*path,const char* argv[])

int execvp(const char*path,const char* argv[])

目标

在指定路径中查找并执行一个文件

头文件

#include <unistd.h>

函数原型

result=execvp(const *file,const char *argv[])

参数

file 要执行的文件名

argv 字符串数组

返回值

-1 如果出错

若成功,execvp 没有返回值

excel,excelp完全相同

excev,execvp 完全相同

excel,excev要求提供可执行文件的绝对路径或相对路径名

带p的 使用$PATH环境变量查找程序

主要区别是:

       1.可执行文件的查找方式:不是P结尾的都是完整的目录路径,p结尾的可只给出文件名,系统自动从环境变量中进行查找

       2.参数的传递方式:

       有2种方式,逐个列举;将所有的参数整体构造指针数组传递,以函数名第5位字母来区分l(list)的表示逐个列举方式 ,语法为char *arg;       字母为v(vertor)的表示将所有参数整体构造指针数组传递,语法为: *const argv[]

exec系列调用没找到和执行文件返回-1 ,否者进程用可执行文件替换它的代码、数据和堆栈

过程:

1.将制定的程序复制到它的进程

2.用制定的字符串数组作为argvp[]传给这个程序

3.运行这个程序

#include <unistd.h>

#include <stdio.h>

#include <stdlib.h>

main(){

    char *arglist[1];

    pid_t pid;

    arglist[0]="-l";

    arglist[1]="-a";

    printf("parent:%d\n",getpid());

    pid=fork();

    if( pid==0 ){

        printf("son1  pid is:%d\n",getpid() );

        execvp("ls",arglist);

        printf("son2  pid is:%d\n",getpid() );

    }else{

        printf("parnet pid:%d\n",getpid() );

    }

    printf("in %d\n",getpid());

}/**

execvp 后的当前进程的代码不再执行

root@centos1 c]# ./exec

parent:26307

parnet pid:26307

in 26307

son1  pid is:26308

[root@centos1 c]# .  ..  exec  exec.c  fork.c  var.c

**/

 

 僵尸进程

当父进程还没有结束而子进程结束运行,同时父进程没有使用wait系统调用获取子进程的结束状态时子进程就成为僵尸进程父进程先结束不会产生僵尸进程。

       僵尸进程没有任何代码、数据和堆栈,占用不了多少资源,但它存在于系统的任务列表中。在进程表里仍占了1个位置占用进程号一般要避免出现这种情况

当使用ps查看进程时如果进程名称旁边出现defunct,则表明该进程为僵尸进程

 

#include <stdio.h>

#include <unistd.h>

void parent_code(int delay){

    sleep(delay);

}

main(){

    pid_t pid;

    int status;

 

    pid=fork();

    if(pid == 0){

 

    }

    if(pid>0){

        parent_code(20);

    }

}/*

[root@centos1 c]# gcc -o zombie zombie.c

[root@centos1 c]# ./zombie&

[1] 28577

[root@centos1 c]# ps

   PID TTY          TIME CMD

 25201 pts/0    00:00:00 bash

 28577 pts/0    00:00:00 zombie

 28578 pts/0    00:00:00 zombie <defunct>

 28580 pts/0    00:00:00 ps

 */

 

为了防止子进程成为僵尸进程,一般在父进程调用wait()系统调用等待子进程的结束并获取子进程的返回状态

wait调用做2件事:

首先暂停调用它的进程直到子进程结束,然后取得子进程结束时传给exit的值

目标

等待子进程的结束

头文件

#include <sys/wait.h>

#include <sys/types.h>

函数原型

pid_t pid=wait(int *status)

参数

status指向一个保存子进程返回状态的整型变量

返回值

如果不存在子进程,返回-1

若有任何一个子进程结束,则返回该子进程的pid并保存其返回状态在status中,同时wait调用也结束

 

#include <sys/wait.h>

#include <sys/types.h>

#include <unistd.h>

#include <stdio.h>

#include <stdlib.h>

void child(int delay){

    sleep(delay);

    exit(0);

}

void parent(int *status){

    wait(status);

}

 

main(){

    pid_t pid;

    int status;

    printf("Before:%d\n",getpid());

    pid=fork();

    if(pid == 0){

        child(10);

    }

    if(pid >0 ){

        printf("pid =%d\n",getpid());

        parent(&status);

        printf("status =%d\n",status);

    }

}/*

[root@centos1 c]# ./a.out

Before:14901

pid =14901

status =0

*/

 

 目标

等待某个子进程的结束

头文件

#include <sys/wait.h>

#include <sys/types.h>

函数原型

pid_t pid=waitpid(pid_t pid,int *status,int options)

参数

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

pid>0 等待进程id时pid的子进程

pid=0 等待其组id等于调用进程组id的任一子进程

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

options选项:

WNOHANG 表示如果没有任何已经结束的子进程则马上返回,不等待

WUNTRACED 如果子进程进入暂停执行情况则马上返回,但结束状态不予以理会

0 作用和wait一样,阻塞父进程,等待子进程结束

返回值

如果不存在子进程,返回-1

若有指定子进程结束,则返回该子进程的pid并保存其返回状态在status中,同时waitpid调用也结束

 

 

说明

WIFEXITED(status)

如果子进程正常结束,则为非0值,此时可调用WEXITSTATUS(status)

取得子进程exit()返回的结束代码

WIFSIGNALED(status)

如果子进程是因为信号则此宏的值为真,

此时可用WIERMSIG(status)取得子进程因信号而终止的信号代码

WIFSTOPPED(status)

如果子进程处于暂停执行情况则此宏为真.采用WSTOPPSIG(status)

取得引发子进程暂停的信号代码

上述代码可防止僵尸进程的产生

waitpid()亦可实现wait()调用类似功能

多数情况下会使用waitpid()

他们的主要区别是:

1.wait()只能得到任何一个子进程结束的状态,当一个父进程有多个子进程时,在某个子进程结束,则wait可得到其结束状态

此时无法再得到其它子进程的结束状态,此时容易产生其它子进程的僵尸进程

2.wait()调用属于阻塞调用,父进程执行该指令后,器等待子进程结束之后才能执行它后面的代码,

而waitpid()可提供非阻塞调用的方式

3.waitpid()调用可以等待指定的子进程具有比wait多的功能

子进程退出状态检测的宏

/////////////////////////////////////////////////////////////////////////////////////////

进程编程基本函数

pid_t fork(void)

功能:创建一个新进程

参数:无参

返回值:成功: 1、在父进程里面返回一个大于0的值(子进程PID)2、在子进程里面返回0

出错:返回-1

pid_t getpid(void):函数返回调用进程本身的PID

pid_t getppid(void):函数返回调用进程的父进程的PID

pid_t wait(int *status)

功能:阻塞接收子进程的退出状态,回收子进程的资源(收尸)

参数:status:用来接收子进程退出状态的值,这个值不是真正的子进程返回的值,这个值右移8位,才是子进程真正返回的值,或者使用WEXITSTATUS(status)。

返回值:成功返回回收子进程的PID,失败返回-1(在调用进程无子进程的情况下失败)

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

功能:回收子进程的退出状态

参数:pid:

pid>0:只等待进程ID等于pid的子进程,不管已经有其他子进程运行结束退出了,只要指定的子进程还没有结束,waitpid就会一直等下去。

pid=-1:等待任何一个子进程退出,此时和wait作用一样。

pid=0:等待其组ID等于调用进程的组ID的任一子进程。

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

status:同wait

options:

WNOHANG:若由pid指定的子进程并不立即可用,则waitpid不阻塞,此时返回值为0

WUNTRACED:若某实现支持作业控制,则由pid指定的任一子进程状态已暂停,且其状态自暂停以来还未报告过,则返回其状态。

0:同wait,阻塞父进程,等待子进程退出。

返回值:

正常:结束的子进程的进程号

使用选项WNOHANG且没有子进程结束时:0

调用出错:-1

wait(NULL) ==> waitpid(-1,NULL,0)

posted @ 2020-09-14 08:34  陈木  阅读(260)  评论(0)    收藏  举报