linux进程控制

进程控制
   ps 可以查看进程,默认情况下只能当前终端启动的进程。
   ps -ef 可以查看所有进程(Unix/Linux)
   ps -aux 也可以
   ps -aux是Linux特有的命令,Unix不直接支持。/usr/ucb/ps 支持。
   Unix命令:
    | more 可以分页显示结果,回车滚动一行,空格翻过一页,q退出。
   ps -aux | more
   whereis 可以查看什么东西在哪里

  主流的操作系统都支持多进程并行(同时运行),但多进程启动是有次序的。比如:Linux由进程0 启动进程1和进程2(有些系统进程2是由进程1启动),其他进程都由这两个进程直接/间接启动。
  如果进程a启动了进程b,a叫父进程,b叫子进程。
  进程的常见状态:
   S 进程处于休眠状态,多数进程都是休眠
   R 运行状态,正在运行
   O 准备运行的状态
   Z 僵尸进程
   僵尸进程就是进程已经结束,但资源没有回收。
   系统管理进程的方式:
  每个进程都有自己的编号,叫进程ID(PID),函数getpid()可以获取自己进程的ID。函数getppid()可以获取父进程的PID。
  进程的PID同一时刻不可能重复,但可以延迟重用(过一段时间后可以再次使用)。
 
  父子进程的关系:
   1 父进程启动子进程后,父子进程同时运行;如果子进程先结束,子进程会给父进程发信号,父进程回收子进程的资源。
   2 父进程启动子进程后,父子进程同时运行;如果父进程先结束,子进程变成孤儿进程,认init进程(进程1)为新的父进程。init进程也叫 孤儿院。
   3 父进程启动子进程后,父子进程同时运行;如果子进程先结束,但没有发出信号或者父进程没有收到信号,子进程将变成僵尸进程

  函数getuid()可以获取当前用户ID。
  如何创建子进程?
   方法1: fork()创建
   方法2:  vfork()+execl()合作创建
  fork()创建的子进程是通过复制父进程产生的,因此和父进程执行相同的代码段。
  vfork()+execl()创建子进程是全新的,执行全新的代码段(不复制父进程的任何东西)。
 
  fork() - 复制父进程,创建子进程
   fork()创建子进程时,除了代码区不复制,其他内存区域全部复制父进程的,父子进程共享代码区。
   如果父进程中有文件描述符,子进程只复制描述符,不复制文件表。
   子进程会复制父进程的缓冲区。
   pid_t pid = fork(void);
   pid_t 就是进程ID,fork()没有参数,返回进程ID。

   fork()之后,父子进程同时运行;谁先运行,谁先结束都不一定。

  所有的浮点运算都是 近似值。每个小数(浮点)都是通过算法 得到近似值。
  fork()创建的子进程是通过复制父进程产生的,因此和父进程执行相同的代码段。
  vfork()+execl()创建子进程是全新的,执行全新的代码段(不复制父进程的任何东西)。
 
  fork() - 复制父进程,创建子进程
   fork()创建子进程时,除了代码区不复制,其他内存区域全部复制父进程的,父子进程共享代码区。
   如果父进程中有文件描述符,子进程只复制描述符,不复制文件表。
   子进程会复制父进程的缓冲区。
   pid_t pid = fork(void);
   pid_t 就是进程ID,fork()没有参数,返回进程ID。
   fork()之后,父子进程同时运行;谁先运行,谁先结束都不一定。

  fork()创建的子进程,只执行fork()之后的代码,fork()之前的代码不会执行。
  也就是说:fork()之前的代码父进程执行一次 ,fork()之后的代码父子进程分别执行一次,会执行二次。
  fork()的返回会执行两次,父进程会返回子进程的PID,子进程返回0。
  fork()在复制内存时,父进程虚拟地址子进程照抄,但会去映射不同的物理内存。

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

