获取当前进程的PID和父进程的PID。
创建getpid1.c,编辑如下内容
执行过程:
在当前系统中使用ps和grep查找父进程2330,可以看到,id为2330的进程为bash即当前的脚本程序,因为是在当前bash中运行此程序,所以父进程为bash
进程创建fork()
产生进程的方式比较多,fork()是其中的一种方式。fork()函数以父进程为蓝本复制一个进程,其ID号和父进程ID号不同在Linux环境下,fork()是以写复制实现的。只有内存等与父进程不同,其他与父进程共享,只有在父进程或者子进程进行了修改后,才重新生成一份。
原理:
fork()执行一次,返回两次。在父进程和子进程中返回的是不同的值,父进程返回的是子进程的ID号,而子进程中则返回0,如果出错,fork()返回1.
fork函数创建一个新的进程,并从内核中为此进程得到一个新的可用进程ID,之后为这个新进程分配进程空间,并将父进程的进程空间中的内容复制到子进程的进程空间中,包括父进程的数据段+堆栈段,并与父进程共享代码段。
fork函数之后,子进程从等待fork返回开始执行,而不是从头开始。
注意:子进程完全复制了父进程的地址空间的内容,包括堆栈段+数据段的内容。但是,**子进程并没有复制代码段,而是和父进程共享代码段。**代码段是只读的,不存在修改的问题,因此可以共用。在创建一个子进程后,子进程的地址空间完全和父进程分开,父子进程是两个独立的进程。
例题1.
fork1.c
```go
#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
#include<stdlib.h>
int main()
{
int n = 10;
//进程复制
int pid = fork();
//fork失败
if(-1 == pid)
{
perror("fork");
exit(-1);
}
if(0 == pid)
{//子进程
printf("child pid=%d,ppid=%d\n",getpid(),getppid());
printf("&n=0x%x\n",&n);
exit(0);
}
else if(pid > 0)
{//父进程
//让当前进程休眠1秒
//sleep(1);
//等待进程结束
//wait(NULL);
printf("father pid=%d,ppid=%d\n",getpid(),getppid());
printf("&n=0x%x\n",&n);
// wait(NULL);
}
return 0;
}
注意:当在父进程前加sleep(1)当前程序等待1秒让子进程先结束,不让子进程成为孤儿,也可输入wait(NULL)等待进程结束
当在父进程前后加wait(NULL)
例题2
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<stdlib.h>
int main()
{
int n = 1;
//如果fork成功,则返回两次
pid_t pid = fork();
if(-1 == pid)
{
perror("fork:");
exit(-1);
}
if(pid == 0)
{//子进程
while(1)
{
n++;
printf("child pid=%d,ppid=%d\n",getpid(),getppid());
sleep(1);
}
exit(0);
}
else
{//父进程
while(1)
{
printf("father pid=%d,n=%d\n",getpid(),n);
sleep(1);
}
}
printf("main ending!\n");
return 0;
}
执行过程:
子进程会拷贝父进程的所有资源,变量。
注意:子进程拷贝了父进程数据空间、堆、栈等资源的副本,
父子进程间不共享这些存储空间,共享的空间只有代码段,
子进程修改一个全局变量,父进程的这个全局变量不会改变,因为是一个副本。
强制结束父进程,则1号接管子进程
注意结束进程执行过程如下:
fork3.c
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
/*全局变量*/
char *msg = "shenlan";
int g_value = 0;
int main()
{
printf("%s\n", msg);
int val = 0;
printf("uid:%d,euid:%d,gid:%d,egid:%d\n", getuid(), geteuid(), getgid(), getegid());
pid_t id = fork();
if (id < 0)
{
printf("fork error\n");
exit(1);
}
else if (id == 0)
{//子进程执行代码
val++;
g_value++;
printf("child:pid:%d,ppid:%d,val:%d,&val:0x%x,g_value:%d,&g_value:0x%x\n",
getpid(), getppid(), val, &val, g_value, &g_value);
}
else
{//父进程执行代码
sleep(1);
printf("father:pid:%d,ppid:%d,val:%d,&val:0x%x,g_value:%d,&g_value:0x%x\n",
getpid(), getppid(), val, &val, g_value, &g_value);
sleep(3);
}
return 0;
}
执行过程:
filed1.c
/*
父子进程的文件描述符可以共享,但是要在fork之前打开,并在公有代码中关闭文件
*/
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(int argc, char *args[])
{
// 打开文件描述符:在fork之前打开才能让父进程和子进程共享
int fd = open("data.txt", O_RDONLY);
// 判断文件打开是否成功
if (fd == -1)
{
printf("File open failed : %s\n", strerror(errno));
}
// 定义读取文件的缓冲区
char buf[1024];
//读取到的字节数
int nread;
// 执行fork函数
pid_t pd = fork();
// 判断fork是否成功
if (pd == -1)
{
printf("fork failed : %s\n", strerror(errno));
}
// 通过if...else...执行父子进程的特有代码
if (pd > 0)
{
sleep(5);
//清空缓冲区内存
memset(buf, 0, sizeof(buf));
// 读取文件内容
nread = read(fd, buf, 7);
buf[nread] = '\0';
//将读取到的内容显示到屏幕上
printf("%s\n", buf);
printf("father fd = %d\n", fd);
//关闭文件描述符
close(fd);
}
else
{
//清空缓冲区内存
memset(buf, 0, sizeof(buf));
// 读取文件内容
nread = read(fd, buf, 5);
buf[nread] = '\0';
//将读取到的内容显示到屏幕上
printf("%s\n", buf);
printf("son fd = %d\n", fd);
//关闭文件描述符
close(fd);
}
printf("process [%d] ending!\n", getpid());
return 0;
}
执行过程:
problem1.c
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main(void)
{
int i;
for(i=0; i<2; i++){
fork();
printf("-");
}
return 0;
}
vfork1.c
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
/*全局变量*/
int g_value = 0;
int main()
{
int val = 0;
printf("uid:%d,euid:%d,gid:%d,egid:%d\n", getuid(), geteuid(), getgid(), getegid());
pid_t id = vfork();
if (id == -1)
{
printf("fork error\n");
exit(1);
}
if (id == 0)
{//子进程执行代码
val++;
g_value++;
printf("child:pid:%d,ppid:%d,val:%d,&val:0x%x,g_value:%d,&g_value:0x%x\n",
getpid(), getppid(), val, &val, g_value, &g_value);
exit(0);
}
else if(id > 0)
{//父进程执行代码
sleep(2);
printf("father:pid:%d,ppid:%d,val:%d,&val:0x%x,g_value:%d,&g_value:0x%x\n",
getpid(), getppid(), val, &val, g_value, &g_value);
}
return 0;
}
执行过程:
wait1.c
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<stdlib.h>
int main()
{
int n = 10;
int pid = fork();
if(-1 == pid)
{
perror("fork");
exit -1;
}
if(0 == pid)
{
printf("child pid=%d,ppid=%d\n",getpid(),getppid());
sleep(1);
exit(1);
}
else if(pid > 0)
{
printf("father pid=%d,ppid=%d\n",getpid(),getppid());
int status;
//阻塞当前进程,等待子进程推出
//pid_t pid = wait(&status);
//pid_t pid = waitpid(-1,&status,0);
//WNOHANG,代表非阻塞
pid_t pid = waitpid(-1,&status,WNOHANG);
printf("child process id = %d\n",pid);
//如果子进程返回值不为0(exit 0)
if(WIFEXITED(status))
{
printf("child process status = %d\n",WEXITSTATUS(status));
}
}
return 0;
}
执行结果: