Linux-进程控制

进程创建

fork函数创建子进程

#include <unistd.h>

pid_t fork(void);
返回值:子进程返回0;父进程返回子进程id.出错返回-1
这个函数有两个返回值,一般用if分流

函数实现过程:

进程调用fork,将控制转移到内核,内核开始工作
1.分配内存块和数据块给子进程
2.将父进程的数据和代码拷贝给子进程
3.添加子进程到进程列表当中
4.forkf返回,开始调度器调度

下面是fork函数的简单用法

#include <unistd.h>
#include <sys/types.h>
#include<stdio.h>
int main(  )
{
    int ret=fork(  );
    if(  ret<0)
    {
          perror(  "  fork\n");
          return -1;
    }
    else if(  ret==0)
    {

        printf("i am child :%d,  ret:%d\n",getpid(),ret);


    }
    else
    {

        printf("i am  father:%d   ret:%d\n",getpid(),ret);
    }


    sleep(  1);
    return 0;

}

运行结果:

[root@localhost my_shell]# ./a.out
i am  father:4259   ret:4260
i am child :4260,   ret:0

可以看出:父进程返回子进程id;子进程返回0;

进程终止

进程退出有三种情况:

  • 程序跑完了,结果正确
  • 程序跑完了,结果不正确
  • 程序异常终止

进程常见退出方法:

  • 从main函数返回,通过return返回
  • 调用exit()函数。终止进程,返回退出码,退出后会进行收尾工作
  • 调用_exit()函数,强制终止进程,返回退出码,不进行收尾工作。

_exit函数

#include <stdlib.h>
 void _exit(int status);
 参数:status定义了进程的终止状态,父进程通过wait函数来获取该状态。
 虽然status是int型,但只有低8位可以用。所以执行_exit(-1)时,在终端执行echo $?时返回255.

exit函数


 #include <stdlib.h>
 void exit(int status);
 返回值:没有返回值

该函数执行后会关闭所有打开的流,然后调用_exit()函数

下面是简单运用

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main(  )

{
      printf(  "  hello,exit\n");
      exit(  0);
}

运行结果

[root@localhost my_shell]# ./a.out
hello,exit

调用_exit函数

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main(  )

{
      printf(  "  hello,_exit\n");
     _exit(0);
}

运行结果

[root@localhost my_shell]# ./a.out
hello,_exit

进程等待

进程等待是指父进程等待子进程,获取子进程的返回状态,回收子进程的过程

等待理由:

  • 子进程退出,父进程如果不管不顾,子进程就会变成僵尸进程,从而导致内存泄露
  • 子进程一旦变成僵尸进程,就会变得刀枪不入。kill -9 也无法杀死。
  • 父进程需要知道派给子进程的任务完成的如何。结果对不对,或者是否正常退出
  • 父亲通过进程等待的方式,获得子进程退出信息,回收子进程

等待方法:
调用wait函数

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

       pid_t wait(int *status);

  返回值:成功返回被等待进程pid,失败返回-1
  参数:输出性参数,获取进程退出状态,不关心可设置成NULL

waitpid函数

#include <sys/types.h>
#include <sys/wait.h>
pid_t waitpid(pid_t pid, int *status, int options);

返回值:当正常返回时,返回收集到的子进程pid
       出错返回-1

下面是简单程序使用

#include<stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <errno.h>
#include <string.h>
int main(  )
{
      int pid=fork(  ); //创建子进程
      if(  pid<0)
      {
            perror(  "  fork\n");
            exit(  1);
      }
      else if(  pid==0)
      {
            sleep(  20);
            printf(  "  child  pid:%d\n",getpid(  ));
            exit(  10);//子进程睡眠20s再退出
      }
      else
      {
            int st;
            int ret=wait(  &st);    //父进程等待子进程退出
            if(  ret>0&&(st&0x7F)==0 )  //子正常退出
            {
                  printf(  "  child exit code:%d\n",(st>>8)&0xFF);
            }
            else if(  ret>0)//异常退出
            {
                  printf("signal code:%d\n",st&0x7F);
            }
      }

    return 0;
}

运行结果:

[root@localhost my_shell]# ./a.out
  child  pid:6485
  child exit code:10

阻塞式等待