int main(){
  //pid_t pid = fork();//没有复制描述符,两个都是新建
  int fd=open("a.txt",O_RDWR|O_CREAT|O_TRUNC,0666);
  if(fd == -1) perror("open"),exit(-1);
  pid_t pid = fork();//复制描述符,不复制文件表
  if(pid == 0){//子进程
    write(fd,"abc",3);
	close(fd);
	fd = 5;//父进程的fd不会改变,因为复制了描述符
	exit(0);
  }
  sleep(1); printf("fd=%d\n",fd);//fd复制了
  write(fd,"123",3);//但文件表没有复制
  close(fd);
}




  进程的终止:
   正常终止
     1 主函数执行了return语句。
     2 用exit()函数退出进程。
     3 用_Exit()/_exit()函数退出进程。
     4 最后一个线程退出(return方式退)。
     5 最后一个线程调用pthread_exit()。
   非正常终止
     1 收到信号,信号导致进程退(比如:ctlr+c)。
     2 最后一个线程被其他线程取消。
   
  _exit()和_Exit()其实是一回事,_Exit()底层调用的就是_exit()。
  exit()和_Exit()不同:
 exit()不会立即结束进程,会先调用atexit()注册的某些函数然后再结束(慢性子)。同时exit()的参数会与 0377做位与以后再返回。
 _Exit()会立即结束进程,同时关闭所有文件描述符,把所有子进程变成孤儿进程,参数直接被返回(急性子)。

  exit(int status)中,status 进程自身无法获取(进程自身已经结束),父进程可以获取status,父进程必须保证 子进程先结束才能回收status。
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>

int main(){
  pid_t pid =fork();
  if(!pid){//子进程执行的分支
    printf("子进程开始运行,PID=%d\n",getpid());
	sleep(2);  printf("子进程结束\n");
	exit(100);//退出码 100
  }
  printf("父进程等待子进程\n");
  int res; pid_t retpid = wait(&res);
  printf("等到的子进程pid=%d\n",retpid);
  printf("res=%d\n",res);
  if(WIFEXITED(res)){
    printf("正常退出,退出码%d\n",WEXITSTATUS(res));
  }
}

  wait()/waitpid()可以让父进程等待子进程结束,同时回收 status并且可以判断子进程是否正常结束。

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

int main(){
  int pid1,pid2; pid1 = fork();
  if(pid1 > 0) pid2 = fork();//只有父进程能执行
  if(pid1 == 0){//子进程pid1执行
	printf("pid1执行,pid=%d\n",getpid());
	sleep(3); printf("pid1执行完毕\n");
    exit(100); }
  if(pid2 == 0){//子进程pid2执行
	printf("pid2执行,pid=%d\n",getpid());
	sleep(1); printf("pid2执行完毕\n");
    exit(200); }
  int res;
  pid_t retpid = waitpid(-1/*pid1*/,&res,0);//0阻塞
  printf("子进程%d结束\n",retpid);
  if(WIFEXITED(res)) 
	printf("code=%d\n",WEXITSTATUS(res));
}




  pid_t wait(int* status)
  wait()会让父进程等待任意子进程结束/挂起,并取得子进程的返回码和结束状态,存入status中,函数的返回值就是结束的子进程PID。wait() 能回收僵尸子进程,因此得名殓尸工。
  status里面存了多种数据,因此需要拆分:
  WIFEXITED(status) - 判断是否正常结束
  WEXITSTATUS(status) - 获取exit()中参数

  waitpid()是wait()的增强版,完全覆盖wait()的功能。
  pid_t waitpid(pid_t pid,int* status,
   int options)
  参数:pid 指定等待哪些子进程
    -1 等待所有子进程
    >0 等待对应pid的子进程(特定)
     0 等待和当前进程同组的子进程之一
    <-1等待特定组(组ID=PID绝对值)的子进程之一
   一般来说,-1 和 大于0 常用。
   status和wait()一样
   options 如果是WNOHANG,非阻塞等待,如果没有子进程结束,直接返回0而不等待
  返回 结束子进程的PID

  vfork()+execl()方式
   vfork() 负责创建子进程,但不复制任何父进程的内存空间。
   execl() 提供新的代码区、全局区、堆区、栈区...,但execl()不会创建新进程。

  vfork()创建子进程时,不复制父进程内存,但会直接占用父进程的内存,因此父进程没有资源继续运行,处于阻塞状态,直到子进程结束或者子进程调用了 execl()函数族。
  vfork()确保子进程先运行。vfork()必须和execl()函数族结合使用才有意义。
  execl()函数 替换进程,原来的进程被和谐,从头开始执行新进程,PID不变。

  vfork()负责创建新进程,新进程的代码和数据execl()提供(用替换方式)。
  vfork()就用法上来说,和fork()一样。
  execl(char* path,char* cmd,...)
   path是新程序所在的路径(包括新程序名)
   cmd是执行新程序的命令
   ...是命令的各种参数和选项,可以没有
   以NULL做最后一个参数,代表结束。
  execl()可以启动 系统程序,可以启动 自定义程序。比如:启动ls -l命令。
  execl("/bin/ls","ls","-l",NULL);



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值