Linux进程控制编程
(1)进程创建
①fork()
#include <unistd.h>pid_t fork(void); //pid_t是个int变量
fork()系统调用的具体实现过程,在《Linux内核设计艺术》这本书“进程1的创建”一节中讲的很详细。
原理图:
函数特性:
共享一份代码程序,但是各有一份独立的数据,为了效率和保持数据的独立采用写时复制技术(COW),通常是以虚拟内存的“1页”来操作;2种用法:
调用一次,返回两次。这样来记忆:fork调用之后不管是哪个进程都是想返回它自己的孩子进程的ID,于是父进程就返回子进程PID,子进程还是少年当然还没有孩子,所以就返回0咯
创建一个子进程来执行当前程序的不同代码
常例:网络服务进程--父进程等待客户端的服务请求,当请求到来时,fork一个子进程去处理请求,父进程继续等待下一个请求。
一个进程要处理另外一个程序
②vfork()常例:shell中执行应用程序,子进程从fork返回后立即调用exec函数来执行。
#include <sys/types.h>
#include <unistd.h>
pid_t vfork(void);
函数特性:父子进程代码段和数据段都是共享的,子进程运行在父进程的空间完全什么都不要复制。子进程优先执行。
如果子进程的进一步运行要依赖于父进程的一些操作就会导致死锁。子进程会修改父进程的数据。
通常用法:vfork之后立即调用exec()函数或exit
(2)exec函数族:execl、execv 、execle 、execve 、execlp 、execvp
后面字母“l”、“v”、“e”、“p”的含义:关系到函数的功能
"l" : list,函数的参数是通过一一列举的方式来传递的
"v" : vector,向量、指针的意思,参数是通过字符数组指针来传递
"e" : environment
"p" : PACH,指定文件默认从PATH 环境变量中查找文件并执行
头文件: #include<unistd.h>
函数原型:函数的主要区别在于参数传递方式的不同
①int execl(const char *path,const char *arg,...);
执行参数path字符串所代表的文件路径,接下来的参数,代表执行该文件时传递过去的argv(0)、argv[1]……,最后一个参,数必须用空指针(NULL)作结束。
②int execv(const char *path,char *const argv[]);
执行参数path字符串所代表的文件路径,与execl()不同的地方在于execve()只需两个参数,第二个参数利用数组指针来传递给执行文件
char *const argv[]是指针数组,argv[0]、argv[1]...存放的是字符串的起始地址,这里要求他的最后一个成员存放的是NULL
③int execlp(const char *file,const char *arg,...);
从PATH环境变量指定的目录中查找符合file指针指向的字符串的文件来执行
④int execle(const char *path,const char *arg,...,char *const envp[]);
⑤int execve(const char *path,char *const argv[],char *const envp[]);
⑥int execvp(const char *file,char *const argv[]);
执行结果: 返回-1,出错;没有返回值,执行成功
例子识记:
const char *argv[]={"ps","-au",NULL};
const char *envp[]={"PATH=/bin/:/usr/bin:usr/local/bin","HOME=/home/clbiao/arm",NULL};
//执行文件位置在第一个参数,执行参数在后面指出
execl("/bin/ps","ps","-au",NULL);
execv("/bin/ps",argv);
//指定参数并指定搜索位置
execve("/bin/ps",argv,envp);
execle("/bin/ps","ps","-aux",NULL,envp);
//只指定参数,从系统默认的PATH中搜,不用自己去定义envp
execvp("ps",argv);
execlp("ps","ps","-au",0);
①wait()
#include<sys/types.h>
#include<sys/wait.h>
pid_t wait (int * status);
直到有信号来到或所有子进程结束时才回复执行。如果在调用wait()时子进程已经结束,则wait()会立即返回子进程结束状态值status
②waitpid()
#include<sys/types.h>
#include<sys/wait.h>
pid_t waitpid(pid_t pid,int * status,int options);
参数pid为欲等待的子进程识别码,意义:
pid<-1 等待进程组识别码为pid绝对值的任何子进程。options 通常设置成0,要进一步探讨的话启动Linux系统,运行"man waitpid"就可以看个究竟
pid=-1 等待任何子进程,相当于wait()。
pid=0 等待进程组识别码与目前进程相同的任何子进程。
pid>0 等待任何子进程识别码为pid的子进程
(5)进程退出
①_exit()
#include <unistd.h>
void _exit(int status);//status是返回给父进程的状态
_exit()函数直接残忍退出进程,不会处理标准I/O缓冲区,
函数说明:
DESCRIPTION
The function _exit() terminates the calling process "immediately". Any open
file descriptors belonging to the process are closed; any children of the
process are inherited by process 1, init, and the process‘s parent is sent a
SIGCHLD signal.
②exit()
#include <stdlib.h>
void exit(int status);
exit()函数会检查进程文件的打开情况,会把打开的文件写回文件
测试程序:
#include <unistd.h> #include <stdlib.h> #include <stdio.h> int main() { int pid; pid = vfork(); //pid = fork(); if(pid==0) { printf("\nChild process\n"); printf("This is in output buffer A"); _exit(0); } else { printf("\nFather process\n"); printf("This is in output buffer B"); exit(0); } }执行结果:
使用vfork时是:
Child process
This is in output buffer A
Father process
This is in output buffer B
使用fork时是:
Child process
Father process
This is in output buffer B
结果分析:(注意:printf函数把缓冲区中的数据打印出来的条件是遇到"\n"换行符)使用vfork时,子进程先执行,“Child process”先打印出来,接着把“This is in output buffer B”写到printf的buffer
并没有打印出来,因为后面的_exit(0)直接忽略I/O缓冲区,不作任何处理。然而轮到父进程执行时,继续接着往printf的buffer里面写数据(因为父子共用进程空间)直到遇到“\n”,
进行处理,所以还是会把之前子进程残留在缓冲区的数据写出来。
使用fork时,父子独立的进程空即数据区,子进程执行到_exit(),buffer没有打印出来的字符就放在那里不管了,一点都不负责任
exit()就不同了,虽然buffer里面的字符串中没有"\n",但是他在退出之前还是收拾了这个“烂摊子”。
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。