【linux】 进程控制

进程创建

fork():通过复制调用进程创建子进程
详情见

vfork():与父进程共用同一块虚拟地址空间–子进程运行,等到子进程exit退出或者程序替换之后父进程才开始运行,子进程return后将资源释放,父进程则调用栈混乱,因此vfork创建在子进程不能在main中return退出
vfork()–快速创建子进程且子进程是专门用来运行其他程序共用地址空间可以减少子进程数据拷贝父进程的消耗,因此速度快
vfork()–已经被淘汰,在fork实现写时拷贝技术之后被淘汰

  1 #include<stdio.h>
  2 #include<unistd.h>
  3 
  4 int main(){
  5 
  6   int pid =vfork();
  7   if(pid==0){
  8     printf("child---%d\n",getpid());
  9   }
 10   else{
 11     printf("parent---%d\n",getpid());
 12   }
 13   while(1){
 14     printf("------%d\n",getpid());
 15     sleep(1);                                                                                  
 16   }
 17   return 0;
 18 }

打印
child---8724
------8724
------8724
------8724
------8724
------8724
------8724
^C
如果不终止会一直打印
------8724

进程终止

终止场景:进程退出返回值的作用
  正常退出,结果符合预期
  正常退出,结果不符合预期
  异常退出

终止方式:
  main函数中return

  1 //进程终止demo
  2 #include<stdio.h>
  3 #include<unistd.h>
  4 #include<stdlib.h>
  5 int main(){
  6   printf("nihao!!\n");
  7 
  8   sleep(3);
  9                      
 10   return 0;
 11 }
打印
nihao 
然后三秒之后退出

  exit() 
库函数 退出前会刷新缓冲区,做退出的收尾工作

  1 //进程终止demo
  2 #include<stdio.h>
  3 #include<unistd.h>
  4 #include<stdlib.h>
  5 int main(){
  6   printf("nihao!!\n");                                                                         
  7  sleep(3);
  8   exit(0);
  9 }  
  打印
  nihao
  然后三秒之后退出

  _exit()  系统调用接口 直接退出,释放资源

  1 //进程终止demo
  2 #include<stdio.h>
  3 #include<unistd.h>
  4 #include<stdlib.h>
  5 int main(){
  6   printf("nihao!!\n");                                                                         
  7   sleep(3);
  8   _exit(0);
  9 
 10 }
打印
nihao!!
然后三秒退出

表面上是这样其实不然。
  1 //进程终止demo
  2 #include<stdio.h>
  3 #include<unistd.h>
  4 #include<stdlib.h>
  5 int main(){
  6   printf("nihao!!\t");                
  7   sleep(3);
  8   _exit(0);
  9 
 10 }
打印
什么都不打印,等待三秒然后退出
这只是将printf中的\n给改成了\t就不一样
  1 //进程终止demo
  2 #include<stdio.h>
  3 #include<unistd.h>
  4 #include<stdlib.h>
  5 int main(){
  6   printf("nihao!!");           
  7   sleep(3);
  8   _exit(0);
  9 
 10 }
打印
什么都不打印,等待三秒然后退出
这次直接是将\t去掉,可结果和\t一样

所以,这才是真正可以看出来_exit(0)的不同,它是直接退出,释放资源,不会刷新缓冲区
而\n不行,因为printf函数在遇到“\n”换行符时自动的从缓冲区中将记录读出

返回值:查看进程返回值通过echo $?查看
返回值只用一个字节进行保存
  错误编号:每次系统调用执行完毕都会重置进程中的errno这么一个全局变量,这个全局变量中存储的就是当次调用的系统调用接口错误编号,当系统调用接口出错,用户就可以通过这个errno获取系统调用错误的原因

  1 #include<stdio.h>
  2 #include<stdlib.h>
  3 #include<string.h>
  4 #include<unistd.h>
  5 int main(){
  6   printf("nihao!!");
  7   int i=0;
  8   for(i=0;i<256;++i){
  9   printf("%s\n",strerror(i));
 10   }
 11   sleep(3);
 12   _exit(255);                         
 13 }
