一、创建进程fork的使用
pid_t fork(void);
会返回两次,给父进程和子进程
返回值是0,表示为当前自己的进程
返回值是非负整数,表示为父进程
返回-1,出错
fork函数调用后,父进程会复制两份fork之下的代码,一份给自己,一份给子进程,并把子进程的fork返回值置1
- 旧Linux下的拷贝是全拷贝,但是数据若是被子进程修改的话,父进程不会受到干扰
- 新Linux下的拷贝是copy on write,例如定义的变量值,若是在过程中不会被修改,则不需要拷贝,以共享方式使用,若是需要修改值,则拷贝到各自进程中处理
//fork创建新子进程实例
#include <unistd.h>
#include <stdio.h>
int main()
{
pid_t pid;
int data;
while(1){
printf("please input data:\n");
scanf("%d",&data);
if(data == 1){
pid = fork();
if(pid > 0){
}
else if(pid == 0){
while(1){
printf("ask net connect,pid=%d\n",getpid());
sleep(3);
}
}
}else{
printf("do nothing!\n");
}
}
return 0;
}
二:vfork
与fork区别
- 直接使用父进程存储空间,不共享也不复制,这样会改变数值
- vfork保证子进程先运行,等到子进程exit退出后,父进程才执行
三:进程退出
- 正常退出
- main函数调用return
- 进程调用exit(),标准c库
- 进程调用_exit()或_Exit(),属于系统调用
- 进程最后一个线程调用pthread_exit返回
2.异常退出
- 调用abort函数
- 当进程收到某些信号时,如CTRL+C
- 最后一个线程对取消请求做出响应
无论是哪种退出,内核都会执行相应代码,这段代码为相应进程关闭所有打开标识符,释放存储器
四:父进程等待子进程退出
1.父进程等待子进程退出,并收集子进程的退出情况
- pid_t wait(int *status)
- status:是一个整数型指针
- 非空:子进程退出状态放在它指向的地址中,空:不关心退出状态
2.子进程退出状态不被收集,变成僵尸进程(父进程不关心子进程的状态,对于子进程进行的exit不做wait反应)
//僵尸进程
if(pid > 0){
while(1){
printf("cnt=%d\n",cnt);
printf("this is father print:%d\n",getpid());
sleep(1);
}
}
else if(pid == 0){
while(1){
printf("this is child print:%d\n",getpid());
sleep(1);
cnt++;
if(cnt == 3){
exit(0);
}
}
}
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main()
{
pid_t pid;
int cnt = 0;
int status = 10;
pid = fork();
if(pid > 0){
while(1){
wait(&status); //指针型数据
printf("status:%d",WEXITSTATUS(status)); //宏定义值,可以返回exit的状态量
printf("cnt=%d\n",cnt);
printf("this is father print:%d\n",getpid());
sleep(1);
}
}
else if(pid == 0){
while(1){
printf("this is child print:%d\n",getpid());
sleep(1);
cnt++;
if(cnt == 3){
exit(3);
}
}
}
return 0;
}
等待函数
- pid_t wait(int *status);
- pid_t waitpid(pid_t pid,int *status,int options);
- int waitid(idtype_t idtype,id_t id,siginfo_t *infop,int options);
- 如果其所有子进程都在运行,则阻塞
- 如果一个子进程状态已终止,正等待父进程获取其终止状态,则取得该子进程的终止状态就立即返回
- 如果它没有任何子进程,则立即出错返回
- 区别:wait中会使调用者阻塞,但是waitpid有一个选项可以使调用者不阻塞(options:WNOHANG)
孤儿进程:
- 父进程如果不等待子进程退出,在子进程运行结束之前就结束了自己的“生命”,则子进程叫做孤儿进程
- Linux中的init进程(pid = 1)来收集孤儿进程
五:exec族函数
exec族函数函数的作用:
我们用fork函数创建新进程后,经常会在新进程中调用exec函数去执行另外一个程序。当进程调用exec函数时,该进程被完全替换为新程序。因为调用exec函数并不创建新进程,所以前后进程的ID并没有改变。
功能:
在调用进程内部执行一个可执行文件。可执行文件既可以是二进制文件,也可以是任何Linux下可执行的脚本文件。
函数族:
exec函数族分别是:execl, execlp, execle, execv, execvp, execvpe返回值:
exec函数族的函数执行成功后不会返回,调用失败时,会设置errno并返回-1,然后从原程序的调用点接着往下执行。
参数说明:
path:可执行文件的路径名字
arg:可执行程序所带的参数,第一个参数为可执行文件名字,没有带路径且arg必须以NULL结束
file:如果参数file中包含/,则就将其视为路径名,否则就按 PATH环境变量,在它所指定的各目录中搜寻可执行文件。exec族函数参数极难记忆和分辨,函数名中的字符会给我们一些帮助:
l : 使用参数列表
p:使用文件名,并从PATH环境进行寻找可执行文件
v:应先构造一个指向各参数的指针数组,然后将该数组的地址作为这些函数的参数。
e:多了envp[]数组,使用新的环境变量代替调用进程的环境变量
#include <unistd.h>
extern char **environ;
int execl(const char *path, const char *arg, ...);
int execlp(const char *file, const char *arg, ...);
int execle(const char *path, const char *arg,..., char * const envp[]);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
int execvpe(const char *file, char *const argv[],char *const envp[]);
带e的不要求
若是当中出错,可以调用perror("why");来查看原因
excel:
可以用此函数来查看系统时间
//echoarg.c
#include <stdio.h>
int main(int argc,char *argv[])
{
int i = 0;
for(i = 0; i < argc; i++)
{
printf("argv[%d]: %s\n",i,argv[i]);
}
return 0;
}
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
//函数原型:int execl(const char *path, const char *arg, ...);
int main(void)
{
printf("the current time is\n");
if(execl("/bin/date","echoarg",NULL,NULL) == -1)
{
printf("execl failed!\n");
//perror("why");
}
printf("after execl\n");
return 0;
}
excelp:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
//函数原型:int execl(const char *path, const char *arg, ...);
int main(void)
{
printf("the current time is\n");
if(execlp("date","echoarg",NULL,NULL) == -1)
{
printf("execl failed!\n");
//perror("why");
}
printf("after execl\n");
return 0;
}
excelp();可以不用添加路径,就能通过环境变量查到可执行文件
execvp:
将excel后面的三个参数另外做成一个数组展示
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
//函数原型:int execl(const char *path, const char *arg, ...);
int main(void)
{
printf("the current time is\n");
char *argv[] = {"echoarg",NULL,NULL};
if(execvp("date",argv) == -1)
{
printf("execl failed!\n");
//perror("why");
}
printf("after execl\n");
return 0;
}
execv:
与前面execl类似,不加p就需要声明绝对路径
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
//函数原型:int execl(const char *path, const char *arg, ...);
int main(void)
{
printf("the current time is\n");
char *argv[] = {"echoarg",NULL,NULL};
if(execv("/bin/date",argv) == -1)
{
printf("execl failed!\n");
//perror("why");
}
printf("after execl\n");
return 0;
}
六:exec函数与fork配合使用
在之前的demo中生成一个可执行文件,之后使用execl调用
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
int main()
{
pid_t pid;
int data;
while(1){
printf("please input data:\n");
scanf("%d",&data);
if(data == 1){
pid = fork();
if(pid > 0){
wait(NULL);
}
if(pid == 0){
execl("./changeData","changeData","test.txt",NULL);
}
}else{
printf("do nothing!\n");
}
}
return 0;
}
七:system函数
int system(const char *cmd);
成功,则返回进程的状态值,当sh不能执行时,返回127;失败返回-1
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
int main()
{
pid_t pid;
int data;
while(1){
printf("please input data:\n");
scanf("%d",&data);
if(data == 1){
pid = fork();
if(pid > 0){
wait(NULL);
}
if(pid == 0){
// execl("./changeData","changeData","test.txt",NULL);
system("./changeData test.txt");
}
}else{
printf("do nothing!\n");
}
}
return 0;
}
//日期显示
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
//函数原型:int execl(const char *path, const char *arg, ...);
int main(void)
{
printf("the current time is\n");
if(system("date") == -1)
{
printf("execl failed!\n");
//perror("why");
}
printf("after execl\n");
return 0;
}
八:popen();
popen(command,mode);
cmd:使用的命令
mode:“r” / “w”
比system好用的是可以获得输出结果
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
//函数原型:int execl(const char *path, const char *arg, ...);
//size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
int main(void)
{
char ret[1024] = {0};
FILE *fp;
fp = popen("date","r");
int n_read = fread(ret,1,1024,fp);
printf("ret print %d byte,ret=%s",n_read,ret);
return 0;
}