我是靠谱客的博主 虚幻自行车,最近开发中收集的这篇文章主要介绍Linux系统编程之进程Linux系统编程之进程,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

Linux系统编程之进程

(一)进程的创建

通过一个例子来对进程创建fork函数进行理解

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

int main()
{
        pid_t pid;
        pid_t pid2;
        pid = getpid();

        printf("Before fork:%dn",pid);
        fork();
        pid2 = getpid();
        printf("After fork:%dn",pid2);

        if(pid == pid2)
                printf("This is father printn");
        else
                printf("This is children print,pid = %dn",pid2);

        return 0;
}

通过pid的值来判断进程的执行情况,运行后会出现以下界面:

在这里插入图片描述

通过分析打印的内容,可以看出,fork执行前pid为55

fork执行后创建新进程后,会先执行父进程的打印函数,然后子进程的打印函数,分别进入两个分支,将信息打印出来。

fork后父进程与子进程都会执行fork之后的代码。

fork编程实战

一个现有进程可以调用fork函数创建一个新进程。

​ 由fork创建的新进程被称为子进程,fork函数被调用一次,但返回两次。两次返回的唯一区别是子进程的返回值是0,而父进程的返回值则是新子进程的进程ID。将子进程ID返回给父进程的理由是:因为一个进程的子进程可以有多个,并且没有一个函数使一个进程可以获得其所有子进程的ID。fork使子进程得到返回值0的理由是:一个进程只会有一个父进程,所以一个子进程总是可以调用getppid以获得其父进程的进程ID(进程ID 0 总是由内核交换进程使用,所以一个子进程的进程ID不可能为0)。

​ 子进程和父进程继续执行fork调用之后的指令。子进程是父进程的副本。例如,子进程获得父进程数据空间、堆和栈的副本。注意,这是子进程所拥有的副本。父、子进程并不共享这些存储空间部分。父、子进程共享正文段。

​ 由于在fork之后经常跟随着exec,所以现在的很多实现并不执行一个父进程数据段、栈和堆的完全复制。作为替代,使用了写时复制(Copy-On-Write,COW)技术。这些区域由父、子进程共享,而且内核将它们的访问权限改变为只读的。如果父、子进程中的任一个试图修改这些区域,则内核只为修改区域的那块内存制作一个副本,通常是虚拟存储器系统中的一“页”。

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

int main()
{
        pid_t pid;
        int data;
        while(1){
                printf("Please input a data:n");
                scanf("%d",&data);
                if(data == 1){
                        pid = fork();
                        if(pid > 0){

                        }
                        else if(pid == 0){
                                while(1){
                                        printf("Do net request,pid=%d",getpid());
                                        sleep(3);
                                }

                        }
                }else{
                        printf("wait ,do nothingn");

                }
        }

        return 0;
}


vfork函数创建进程

vfork函数也可以创建进程,与fork有什么区别呢?

关键区别一:vfork直接使用父进程存储空间,不拷贝。

关键区别二:vfork保证子进程先运行,当子进程调用exit退出后,父进程才执行。

练习如下:

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

int main()
{
        pid_t pid;
        int cnt = 0;
        pid = vfork();

        if(pid > 0){
                while(1){
                        printf("cnt = %dn",cnt);
                        printf("This is father print,pid = %dn",getpid());
                        sleep(1);
                }

        }else if(pid == 0){
                while(1){
                        printf("This is children print,pid = %dn",getpid());
                        sleep(1);
                        cnt++;
                        if(cnt == 3){
                                exit(0);
                        }
                }

        }


        return 0;
}

进程退出

(一)正常退出

1.main函数调用return

2.进程调用exit(),标准C库

3.进程调用_exit()或者__Exit(),属于系统调用

补充:

1.进程最后一个线程返回

2.最后一个线程调用pthread_exit

(二)异常退出

1.调用abort

2.当进程收到某些信号时,如ctrl+C

3.最后一个线程对取消(cancellation)请求做出响应

总结

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

(二)父进程等待子进程退出

1.父进程等待子进程退出,并收集子进程的退出状态。

#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
#include<stdlib.h>
#include<wait.h>
int main()
{
        pid_t pid;
        int cnt = 0;
        int status=10;
        pid = fork();

        if(pid > 0){
                wait(&status);
                printf("Child quit,child status %dn",WEXITSTATUS(status));
                while(1){
                        printf("cnt = %dn",cnt);
                        printf("This is father print,pid = %dn",getpid());
                        sleep(1);
                }

        }else if(pid == 0){
                while(1){
                        printf("This is children print,pid = %dn",getpid());
                        sleep(1);
                        cnt++;
                        if(cnt == 5){
                                exit(3);
                        }
                }

        }


        return 0;
}

2.子进程退出状态不被收集,变成僵死进程(僵尸进程)。

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

int main()
{
        pid_t pid;
        int cnt = 0;
        pid = vfork();

        if(pid > 0){
                while(1){
                        printf("cnt = %dn",cnt);
                        printf("This is father print,pid = %dn",getpid());
                        sleep(1);
                }

        }else if(pid == 0){
                while(1){
                        printf("This is children print,pid = %dn",getpid());
                        sleep(1);
                        cnt++;
                        if(cnt == 3){
                                exit(0);
                        }
                }

        }


        return 0;
}