打印
nihao!!Success
Operation not permitted
No such file or directory
No such process
Interrupted system call
Input/output error
No such device or address
Argument list too long
Exec format error
Bad file descriptor
No child processes
Resource temporarily unavailable
Cannot allocate memory
Permission denied
Bad address
Block device required
Device or resource busy
File exists
Invalid cross-device link
No such device
Not a directory
Is a directory
Invalid argument
Too many open files in system
Too many open files
Inappropriate ioctl for device
Text file busy
File too large
No space left on device
Illegal seek
Read-only file system
Too many links
Broken pipe
Numerical argument out of domain
Numerical result out of range
Resource deadlock avoided
File name too long
No locks available
Function not implemented
Directory not empty
Too many levels of symbolic links
Unknown error 41
No message of desired type
Identifier removed
Channel number out of range
Level 2 not synchronized
Level 3 halted
Level 3 reset
Link number out of range
Protocol driver not attached
No CSI structure available
Level 2 halted
Invalid exchange
Invalid request descriptor
Exchange full
No anode
Invalid request code
Invalid slot
Unknown error 58
Bad font file format
Device not a stream
No data available
Timer expired
Out of streams resources
Machine is not on the network
Package not installed
Object is remote
Link has been severed
Advertise error
Srmount error
Communication error on send
Protocol error
Multihop attempted
RFS specific error
Bad message
Value too large for defined data type
Name not unique on network
File descriptor in bad state
Remote address changed
Can not access a needed shared library
Accessing a corrupted shared library
.lib section in a.out corrupted
Attempting to link in too many shared libraries
Cannot exec a shared library directly
Invalid or incomplete multibyte or wide character
Interrupted system call should be restarted
Streams pipe error
Too many users
Socket operation on non-socket
Destination address required
Message too long
Protocol wrong type for socket
Protocol not available
Protocol not supported
Socket type not supported
Operation not supported
Protocol family not supported
Address family not supported by protocol
Address already in use
Cannot assign requested address
Network is down
Network is unreachable
Network dropped connection on reset
Software caused connection abort
Connection reset by peer
No buffer space available
Transport endpoint is already connected
Transport endpoint is not connected
Cannot send after transport endpoint shutdown
Too many references: cannot splice
Connection timed out
Connection refused
Host is down
No route to host
Operation already in progress
Operation now in progress
Stale file handle
Structure needs cleaning
Not a XENIX named type file
No XENIX semaphores available
Is a named type file
Remote I/O error
Disk quota exceeded
No medium found
Wrong medium type
Operation canceled
Required key not available
Key has expired
Key has been revoked
Key was rejected by service
Owner died
State not recoverable
Operation not possible due to RF-kill
Memory page has hardware error
Unknown error 134
(此处省略 error 135~254)
Unknown error 255
然后等待三秒钟后退出

echo$?
打印
255
在查看返回值错误主要就是通过strerror与perror

C 库函数 char *strerror(int errnum) 从内部数组中搜索错误号 errnum,并返回一个指向错误消息字符串的指针。 该错误字符串描述了错误 errnum(它通常就说errno值)

perror:头文件:stdio.h void perror(const char *msg); 它是基于errno的当前值,在标准错上产生一条出错信息,然后返回。它先输出由msg字符串,然后是一个冒号后头接着对应于errno值的出错信息 strerror与perror的区别: perror和strerror都是根据errno的值打印错误信息的。   perror是将errno对应的错误消息的字符串打印到标准错误输出上,即stderr或2上,若你的程序将标准错误输出重定向到/dev/null,那就看不到了,就不能用perror了。   而 strerror的作用只是将errno对应的错误消息字符串返回.你可以自己决定咋样处理字符串,比如可以存到日志,也可以直接printf出来。

进程等待

等待子进程退出–避免产生僵尸进程

僵尸进程的产生:由于父进程不知道子进程什么时候退出,所以忙其他事情,若此时子进程退出,此时操作系统通知父进程,可能父进程没有注意到,子进程就变成僵尸进程了

  1 #include<stdlib.h>
  2 #include<stdio.h>
  3 #include<unistd.h>
  4 int main(){
  5   int pid=fork();
  6   if(pid<0){
  7     perror("fork error");
  8     exit(-1);
  9   }else if(pid==0){
 10     sleep(3);
 11     exit(0);
 12   }
 13   while(1){
 14     sleep(0);
 15   }
 16   return 0;                             
 17 }
打印
while [ true ] ; do clear; ps aux |grep wait;sleep 0.5;done
这是一个shell脚本,死循环,执行do到done之间的语句
muxiaoxi  11448  111  0.0   4156   340 pts/1    R+   11:46   0:02 ./waitt
muxiaoxi  11449  0.0  0.0   4156    80 pts/1    S+   11:46   0:00 ./waitt
muxiaoxi  11468  0.0  0.0 112672   972 pts/2    S+   11:46   0:00 grep --color=auto wait
第二秒时,进程的状态,僵尸进程还没有出现
muxiaoxi  11593 94.2  0.0   4156   344 pts/1    R+   11:49   0:04 ./waitt
muxiaoxi  11594  0.0  0.0      0     0 pts/1    Z+   11:49   0:00 [waitt] <defunct>
muxiaoxi  11629  0.0  0.0 112672   972 pts/2    S+   11:49   0:00 grep --color=auto wait
第三秒时,父进程退出,子进程变成僵尸进程

