一、进程的概念
1.可以用ps或top查看进程
2.pid=0;交换进程(作用是进程调度)
pid=1;init进程(作用是系统初始化)
3.getpid();//获取自身的进程标识符
4.getppid();//获取父进程的进程标识符
什么是父进程?
答:进程A创建了进程B,那么A叫做父进程,B叫做子进程。
二、进程的创建
使用fork函数创建一个进程,
pid_t fork(void),
fork函数调用成功,返回两次。
返回值为0,代表当前进程为子进程。
返回值为非负数,代表当前进程为父进程。
返回值为-1,代表调用失败。
(1)查看pid_t fork(void)函数原型,判断怎样使用该api.
man 2 fork
发现需要2个头文件。
(2)调用api创建进程并查看其pid
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main(void)
{
pid_t pid,pid1,pid2;
char return_of_fork;
pid1 = getpid();//pid of 1_creat_process.c
printf("this is father process,pid = %d\n",pid1);
return_of_fork = fork();
if(return_of_fork > 0)
{
printf("this is father process,pid = %d\n",getpid());
}
else if(return_of_fork == 0)
{
printf("this is son process,pid = %d\n",getpid());
}
else if(return_of_fork < 0)
{
printf("fork failed\n");
}
return 0;
}
注意,执行完fork,
linux系统会为子进程拷贝一份文件(包括程序的正文,初始化的数据,未被初始化的数据,堆,栈)
然后运行父进程和子进程(进程调度),
运行父进程后,return_of_fork >0,并打印,
运行子进程后,return_of_fork =0,并打印,
*因此才能看到既满足 if(return_of_fork > 0),又满足 if(return_of_fork == 0); *
其实并不是同时满足,
上面提到,系统会为紫荆城拷贝一份文件,
在进程调度时,会分别执行父进程和子进程(用不太规范的话来描述:执行了2份main函数),
在父进程的调度中return_of_fork > 0并打印,
在子进程的调度中return_of_fork > 0==0并打印。
注意:向执行memcpy()时在地上申请空间
调用函数时,在栈上运行
三、创建进程时发生了什么
四、fork的应用场景
要求:父进程解析键盘输入的数值,当键盘输入的值为9时创建子进程并屏幕输出。
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main(void)
{
char return_of_fork;
int data = 1;
while(1)
{
printf("please input a data\n");
scanf("%d",&data);
if(data == 9)
{
return_of_fork = fork();
if(return_of_fork > 0)
{
printf("father process,pid = %d\n",getpid());
}
else if(return_of_fork == 0)
{
while(1)
{
printf("child process,pid = %d\n",getpid());
sleep(1);
}
}
}
else
{
printf("wait ! do nothing\n");
}
}
return 0;
}
五、vfork和fork的区别
(1)直接使用父进程空间,不拷贝
(2)保证子进程优先运行,当子进程调用exit退出后,父进程才执行
六、进程的退出
- 种类:5种正常退出+3种异常退出
(1)5种正常退出:
main函数调用return;
进程调用exit();
进程调用_exit()或_Exit()
进程最后1个线程返回;
最后1个线程调用pthread_exit
(2)3种异常退出
调用adort;
当进程收到某些信号时;
最后1个线程对取消(cancellation)请求做出响应
七、父进程等待子进程退出
1.调用的api:
wait();//调用wait,可以防止子进程变成僵尸进程(子进程退出状态未被收集,则子进程会变为僵尸进程)
或waitpid();
man 2 wait
(1)wait()的使用方法,和常见使用场景
等待子进程的退出,并且收集子进程退出的状态(子进程正常退出和异常退出返回的状态不同)。
子进程的状态被返回给wait(int *wstatus)的参数
a)背景:忽略使用wait()时会导致子进程变成僵尸进程。
运行下面的代码,键盘输入9后后创建子进程,每隔1秒屏幕输出1次,打印三次后会exit子进程
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
int main(void)
{
pid_treturn_of_fork;
int cnt=0;
int data = 1;
return_of_fork = fork();
while(1)
{
printf("please input a data\n");
scanf("%d",&data);
if(return_of_fork > 0)
{
while(1)
{
printf("father process,pid = %d\n",getpid());
sleep(1);
}
}
else if(return_of_fork == 0 && data == 9)
{
while(1)
{
printf("child process,pid = %d\n",getpid());
sleep(1);
cnt++;
if(cnt ==3)
{
exit(0);
}
}
}
else
{
printf("wait ! do nothing\n");
}
}
return 0;
}
如图,键盘输入1后,一直调度父进程。
当键盘输入9后,开始调度子进程。子进程运行3次之后,子进程被结束。
查看pid,发现子进程pid为22507,子进程变成僵尸进程了。
Z+指僵尸进程
b)使用wait()获取子进程退出状态,避免子进程变成僵尸进程
使用wait(NULL);(如果我们对这个子进程是如何死掉毫不在意,只想把这个僵尸进程消灭掉,(事实上绝大多数情况下,我们都会这样想),我们就可以设定这个参数为NULL)
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
int main(void)
{
pid_t return_of_fork;
int cnt=0;
int data = 1;
return_of_fork = fork();
while(1)
{
printf("please input a data\n");
scanf("%d",&data);
if(return_of_fork > 0)
{
wait(NULL);
while(1)
{
printf("father process,pid = %d\n",getpid());
sleep(1);
}
}
else if(return_of_fork == 0 )
{
while(data == 9)
{
printf("child process,pid = %d\n",getpid());
sleep(1);
cnt++;
if(cnt ==3)
{
exit(0);
}
}
}
else
{
printf("wait ! do nothing\n");
}
}
return 0;
}
如图,使用wait(NULL),可以关掉僵尸进程
/------------/
如图,使用WEXITSTATUS(status)可以查看子进程正常终止返回的状态;
使用方法如下:
int status_childProcess;
wait(&status_childProcess);
printf(“child process exit status:%d\n”,WEXITSTATUS(status_childProcess;));
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
int main(void)
{
pid_t return_of_fork;
int cnt=0;
int data = 1;
int status_childProcess;
return_of_fork = fork();
while(1)
{
printf("please input a data\n");
scanf("%d",&data);
if(return_of_fork > 0)
{
wait(&status_childProcess);
printf("child process exit status:%d\n",WEXITSTATUS(status_childProcess));
while(1)
{
printf("father process,pid = %d\n",getpid());
sleep(1);
}
}
else if(return_of_fork == 0 )
{
while(data == 9)
{
printf("child process,pid = %d\n",getpid());
sleep(1);
cnt++;
if(cnt ==3)
{
exit(1);
}
}
}
else
{
printf("wait ! do nothing\n");
}
}
return 0;
}
如图所示配合wait()相关的宏,可以获得子进程退出状态。而且父进程是在子进程退出后才执行的。
(2)waitpid()的使用方法,和常见使用场景
(3)孤儿进程
获取父进程的api:
getppid();
init进程的pid号==1
八、exec族函数
1.exec族函数中的函数调用失败时会设置error并返回-1,然后从源程序调用点接着往下执行。
执行成功后不会返回,也不会从源程序调用点接着往下执行。
函数原型:
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[]);
参数
path参数表示你要启动程序的名称包括路径名
arg参数表示启动程序所带的参数,一般第一个参数为要执行命令名,不是带路径且arg必须以NULL结束
返回值:成功返回0,失败返回-1
2.其他知识
(1)如何查看linux指令所在路径
如图,date指令用于查看系统时间。
ls指令用于查看该路径下的文件。
whereis用于获取linux指令的路径。
如上图所示,ls所在路径为“/bin/ls”,现在要求用execl调用ls.
程序如下
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(void)
{
printf("before exec\n");
if(execl("/bin/ls","ls","-l",NULL) == -1)//exec族函数中的函数调用失败时会设置error并返回-1,然后从源程序调用点接着往下执行。执行成功后不会返回,也不会从源程序调用点接着往下执行。
{
printf("exec failed\n");
}
printf("after exec\n");
}
运行结果:如图运行成功,execl调用了ls.
调用成功,不返回-1,不执行 printf(“exec failed\n”);
调用成功,也没有继续往下执行,即不执行 printf(“after exec\n”);
若调用失败,则如图所示(失败的原有是 ls的路径不对)
(2)怎样查看环境变量
(3)怎样配置临时环境变量(临时环境变量的生存周期在该终端框被关闭时结束)
先使用pwd查看当前路径,
再使用export PATH=
"="后面的参数¥PATH是指原先的环境变量。
添加好临时环境变量后就可以在别的路径下调用这里的可执行文件。
例如myechoarg是可执行文件,在配置环境变量前myechoarg只能在这个路径执行,
配置环境变量后,就可以在任意路径下执行。
(4)perror();的用法
(5)
九、system
system()是封装后的execl();
system()和execl()的区别是:system执行后,会从源程序调用点继续往下执行。
函数原型:
如图,是system的使用方法,system的参数是可执行文件的路径
在本路径下,有4个文件
main.c编译生成的目标文件是work,其运行结果是:屏幕输出hello
my_system.c是调用system()的demo,其编译生成目标文件是myrun,运行结果是"执行work文件"
补充:可直接调用shell指令
如图,调用ls
十、popen
popen和system相比,
有1个好处,
即:可以获得运行的结果
函数原型
其返回值为FILE型,
#include <stdio.h>
#include <stdlib.h>
int main()
{
char return_buffer[1024] = {0};
FILE *fp;
fp = popen("ps","r");
int nread = fread(return_buffer,1,1024,fp);
printf("return_buffer %d byte,buffer:%s\n",nread,return_buffer);
return 0;
}
如图,popen的返回值是运动的结果;