一.使用的指令
ps指令可以查看当前运行的进程
ps -aux 和ps -ef 查看所有进程
ps -aux|grep 进程类型 查看指定进程
top指令可以观察cpu、内存使用情况
二.getpid函数
函数原型pid_z getpid(void)
获取当前进程的pid号(一般是该程序),返还一个非负整数
每一个进程都一个非负整数表示唯一ID叫做PID,
pid_z pid;
pid = getpid();
三.fork函数
pid_t fork(void);——用于创建进程的函数
fork函数返回两次返回值: 返回值为0时,代表当前进程为子进程;
返回值为负数,代表当前进程为父进程;
返回值为-1时,创建进程失败;
实例1:
在调用fork函数后,下面fork会返回两次并创建一个子进程
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
void main()
{
pid_t pid;
pid_t pid2;
pid = getpid();
printf("before fork pid is %d\n",pid);
fork();
pid2 = getpid();
printf("pid2 = %d\n",getpid());
if(pid == pid2)
{
printf("This is father fork\n");
}else
{
printf("after fork pid is %d\n",getpid());
}
}
使用fork创建的子进程会将父进程的代码变量等复制一份,在父子进程中对变量进行修改互相之间不影响,因为是复制的变量,不共用内存;
实例2:
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
void main()
{
pid_t pid;
pid_t pid2;
int a=10;
int b=5;
pid = fork();
if(pid > 0)
{
a = 50;
printf("a = %d,b = %d pid = %d\n",a,b,getpid());
}
if(pid == 0 )
{
b = 30;
printf("a = %d,b = %d pid = %d\n",a,b,getpid());
}
}
运行结果如上图:
使用fork的场景:
实例3:
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
void main()
{
pid_t pid;
int a;
while(1)
{
printf("please input data\n");
scanf("%d",&a);
if(a == 1)
{
pid = fork();
if(pid >0)
{
;
}else if(pid == 0)
{
while(1)
{
printf("child fork %d\n",getpid());
sleep(2);
}
}
}else{
printf("do nothing\n");
}
}
}
通过父进程不断创建子进程.每当输入1就创建一个 子进程,各个进程一起运行。
四.vfork函数
pid_t vfork(void);也可以创建进程,
vfork和fork的区别
-
区别一:vfork直接使用父进程存储空间,不拷贝。(在fork函数中创建的进程会复制一份父进程代码,所以在修改子进程时对父进程变量无影响,而在vfork创建的进程中,由于是共享的内存,对子进程中变量进行修改会影响这个变量在父进程中的值)
- 区别二:vfork保证子进程先运行,当子进程调用exit退出后,父进程才执行。(在fork中子进程和父进程的运行顺序有系统调度决定,在vfork创建的进程会先执行子进程等待子进程结束后父进程才执行,但需要子进程exit正常退出)
实例4:
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
void main()
{
pid_t pid;
pid_t pid2;
int cnt = 0;
int a = 10;
pid = vfork();
if(pid > 0)
{
while(1)
{
printf("this is father fork %d a = %d\n",pid,a);
sleep(2);
}
}
if(pid == 0 )
{
while(1)
{
printf("this is chlid fork %d\n",getpid());
sleep(2);
cnt++;
a = 20;
if(cnt==3)
{
exit(1);
}
}
}
}
运行结果如上图: (子进程运行次后退出进入父进程)
五.进程退出与等待
退出的类型
正常退出:
1.Main函数调用return;
2.进程调用exit(),标准c库;
3.进程调用_exit()或者_Exit(),属于系统调用;
异常退出:
1.调用abort;
2.当进程收到某些信号时,如ctrl+C;
3.最后一个线程对取消(cancellation)请求做出响应;
wait和waitpid函数
pid_t wait(int *status)
在使用fork函数时,子进程与父进程的运行顺序是靠系统调度的,可以使用wait()函数对父进程进行阻塞 ;
当一个进程通过exit退出后需要对该进程的状态进行收集,可以使用wait函数进行收集exit(5)中的5,当不关心返回的状态码时 status可以为NULL;
通过宏来解析收集到的结束码WEXITSTATUS(status) 打印结果为5;
WIFEXITED(status) | 如果子进程正常结束,它就返回真;否则返回假。 |
WEXITSTATUS(status) | 如果WIFEXITED(status)为真,则可以用该宏取得子进程exit()返回的结束代码。 |
WIFSIGNALED(status) | 如果子进程因为一个未捕获的信号而终止,它就返回真;否则返回假。 |
WTERMSIG(status) | 如果WIFSIGNALED(status)为真,则可以用该宏获得导致子进程终止的信号代码。 |
WIFSTOPPED(status) | 如果当前子进程被暂停了,则返回真;否则返回假。 |
WSTOPSIG(status) | 如果WIFSTOPPED(status)为真,则可以使用该宏获得导致子进程暂停的信号代码。 |
pid_t waitpid(pid_t pid,int *status,int options)
pid<-1 等待进程组号为pid绝对值的任何子进程。
pid=-1 等待任何子进程,此时的waitpid()函数就退化成了普通的wait()函数。
pid=0 等待进程组号与目前进程相同的任何子进程,也就是说任何和调用waitpid()函数的进程在同一个进程组的进程。
pid>0 等待进程号为pid的子进程。
options参数为宏:
wait和waitpid的区别
当使用wait函数时父进程会阻塞等待收集,而waitpid函数可以使用宏(WNOHANG)来避免阻塞;
实例5:
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
void main()
{
pid_t pid;
pid_t pid2;
int cnt = 0;
int status = 0;
pid = fork();
if(pid > 0)
{
// wait(&status);
waitpid(pid,&status,WNOHANG);
while(1)
{
printf("this is father fork %d status = %d\n",pid,WEXITSTATUS(status));
sleep(2);
}
}
if(pid == 0 )
{
while(1)
{
printf("this is chlid fork %d\n",getpid());
sleep(2);
cnt++;
if(cnt==3)
{
exit(5);
}
}
}
}
孤儿进程
父进程如果不等待子进程退出,在子进程之前就结束了自己的“生命”,此时子进程叫做孤儿进程Linux避免系统存在过多孤儿进程,init进程收留孤儿进程,变成孤儿进程的父进程