wait(int)–阻塞函数;等待任意一个子进程退出,若当前父进程有多个子进程,只要有一个子进程退出,就会退出,如果没有子进程,则直接报错

   1 #include<stdlib.h>         
    2 #include<stdio.h>
    3 #include<unistd.h>
    4 int main(){
    5   int pid=fork();
    6   if(pid<0){
    7     perror("fork error");
    8     exit(-1);
    9   }else if(pid==0){
   10     sleep(3);
   11     exit(0);
   12   }
   13   //pid_t wait(int* status);
   14   //status:用于获取退出原因
   15   //返回值:退出的子进程的pid
   16   int statu;
E> 17   int ret=wait(&statu);
   18  printf("%d-%d\n",ret,pid);
   19   while(1){
   20     printf("--------------\n");       
   21     sleep(1);
   22   }
   23   return 0;
   24 }
打印
10701-10701
--------------
--------------
^C

阻塞:为了完成功能发起调用,如果当前不具备完成条件,则一直等待,直到完成后返回(即父进程会为了子进程而一直等下去,只有子进程退出后,父进程才开始做自己的事情)
非阻塞:为了完成功能发起调用,如果当前不具备完成条件,则立即报错返回
pid_t waitpid(int pid,int * statu,int options
  可以等到任意一个子进程退出或者等待指定子进程退出
  waitpid可以通过opt将操作设置为非阻塞

阻塞版:
    1 #include<stdio.h>                 
    2 #include<stdlib.h>
    3 #include<unistd.h>
    4 int main(){
    5   int pid=fork();
    6   if(pid<0){
    7     printf("fork error");
    8     exit(-1);
    9   }
   10   else if(pid==0){
   11     sleep(3);
   12     exit(0);
   13   }
   14   //pid_t waitpid(pid_t pid,int* status,int options);
   15   //pid:
   16   // pid==-1 等待任意子进程退出
   17   // pid>0   等待指定的子进程退出
   18   // status: 用于获取退出原因
   19   // options:
   20   //    WNOHANG 将waitpid设置为非阻塞
   21   int statu;
   22   int ret=waitpid(pid,&statu,0);
   23   printf("%d-%d\n",ret,pid);
   24   while(1){
   25     printf("-------------\n");
   26     sleep(1);
   27   }
   28 } 
   
   打印
   12697-12697
-------------
-------------
^C
非阻塞:
    1 #include<stdio.h>                  
    2 #include<stdlib.h>
    3 #include<unistd.h>
    4 int main(){
    5   int pid=fork();
    6   if(pid<0){
    7     printf("fork error");
    8     exit(-1);
    9   }
   10   else if(pid==0){
   11     sleep(3);
   12     exit(0);
   13   }
   14   //pid_t waitpid(pid_t pid,int* status,int options);
   15   //pid:
   16   // pid==-1 等待任意子进程退出
   17   // pid>0   等待指定的子进程退出
   18   // status: 用于获取退出原因
   19   // options:
   20   //    WNOHANG 将waitpid设置为非阻塞
   21   //返回值;-1:错误
   22   //        0:没有子进程退出
   23   //       >0:退出子进程的pid
   24   int statu;
   25   int ret;
E> 26   while((ret=waitpid(pid,&statu,WNOHANG)==0)){
   27     printf("打麻将!\n");
   28     sleep(1);
   29   }
   30  // int ret=waitpid(pid,&statu,0);
   31   printf("%d-%d\n",ret,pid);
   32   while(1){
   33     printf("-------------\n");
   34     sleep(1);
   35   }
   36 } 
   打印
打麻将!
打麻将!
打麻将!
0-12943
-------------
-------------
^C
每执行一次打麻将后,就查看一次子进程是否返回,若返回则执行下一份任务,否则继续打麻将

阻塞与非阻塞区别:调用功能当前不具备完成条件是否立即返回

获取子进程的退出返回值:

如何获取子进程退出码:(statu>>8)&0xff
判断程序是否正常退出:statu&0x7f–异常信号值为0,证明程序是正常退出;否则异常退出

 1 #include<stdio.h>
    2 #include<stdlib.h>                                                                           
    3 #include<unistd.h>
    4 int main(){
    5   int pid=fork();
    6   if(pid<0){
    7     printf("fork error");
    8     exit(-1);
    9   }
   10   else if(pid==0){
   11     sleep(3);
   12     exit(255);
   13   }
   14   int statu;
   15   int ret;
   16   while((ret=waitpid(pid,&statu,WNOHANG))==0){
   17   printf("打麻将\n");
   18   sleep(1);
   19   }
   20   if(!(statu&0x7f)){//正常退出才进入判断 
   21   printf("%d-%d--child exit code:%d\n",ret,pid,
   22       (statu>>8)&0xff);
   23   }
   24   if(WIFEXITED(statu)){
   25     printf("%d-%d---chile exit code:%d\n",ret,pid,
   26         WEXITSTATUS(statu));
   27   }
   28   while(1){
   29     printf("-------\n");
   30     sleep(1);
   31   }
   32 } 
   打印
打麻将
打麻将
打麻将
打麻将
16032-16032--child exit code:255
16032-16032---chile exit code:255
-------
-------
^C
可见代码20-23,与代码24-27效果是一样的

上面我们讲述的fork函数都是用来创建一个子进程,该子进程几乎就是父进程的副本,但是有时候我们希望子进程去执行其他程序,那么这又应该怎么做那?这就要用到我们的exec函数系列。

exec函数系列:根据指定文件名或目录名,找到可执行文件并用

(1)exec函数说明

fork函数是用于创建一个子进程,该子进程几乎是父进程的副本,而有时我们希望子进程去执行另外的程序,exec函数系列就提供了一个在进程中启动另一个程序执行的方法。该系列函数可以根据指定的文件名或目录名找到可执行文件,并用找到的可执行文件来取代原调用进程的数据段、代码段和堆栈段,在执行完之后,原调用进程的内容除了进程号外,其他全部被新程序的内容替换了。另外,这里的可执行文件既可以是二进制文件,也可以是Linux下任何可执行脚本文件。
(2)在Linux中使用exec函数系列主要有以下两种情况

①当进程认为自己不能再为系统和用户做出任何贡献时,就可以调用任何exec 函数系列让自己重生。

②如果一个进程想执行另一个程序,那么它就可以调用fork函数新建一个进程,然后调用任何一个exec函数系列使子进程重生。
(3)exec函数系列

//头文件

#include <unistd.h>

//函数说明


//函数原型

int execl(const char *pathname, const char *arg, ...)

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

int execle(const char *pathname, const char *arg, ..., char *const envp[])

int execve(const char *pathname, char *const argv[], char *const envp[])

int execlp(const char *filename, const char *arg, ...)

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

//函数返回值

成功:函数不会返回

失败:返回-1,失败原因记录在error中

execl函数系列:
l(list):表示参数采用列表
v(vector):表示参数用数组
p(path):有p自动搜索环境变量PATH
e(env):表示自己维护环境变量
具体详情可以使用 man + execl 进行查看

函数名参数格式是否带路径是否使用当前环境变量
execl列表不是
execlp列表
execle列表不是不是,须自己组装环境变量
execv数组不是
execvp数组
execve数组不是不是,须自己组装环境变量
    1 //exec函数族中接口的使用
    2 #include<stdio.h>
    3 #include<stdlib.h>
    4 #include<unistd.h>
    5 int main(){
    6   printf("hehe\n");
    7   //execl("./test","test","-l",NULL);//必须要有路径
    8   // execlp("ls","-l",NULL);
    9   char* env[32];
W> 10   env[0]="MYENV=1000";
   11   env[1]=NULL;
   12   execle("./test","test","-a",NULL,env);                                                     
   13   printf("nihao\n");
   14   return 0;
   15 }
   
test.c
  1 #include<stdio.h>
  2 #include<stdlib.h>
  3 #include<unistd.h>
  4 int main(int argc,char* argv[],char* env[])
  5 {
  6   int i;
  7   for(i=0;i<argc;++i){
  8     printf("argv[%d]=[%s]\n",i,argv[i]);
  9   }
 10   for(i=0;env[i]!=NULL;++i){                                                                   
 11     printf("env[%d]=[%s]\n",i,env[i]);
 12   }
 13   return 0;
 14 }

打印
hehe
argv[0]=[test]
argv[1]=[-a]
env[0]=[MYENV=1000]

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值