Linux进程学习—进程的创建、控制与退出

Linux进程学习—进程的创建、控制与退出

本文记录一些Linux学习过程中关于进程创建与控制的相关函数与概念,主要包括fork、exec函数族、system、popen的使用示例。

1、进程的创建

1.1进程定义

进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。在早期面向进程设计的计算机结构中,进程是程序的基本执行实体;在当代面向线程设计的计算机结构中,进程是线程的容器。程序是指令、数据及其组织形式的描述,进程是程序的实体。

1.2进程创建

实际操作过程中可以利用forkvfork函数实现进程的创建,进程创建后分为子进程父进程,函数的两个返回值分别为0和子进程的进程PID号。由此可以判定返回值为0的进程为子进程;返回值为非0值的进程为父进程

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


int main()
{
    
    
        pid_t pid;

        pid = getpid();

        int key=fork();

        if(key>0)
        {
    
    
                printf("father pid is :%d  \n",getpid());
        }
        else
        {
    
    
                printf("child pid is :%d  \n",getpid());

        }

        return 0;
}

同时,需要注意forkvfork两个函数在内存空间与执行次序两方面有所区别:

1. 内存空间

fork (): 子进程拷贝父进程的数据段,代码段
vfork(): 子进程与父进程共享数据段

2. 执行次序

fork (): 父/子进程的执行次序不确定
vfork(): 子进程先运行,在调用exec 或exit 之前与父进程数据是共享的, 子进程调用exec或exit 之后,父进程才可能被调度运行。如果在调用这两个函数之前子进程依赖于父进程的进一步动作,则会导致死锁。

2、进程退出

正常退出:

main函数调用return
进程调用_exit() 或者 _Exit(),系统调用
进程调用exit(),标准C库( exit是_exit和_Exit的封装,前者是处理完缓存区中的内容再退出,而后两者是直接退出)

异常退出:

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

不管是正常退出还是异常退出,进程退出时都会将打开的描述符关闭,并且释放存储器。值得注意的是子进程终止进程时,需要在父进程中调用wait或waitpid函数来取得子进程的运行状态(exit的参数)。

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


int main()
{
    
    
        pid_t pid;

        int cnt=0;
        int status=10;

        pid=fork();//创建子进程 子进程中的返回值是0,父进程中的返回值是子进程的pid号

        if(pid>0)//父进程
        {
    
    

                wait(&status);
        //      waitpid(pid,&status,WNOHANG);
                printf("father pid is :%d,status:%d  \n",getpid(),WEXITSTATUS(status));

                while(1)
                {
    
    
                        printf("cnt=%d\n",cnt);
                        printf("This is Father print,pid=%d\n",getpid());
                        sleep(1);
                }
        }
        else//pid==0子进程
        {
    
    
                while(1)
                {
    
    
                        printf("child pid is :%d  \n",getpid());
                        cnt++;
                        if(cnt == 5)
                        {
    
    
                                exit(1);
                        }
                }

        }

        return 0;
}                                                                                                        39,1-8       全部

需要注意的是:

子进程退出后状态不被收集(wait),最终会变成僵尸进程
子进程尚未结束而父进程已经over,则会变成孤儿进程

3、进程控制

3.1、创建进程的目的

1.一个父进程希望复制自己,使父、子进程同时执行不同的代码段。这在网络服务进程中是常见的------父进程等待客户端的服务请求。当这种请求到达时,父进程调用fork,使子进程处理此请求。父进程则继续等待下一个服务请求到达。
2.一个进程要执行一个不同的程序,这对shell是常见的情况。在这种情况下,子进程从fork返回后立即调用exec

3.2、exec函数族

用fork函数创建新进程后,经常会在新进程中调用exec函数去执行另外一个程序。当进程调用exec函数时,该进程被完全替换为新程序。因为调用exec函数并不创建新进程,所以前后进程的ID并没有改变。
头文件

 #include <unistd.h>

函数原型

**1.execl**   int execl(const char *path, const char *arg, ...);
**2.execlp**  int execlp(const char *file, const char *arg, ...);
**3.execv**   int execv(const char *path, char *const argv[]);
**4.execvp**  int execvp(const char *file, char *const argv[]);

**5.execle**  int execle(const char *path, const char *arg,
                  ..., char * const envp[]);
**6.execvpe** int execvpe(const char *file, char *const argv[],char *const envp[]);         

返回值
exec函数族的函数执行成功后不会返回,调用失败时,会设置errno并返回-1,然后从原程序的调用点接着往下执行。
exec使用示例

#include <stdio.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
//函数原型:int execl(const char *path, const char *arg, ...);

int main(void)
{
    
    
    printf("before execl\n");
    if(execl("/bin/ls","ls","-l",NULL) == -1)
    {
    
    
        printf("execl failed!\n");
        perror("why");
    }
    printf("after execl\n");
    return 0;
}
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

//函数原型:int execl(const char *path, const char *arg, ...);

int main(void)
{
    
    
    printf("before execl\n");
    if(execl("/bin/date","date",NULL) == -1)
    {
    
    
        printf("execl failed!\n");
        perror("why");
    }
    printf("after execl\n");
    return 0;
}

3.3 system函数

源码

int system(const char * cmdstring)
{
    
    
    pid_t pid;
    int status;
 
    if(cmdstring == NULL){
    
    
          
         return (1);
 
    }
 
 
    if((pid = fork()) < 0){
    
    
 
            status = -1;
 
    }else if(pid == 0){
    
    
 
        execl("/bin/sh", "sh", "-c", cmdstring, (char *)0);
        -exit(127); 
 
    }else{
    
    
            while(waitpid(pid, &status, 0) < 0){
    
    
                if(errno != EINTER){
    
    
                    status = -1;
                    break;
                }
            }
    }
 
    return status;
}

system函数使用实例

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


int main(void)
{
    
    
    if(system("date") == -1)
    {
    
    
        printf("execl failed!\n");
        perror("why");
    }
    return 0;
}

3.4 popen函数

  #include <stdio.h>
  FILE *popen( const char* command, const char* mode )

相对于system函数,popen可以获取返回值便于读取进程操作结果

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


int main(void)
{
    
    
        char temp[1024]={
    
    "0"};
        system("date");

        FILE *Pid=popen("ps","r");
        int nread=fread(temp,1,1024,Pid);

        printf("fread number is :%d,conclude is :%s\n",nread,temp);
        return 0;
}

猜你喜欢

转载自blog.csdn.net/ONERYJHHH/article/details/126377637
今日推荐