父进程没有获取子进程的状态信息,子进程变成僵尸进程。

父进程调用wait不会让子进程变成僵尸进程。

相关函数:

NAME
wait, waitpid, waitid - wait for process to change state

SYNOPSIS
#include <sys/types.h>
#include <sys/wait.h>

   pid_t wait(int *wstatus);

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

   int waitid(idtype_t idtype, id_t id, siginfo_t *infop, int options);
                   /* This is the glibc and POSIX interface; see
                      NOTES for information on the raw system call. */

如果其所有子进程都还在运行,则阻塞。

如果一个子进程已终止,正等待父进程获取其终止状态,则取得该子进程的终止状态立即返回。

如果它没有任何子进程,则立即出错返回。

wait与waitpid的区别

wait使调用者阻塞,waitpid有一个选项,可以使调用者不阻塞。

#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
#include<stdlib.h>
#include<wait.h>
int main()
{
        pid_t pid;
        int cnt = 0;
        int status=10;
        pid = fork();

        if(pid > 0){
                waitpid(pid,&status,WNOHANG);//非阻塞
                printf("Child quit,child status %dn",WEXITSTATUS(status));
                while(1){
                        printf("cnt = %dn",cnt);
                        printf("This is father print,pid = %dn",getpid());
                        sleep(1);
                }

        }else if(pid == 0){
                while(1){
                        printf("This is children print,pid = %dn",getpid());
                        sleep(1);
                        cnt++;
                        if(cnt == 5){
                                exit(3);
                        }
                }

        }


        return 0;
}

需要注意的是,父进程虽然调用了wait,但是在非阻塞等待状态时,子进程会变成僵尸进程。

孤儿进程

父进程如果不等待子进程退出,在子进程之前就结束了自己的“生命”,此时子进程叫做孤儿进程。

Linux避免系统存在过多孤儿进程,init进程收留孤儿进程,变成孤儿进程的父进程。

(三)exec族函数

exec族函数有:execl, execlp, execle, execv, execvp, execvpe

函数原型:

#include <unistd.h>
extern char **environ;

int execl(const char *path, const char *arg, ...);
int execlp(const char *file, const char *arg, ...);
int execle(const char *path, const char *arg,..., char * const envp[]);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
int execvpe(const char *file, char *const argv[],char *const envp[]);

示例:

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>

int main()
{
        printf("This pro get system date:n");

        if(execlp("date","date",NULL,NULL)==-1)
        {
                printf("execlp failed:n");
                perror("why");
        }
        printf("after execlpn");
        return 0;
}
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>

int main()
{
        printf("This pro get system date:n");
        char *argv[]={"date",NULL,NULL};
        if(execvp("date",argv)==-1)
        {
                printf("execvp failed:n");
                perror("why");
        }
        printf("after execvpn");
        return 0;
}

以上两个都是获取系统时间的。

如何增加环境变量

1.用pwd命令获取当前路径

2.用export PATH=$PATH:(pwd获取到的路径)

3.回车执行

这样环境变量中便会增加一条当前路径的信息。

exec族函数配合fork使用

代码示例:

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

int main()
{
        pid_t pid;
        int data=10;
        while(1){
                printf("Please input a datan");
                scanf("%d",&data);
                if(data == 1){
                        int fdSrc;
                        pid = fork();
                        if(pid > 0){
                                wait(NULL);

                        }
                        if(pid == 0){
                                execl("./changData","changData","config.txt",NULL);
                        }

                }else{
                        printf("wait ,do nothingn");
                }

        }
        return 0;
}

其中的changData的代码如下:

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

int main(int argc,char **argv)
{
        int fdSrc;
        char *readBuf=NULL;
        if(argc!=2){
                printf("参数传递错误n");
                exit(-1);
        }
        fdSrc=open(argv[1],O_RDWR);
        int size=lseek(fdSrc,0,SEEK_END);
        lseek(fdSrc,0,SEEK_SET);
        readBuf=(char *)malloc(sizeof(char)*size+8);
        int n_read=read(fdSrc,readBuf,size);

        char *p=strstr(readBuf,"SCORE=");
        if(p==NULL){
                printf("not found!!!n");
                exit(-1);
        }
        p=p+strlen("SCORE=");
        *p = '5';

        lseek(fdSrc,0,SEEK_SET);
        int n_write=write(fdSrc,readBuf,strlen(readBuf));

        close(fdSrc);

        return 0;
}

(四)system函数

system函数的返回值如下:

成功,则返回进程的状态值;

当sh不能执行时,返回127;

失败返回-1;

与exec不同的是:system还会返回到原程序中执行后边的代码。

(五)popen函数

popen函数比system函数在应用中的好处是:

可以获取运行的输出结果。

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

int main()
{
        char ret[1024] = {0};
        FILE *fp;
        fp = popen("ps","r");
        int nread = fread(ret,1,1024,fp);
        printf("read ret %d byte,ret = %sn",nread,ret);

        return 0;
}

最后

以上就是虚幻自行车为你收集整理的Linux系统编程之进程Linux系统编程之进程的全部内容,希望文章能够帮你解决Linux系统编程之进程Linux系统编程之进程所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(54)

评论列表共有 0 条评论

立即
投稿
返回
顶部