1.进程标识符
每个进程都有一个非负整数表示的唯一ID,叫做pid
pid = 0;称为进程交换。作用—进程调度
pid = 1;init进程。作用—系统初始化
编程调用getpid函数获取自身的进程标识符
getppid获取父进程的进程标识符
#include<stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
pid_t pid;
pid = getpid();
printf("my pid is %d\n",pid);
return 0;
}
2.fork函数创建一个进程
函数原型
SYNOPSIS
#include <unistd.h>
pid_t fork(void);
返回值 pid_t
fork函数调用成功返回两次:
返回值为0,代表当前进程是子进程
返回值为非负数,代表当前进程为父进程
返回失败返回-1
#include<stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
pid_t pid;
pid = getpid();
fork();
if(pid == getpid()){
printf("this is father,pid = %d\n",getpid());
}else{
printf("this is child,pid = %d\n",getpid());
}
return 0;
}
运行结果:
this is father,pid = 3339
this is child,pid = 3340
#include<stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
pid_t pid;
pid_t pid2;
pid = getpid();
printf("before fork pid = %d\n",pid);
fork();
pid2 = getpid();
printf("after fork pid = %d\n",pid2);
if(pid == pid2){
printf("this is father,pid = %d\n",getpid());
}else{
printf("this is child,pid = %d\n",getpid());
}
return 0;
}
运行结果:
before fork pid = 3441
after fork pid = 3441
this is father,pid = 3441
after fork pid = 3442
this is child,pid = 3442
就是说fork以后,父进程和子进程都会执行下面的代码,子进程和父进程走不一样的分支
#include<stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
pid_t pid;
printf("father id = %d\n",getpid());
pid = fork();
if(pid >0){
printf("this is father,pid = %d,fork return = %d\n",getpid(),pid);
}else if(pid == 0){
printf("this is child,pid = %d,fork return = %d\n\n",getpid(),pid);
}
return 0;
}
运行结果:
father id = 3557
this is father,pid = 3557,fork return = 3558
this is child,pid = 3558,fork return = 0
pid值为0代表是子进程,大于0代表是父进程。
子进程拷贝了一份父进程的内容
在早期的Linux底下,子进程会拷贝父进程存储空间中的正文,初始化的数据,把堆栈,命令行参数进行拷贝,以及打开的文件io流等等全部进行拷贝
后面Linux技术的内容更新,不一定会把这些内容进行拷贝,而是进行写时拷贝,假设一个数据,如果后面的代码子进程没有对该数据进行改变的话,采用共享的方式,共享这个内存空间,不一定要做拷贝,这个就是大致上的写实拷贝
3.fork创建一个子进程的一般目的
(1)一个父进程希望复制自己,使父,子进程同时执行不同的代码段。这在网络服务进程中是常见的——父进程等待客户端的服务请求。当这种情况到达时,父进程调用fork,使子进程处理此请求。父进程则继续等待下一个服务请求到达。
(2)一个进程要执行一个不同的程序。这对shell是常见的情况。在这种情况下,子进程从fork返回后立即调用exec。
模拟服务器接收客户端服务请求
#include<stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
pid_t pid;
int data = 0;
while(1){
printf("please input a data:\n");
scanf("%d",&data);
if(data == 1){
pid = fork();
if(pid > 0){
}
else if(pid == 0){
while(1){
printf("do net request,pid = %d\n",getpid());
sleep(3);
}
}
}else{
printf("wait,do nothing\n");
}
}
return 0;
}
运行结果:
please input a data:
2
wait,do nothing
please input a data:
3
wait,do nothing
please input a data:
1
please input a data:
do net request,pid = 3680
do net request,pid = 3680
4
wait,do nothing
please input a data:
do net request,pid = 3680
5
wait,do nothing
please input a data:
do net request,pid = 3680
1
please input a data:
do net request,pid = 3681
do net request,pid = 3680
do net request,pid = 3681
do net request,pid = 3680
1
please input a data:
do net request,pid = 3682
do net request,pid = 3681
do net request,pid = 3680
do net request,pid = 3682
do net request,pid = 3681
do net request,pid = 3680
do net request,pid = 3682
4.vfork函数创建进程
vfork函数和fork函数的区别
(1)vfork直接使用父进程存储空间,不拷贝。
(2)vfork保证子进程先运行,当子进程调用exit退出后,父进程才执行。
用代码显示这两个函数的区别:
(1)fork函数
#include<stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
pid_t pid;
pid = fork();
if(pid > 0){
while(1){
printf("this is father print,pid = %d\n",getpid());
sleep(1);
}
}
else if(pid == 0){
while(1){
printf("this is child print,pid = %d\n",getpid());
sleep(1);
}
}
return 0;
}
运行结果:
this is father print,pid = 3799
this is child print,pid = 3800
this is father print,pid = 3799
this is child print,pid = 3800
this is father print,pid = 3799
this is child print,pid = 3800
this is father print,pid = 3799
this is child print,pid = 3800
this is father print,pid = 3799
this is child print,pid = 3800
this is father print,pid = 3799
this is child print,pid = 3800
this is father print,pid = 3799
this is child print,pid = 3800
this is father print,pid = 3799
this is child print,pid = 3800
父子进程都会执行,互相争夺CPU,不一定是一人一次,取决于进程的调度,fork函数的特点。
(2)vfork函数
#include<stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
pid_t pid;
pid = vfork();
if(pid > 0){
while(1){
printf("this is father print,pid = %d\n",getpid());
sleep(1);
}
}
else if(pid == 0){
while(1){
printf("this is child print,pid = %d\n",getpid());
sleep(1);
}
}
return 0;
}
运行结果:
this is child print,pid = 3827
this is child print,pid = 3827
this is child print,pid = 3827
this is child print,pid = 3827
this is child print,pid = 3827
this is child print,pid = 3827
this is child print,pid = 3827
this is child print,pid = 3827
一直都在运行子进程
(3)vfork函数例子
#include<stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include<stdlib.h>
int main()
{
pid_t pid;
int cnt = 0;
pid = vfork();
if(pid > 0){
while(1){
printf("cnt = %d\n",cnt);
printf("this is father print,pid = %d\n",getpid());
sleep(1);
}
}
else if(pid == 0){
while(1){
printf("this is child print,pid = %d\n",getpid());
sleep(1);
cnt++;
if(cnt == 3){
exit(0); 0代表其推出状态
}
}
}
return 0;
}
运行结果:
this is child print,pid = 3876
this is child print,pid = 3876
this is child print,pid = 3876
cnt = 3
this is father print,pid = 3875
cnt = 3
this is father print,pid = 3875
cnt = 3
this is father print,pid = 3875
cnt = 3
this is father print,pid = 3875
cnt = 3
this is father print,pid = 3875
子进程运行次数达到,退出,运行父进程,因为直接使用的是父进程的存储空间,所以cnt的值被改变,子程序退出时,
需要用一个好的方式推出,否则会破坏变量。
5.进程推出
进程推出分为正常推出和异常推出
正常推出
1.Main函数调用return
2.进程调用exit(),标准库
3.进程调用_exit()或者_Exit(),属于系统调用
补充:
1.进程最后一个线程返回
2.最后一个线程调用pthread_exit
异常推出
1.调用abort
2.当进程收到某些信号时,如ctrl+C
3.最后一个线程对取消(cancellation)请求做出响应
6.父进程等待子进程退出
子进程正常退出有5种方式,异常推出3种。
父进程等待收集子进程的退出状态,函数原型
NAME
wait, waitpid, waitid - wait for process to change state
SYNOPSIS
#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(int *status);
status参数: 是一个整型数指针
非空:子进程退出状态放在它所指向的地址中,打印时需要用到宏WEXITSTATUS(status),取子进程正常退出
的前三种参数的低八位。
空:不关心退出状态
pid_t waitpid(pid_t pid, int *status, int options);
wait使调用者阻塞,waitpid有一个选项options,可以使调用者不阻塞
对于waitpid函数中的pid参数的作用解释如下:
pid == -1 等待任一子程序。就这一方面而言,waitpid与wait等效。
pid > 0 等待其进程ID与pid相等的子进程。(用的较多)
pid == 0 等待其组ID等于调用者ID的任一子进程。
pid < -1 等待其组ID等于pid绝对值的任一子进程。
waitpid的 options常量用的较多的宏:
WNOHANG :若pid指定的子进程并不是立即可用的,则waitpid不阻塞,此时其返回值为0。
int waitid(idtype_t idtype, id_t id, siginfo_t *infop, int options);
如果其所有子进程都还在运行,则阻塞。
如果一个子进程已经终止,正等待父进程获取其终止状态,则取得该子进程的终止状态立即返回。
如果它没有任何子进程,则立即出错返回。
小结
基础的东西,会随时修改