#include<stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <errno.h>
#include <string.h>
int main(  )
{
      int pid=fork(  ); //创建子进程
      if(  pid<0)
      {
            perror(  "  fork\n");
            exit(  1);
      }
      else if(  pid==0)
      {
            printf(  "  child is runing, pid:%d\n",getpid(  ));
            sleep(5);
            exit(257);//子进程睡眠5s再退出
      }
      else
      {
            int status=0;
            int ret=waitpid(-1,&status,0);  //父进程等待子进程退出,阻塞式等待
            printf(  "father say:this is test for wait\n");
            if( WIFEXITED( status )&ret==pid )  //子正常退出
            {
                  printf(  "father say:wait child 5s,child return code:%d\n",WEXITSTATUS(  status));
            }
            else if(  ret>0)//异常退出
            {
                  printf("child failed return.\n");
            }
      }

    return 0;
}

[root@localhost my_shell]# ./a.out
  child is runing, pid:6565



father say:this is test for wait
father say:wait child 5s,child return code:1

从结果可以看出,父进程一直在等待子进程执行完毕,再继续执行自己程序,所以输出时,中间空了五个空行
下面来看下,父进程边等边执行自己的程序,即 非阻塞式等待

非阻塞式等待应用程序

#include<stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <errno.h>
#include <string.h>
int main(  )
{
      int pid=fork(  ); //创建子进程
      if(  pid<0)
      {
            perror(  "  fork\n");
            exit(  1);
      }
      else if(  pid==0)
      {
            printf(  "  child is runing, pid:%d\n",getpid(  ));
            sleep(5);
            exit(257);//子进程睡眠5s再退出
      }
      else
      {
            int status=0;
            pid_t ret=0;
            do
            {

                ret=waitpid(-1,&status,WNOHANG);    //父进程等待子进程退出,非阻塞式等待
                printf(  "  child is runing\n");
                sleep(  1);
            }while(  ret==0);

            if( WIFEXITED( status )&ret==pid )  //子正常退出
            {
                  printf(  "father say:wait child 5s,child return code:%d\n",WEXITSTATUS(  status));
            }
            else if(  ret>0)//异常退出
            {

                printf("wait child failed return.\n");
                  return 1;
            }
      }

    return 0;
}

运行结果

[root@localhost my_shell]# ./a.out
  child is runing
  child is runing, pid:6769
  child is runing
  child is runing
  child is runing
  child is runing
  child is runing
father say:wait child 5s,child return code:1

可以看出,父进程在等的时候,并不停下手中的工作,而是继续执行。这种效率更高。

在执行上述一系列流程后,我们对进程的每一步都有了清晰的认识之后。
下面我们来介绍两个综合函数
popen/system

#include <stdio.h>

FILE *popen(const char *command, const char *type);

int pclose(FILE *stream);

popen总是和pclose一起出现被使用的。popen() 创建一个管道,通过fork或者invoke一个子进程,然后执行command。返回值在标准IO流中,由于是在管道之中,因此数据流是单向的,command只能产生stdout或者读取stdin,因此type只有两个值:‘w’或‘r’。r表示command从管道中读取数据流,而w表示command的stdout输出到管道中。command无法同时读取和输出。popen返回该FIFO数据流的指针

管道读:先创建一个文件test,然后再test文件内写入“Read pipe successfully !”
  #include “stdio.h”
  #include “stdlib.h”
  int main()
  {
  FILE *fp;
  char buf[200] = {0};
  if((fp = popen(“cat test”, “r”)) == NULL) {
  perror(“Fail to popen\n”);
  exit(1);
  }
  while(fgets(buf, 200, fp) != NULL) {
  printf(“%s”, buf);
  }
  pclose(fp);
  return 0;
  }


运行结果:

Read pipe successfully !
管道读:
  #include “stdio.h”
  #include “stdlib.h”
  int main()
  {
  FILE *fp;
  char buf[200] = {0};
  if((fp = popen(“cat > test1″, “w”)) == NULL) {
  perror(“Fail to popen\n”);
  exit(1);
  }
  fwrite(“Read pipe successfully !”, 1, sizeof(“Read pipe successfully !”), fp);
  pclose(fp);
  return 0;
  }

运行结果

Read pipe successfully !

对于管道读例子已经很清楚,而管道写可能用的地方比较少。而对于写可能更常用的是system函数:
system(“cat “Read pipe successfully!” > test1”)
可以看出,popen可以控制程序的输入或者输出,而system的功能明显要弱一点,比如无法将ls的结果用到程序中。如果不需要使用到程序的I/O数据流,那么system是最方便的。
而且system函数是C89和C99中标准定义的,可以跨平台使用。而popen是Posix 标准函数,可能在某些平台无法使用(windows应该是可以的吧,没做过测试)。
如果上述两个函数还无法满足你的交互需求,那么可以考虑exec函数组了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值