1.fork()
fork函数主要用于在一个进程中创建子进程,目的是为了能够在一个程序里面分别独立执行多个任务,相互之间还没有影响
#include <sys/types.h>
#include <unistd.h>
pid_t fork(void);
功能:
父进程创建一个子进程
参数:
无
返回值:
成功:
>0 子进程的进程号,标识父进程的代码区
0 标识子进程的代码区
失败:
-1
1.1 实例1:fork的含义
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main(int argc, char const *argv[])
{
//注意:只要执行一次fork,就会在原有的进程基础上创建一个子进程
//如果不区分父子进程的代码区,后面所有的代码都会执行
// fork();
// fork();
// fork();
// while(1)
// {
// }
int i;
for(i = 1; i <= 3; i++)
{
fork();
printf("hello world\n");
sleep(1);
}
return 0;
}
1.2 实例2:父子进程区分代码区
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main(int argc, char const *argv[])
{
//如果要使用fork在一个程序里面创建子进程
//肯定是为了执行不同的任务,所以一般都要区分父子进程的代码区
//使用fork创建一个子进程
pid_t pid = fork();
if(pid == -1)
{
perror("fork error");
return -1;
}
else if(pid > 0) //父进程的代码区
{
printf("正在执行父进程的代码\n");
}
else //子进程的代码区
{
printf("正在执行子进程的代码\n");
}
//如果区分了父子进程运行的代码,则父进程和子进程都只执行自己的,
//但是当区分的代码执行完毕后,如果进程没有结束,则会继续向下执行,
//如果不区分父子的代码区,那么都会执行
printf("hello world\n");
return 0;
}
1.3 实例3:父子进程运行顺序问题
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main(int argc, char const *argv[])
{
//进程的调度机制是时间片轮转,上下文切换
//所以父子进程在执行的时候不是父进程执行完
//再执行子进程,而是来回交替执行,谁先执行
//谁后执行是不确定的
pid_t pid = fork();
if(pid == -1)
{
perror("fork error");
return -1;
}
else if(pid > 0) //父进程的代码区
{
printf("正在执行父进程的代码\n");
sleep(1);
printf("父进程父进程父进程\n");
}
else //子进程的代码区
{
printf("正在执行子进程的代码\n");
sleep(1);
printf("子进程子进程子进程\n");
}
while(1){}
return 0;
}
1.4 实例4:父子进程的用户空间资源问题
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
//当父进程执行完fork之后,就会创建一个子进程,
//子进程会将父进程的虚拟内存复制一份作为子进程的空间,
//当复制完毕之后,父进程和子进程的用户空间是独立的,
//所以父进程或者子进程任意对用户空间操作,相互是不会
//有影响的
int a = 100;
int main(int argc, char const *argv[])
{
int b = 200;
static int c = 300;
pid_t pid = fork();
if(pid == -1)
{
perror("fork error");
return -1;
}
else if(pid > 0) //父进程的代码区
{
printf("正在执行父进程的代码\n");
a = 666;
b = 777;
c = 888;
printf("父进程:a = %d, b = %d, c = %d\n", a, b, c);
printf("父进程:&a = %p, &b = %p, &c = %p\n", &a, &b, &c);
}
else //子进程的代码区
{
sleep(1);
printf("正在执行子进程的代码\n");
printf("子进程:a = %d, b = %d, c = %d\n", a, b, c);
printf("子进程:&a = %p, &b = %p, &c = %p\n", &a, &b, &c);
}
while(1){}
return 0;
}
1.5 实例5:父子进程对共享资源操作问题
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(int argc, char const *argv[])
{
int fd;
if((fd = open(argv[1], O_RDWR)) == -1)
{
perror("open error");
return -1;
}
pid_t pid = fork();
if(pid == -1)
{
perror("fork error");
return -1;
}
else if(pid > 0) //父进程的代码区
{
printf("正在执行父进程的代码\n");
//父进程向文件写入数据
write(fd, "hello world\n", 12);
printf("offset = %ld\n", lseek(fd, 0, SEEK_CUR));
}
else //子进程的代码区
{
sleep(1);
printf("正在执行子进程的代码\n");
//子进程从文件中读取数据
printf("offset = %ld\n", lseek(fd, 0, SEEK_CUR));
lseek(fd, 0, SEEK_SET);
char buf[32] = {0};
read(fd, buf, 32);
printf("buf = [%s]\n", buf);
}
while(1){}
return 0;
}
2.getpid()/getppid()
#include <sys/types.h>
#include <unistd.h>
获取当前进程的进程号
pid_t getpid(void);
获取当前进程的父进程的进程号
pid_t getppid(void);
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main(int argc, char const *argv[])
{
pid_t pid = fork();
if(pid == -1)
{
perror("fork error");
return -1;
}
else if(pid > 0) //父进程
{
printf("父进程:pid = %d, ppid = %d\n", getpid(), getppid());
printf("父进程的子进程的进程号:%d\n", pid);
}
else //子进程
{
printf("子进程:pid = %d, ppid = %d\n", getpid(), getppid());
}
while(1){}
return 0;
}
3. exit()/_exit()
#include <stdlib.h>
void exit(int status);
#include <unistd.h>
void _exit(int status);
功能:
退出当前进程
参数:
status:
当前进程退出的状态值,
可以返回给父进程(父进程可以通过wait函数获取这个值)
一般不需要将这个值返回给父进程,所以一般设置为0表示成功
退出,非0表示错误退出
返回值:
无
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
void myfun()
{
printf("nihao beijing");
//return如果放在main函数中执行,可以结束当前进程,
//但是如果是在子函数中执行,只能退出当前函数
//return ;
//exit函数不管在哪执行,都会结束整个进程
//exit和_exit的区别是
//exit是库函数,会自动刷新缓冲区
//_exit是系统调用,不会刷新缓冲区
exit(0);
//_exit(0);
printf("hahahahahahaha\n");
}
int main(int argc, char const *argv[])
{
printf("hello world\n");
myfun();
printf("welcome to hqyj\n");
return 0;
}
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
void myexit()
{
printf("这是进程结束后执行的最后一个代码\n");
}
void myfun()
{
printf("nihao beijing");
//return如果放在main函数中执行,可以结束当前进程,
//但是如果是在子函数中执行,只能退出当前函数
//return ;
//exit函数不管在哪执行,都会结束整个进程
//exit和_exit的区别是
//exit是库函数,会自动刷新缓冲区
//_exit是系统调用,不会刷新缓冲区
exit(0);
//_exit(0);
printf("hahahahahahaha\n");
}
int main(int argc, char const *argv[])
{
//当进程结束之后,还是可以执行代码的,
//所执行的代码是atexit的回调函数
atexit(myexit);
printf("hello world\n");
myfun();
printf("welcome to hqyj\n");
return 0;
}
4. wait()/waitpid()
#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(int *wstatus);
pid_t waitpid(pid_t pid, int *wstatus, int options);
功能:
阻塞等待子进程的状态(子进程退出状态)改变
参数:
pid:指定要等待的子进程
<-1 只阻塞等待进程组id等于这个值的绝对值的组中任意一个子进程
-1 阻塞等待所有子进程的退出状态
0 阻塞等待进程组id等于当前进程的进程号的组中任意一个子进程
>0 阻塞等待子进程的进程号等于这个值的子进程
wstatus:
保存子进程状态改变值
options:
选项
0 阻塞
WNOHANG 非阻塞
返回值:
成功:
退出的子进程的进程号
失败:
-1
wait(NULL) <==> waitpid(-1, NULL, 0);
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
#include <stdlib.h>
int main(int argc, char const *argv[])
{
pid_t pid;
if((pid = fork()) == -1)
{
perror("fork error");
exit(1);
}
else if(pid > 0) //父进程
{
int ret;
printf("父进程正在执行...\n");
//父进程调用wait函数阻塞等待子进程的退出状态
//wait(NULL);
waitpid(-1, NULL, 0);
printf("父进程也执行完毕了\n");
exit(0);
}
else //子进程
{
int i;
printf("子进程正在执行...\n");
for(i = 1; i <= 5; i++)
{
printf("hello world\n");
sleep(1);
}
exit(0);
}
return 0;
}