父子进程的创建
父子进程的创建
父子进程是来回交替执行的
#include <stdio.h>
#include <stdlib.h>
#include<unistd.h>
int main()
{
#if 0 //
fork(); // 通过fork函数创建一个子进程
printf("hello world\n");
while (1) ;
#endif
pid_t pid;
pid = fork(); // 通过fork函数的返回值来区分父子进程的独立代码区
if(pid <0)
{
perror("fail to fork");
return -1;
}
else if(pid > 0) // 父进程的代码区
{
printf("parent: pid = %d, ppid = %d\n",getpid(), getppid());
printf("pid = %d\n", pid);
while (1) {
printf("this is a parent process\n");
sleep(1);
}
}
else // 子进程的代码区
{
printf("son: pid = %d, ppid = %d\n",getpid(), getppid());
while (1) {
printf("this is a son process\n");
sleep(2);
}
}
return 0;
}
centos man手册问题
centos man手册问题
参考参考
man 2 open
查询open手册
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
父子进程拥有独立的地址空间
父子进程拥有独立的地址空间
#include <stdio.h>
#include <stdlib.h>
#include<unistd.h>
int a = 666;
int main()
{
#if 0 //
fork(); // 通过fork函数创建一个子进程
printf("hello world\n");
while (1) ;
#endif
static int b = 777;
int c = 888;
// 子进程会复制父进程fork之前的所有内容
// 但是fork之后,父子进程完全独立,所以不管双方怎么改变(堆区、栈区、数据区等)都不会受到对方影响
pid_t pid;
pid = fork(); // 通过fork函数的返回值来区分父子进程的独立代码区
if(pid <0)
{
perror("fail to fork");
return -1;
}
else if(pid > 0) // 父进程的代码区
{
printf("this is a parent process\n");
a++;
b++;
c++;
printf("a = %d, b=%d,c=%d\n",a,b,c);
}
else // 子进程的代码区 这里是pid等于0
{
sleep(1);
printf("this is a son process\n");
printf("a = %d, b=%d,c=%d\n",a,b,c);
}
while (1) {
}
return 0;
}
执行结果
子进程继承父进程的空间
子进程继承父进程的空间
#include <stdio.h>
#include <stdlib.h>
#include<unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()
{
int fd;
if( (fd = open("file.txt", O_RDONLY)) == -1 )
{
perror("fail to open");
return -1;
}
// 子进程会继承父进程的一些公有区域,比如磁盘空间、内核空间
// 文件描述符的偏移量保存在内核空间中,所以父进程改变偏移量,则子进程获取的偏移量是改变之后的
pid_t pid;
pid = fork();
if(pid <0)
{
perror("fail to fork");
return -1;
}
else if(pid > 0) // 父进程的代码区
{
printf("this is a parent process\n");
char buf[32] = "";
if(read(fd, buf, 12) == -1)
{
perror("fail to read");
}
printf("buf = [%s]\n", buf);
}
else // 子进程的代码区 这里是pid等于0
{
sleep(1);
printf("this is a son process\n");
char buf[32] = "";
if(read(fd, buf, 12) == -1)
{
perror("fail to read");
}
printf("buf = [%s]\n", buf);
}
while (1) {
}
return 0;
}
进程的等待
进程的等待
wait
#include <stdio.h>
#include <stdlib.h>
#include<unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main()
{
pid_t pid;
pid = fork();
if(pid <0)
{
perror("fail to fork");
return -1;
}
else if(pid > 0) // 父进程的代码区
{
// 使用wait在父进程中阻塞等待子进程的退出
// 不接收子进程的退出状态
// wait(NULL);
// 接收子进程的退出状态
// 子进程中必须使用exit或者_exit函数退出进程时发送退出状态
int status =0;
wait(&status);
if(WIFEXITED(status) != 0) // 如果子进程是正常终止的,取出的字段值非零
{
// 返回子进程的退出状态
// 在使用此宏 WEXITSTATUS 前,应先使用宏WIFEXITED判断子进程是否正常退出,正常退出才可以使用此宏
printf("The son process return status: %d\n", WEXITSTATUS(status));
}
printf("this is a parent process\n");
}
else if(pid == 0) // 子进程的代码区
{
int i = 0;
for(i=0;i<5;i++)
{
printf("this is a son process\n");
sleep(1);
}
// 使用exit退出当前进程并设置退出状态
exit(2);
}
return 0;
}
进程的中止
进程的中止
exit是库函数,_exit是系统调用
exit会刷新缓冲区
进程的创建 vfork函数
进程的创建vfork函数
vfork保证子进程先运行,在它调用exec或exit之后,父进程才可能被调度运行。
vfork和fork一样都创建一个子进程,但它并不将父进程的地址空间完全复制到子进程中,因为子进程会立即调用exec(或exit)
在子进程中调用exec或exit之前,它在父进程的地址空间中运行,在exec之后子进程会有自己的进程空间
- 子进程在父进程之前执行
#include <stdio.h>
#include <stdlib.h>
#include<unistd.h>
#include <sys/types.h>
int main()
{
pid_t pid;
pid = vfork(); // 使用vfork函数创建完子进程后,子进程会先执行直到子进程执行exec或exit后,父进程才会执行
if(pid <0)
{
perror("fail to fork");
exit(1);
}
else if(pid > 0) // 父进程的代码区
{
while (1)
{
printf("this is a father process\n");
sleep(1);
}
}
else if(pid == 0) // 子进程的代码区
{
int i=0;
for(i=0;i<5;i++)
{
printf("this is a son process\n");
sleep(1);
}
exit(0);
}
return 0;
}
- 子进程和父进程共享同一块空间
#include <stdio.h>
#include <stdlib.h>
#include<unistd.h>
#include <sys/types.h>
int a = 10;
int main()
{
int b = 9;
pid_t pid;
pid = vfork(); // 使用vfork创建完子进程,在子进程执行exec或exit之前,父子进程共有同一块地址空间
if(pid <0)
{
perror("fail to fork");
exit(1);
}
else if(pid > 0) // 父进程的代码区
{
printf("this is a father process\n");
printf("in father a=%d,b=%d\n",a,b);
}
else if(pid == 0) // 子进程的代码区
{
a++;
b++;
printf("this is a son process\n");
printf("in son a=%d,b=%d\n",a,b);
exit(0);
}
return 0;
}
进程的替换
进程的替换
1、exec函数族取代调用进程的数据段、代码段和堆栈段
2、调用exec函数的进程并不创建新的进程,故调用exec前后,进程的进程号并不会改变,其执行的程序完全由新的程序替换,而新程序则从其main函数开始执行。
3、一个进程调用exec后,除了进程ID,进程还保留了这些特征不变:父进程号、进程组号、控制终端、根目录、当前工作目录、进程信号屏蔽集、未处理信号等等
六个exec函数中只有execve是真正意义上的系统调用(内核提供的接口),其他函数都是在此基础上经过封装的库函数
centos里可以通过whereis 查询命令的路径
#include <stdio.h>
#include <stdlib.h>
#include<unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main()
{
pid_t pid;
pid = fork();
if(pid <0)
{
perror("fail to fork");
exit(1);
}
else if(pid > 0) // 父进程的代码区
{
printf("this is a father process\n");
wait(NULL);
printf("The child process has quited\n");
}
else if(pid == 0) // 子进程的代码区
{
printf("this is a son process\n");
// 使用exec函数族调用shell命令
// 不带p的函数,命令的路径一定要用绝对路径
#if 0
if(execl("/bin/ls", "ls", "-l", NULL) == -1)
{
perror("fail to execl");
exit(1);
}
#endif
// 带p的函数,第一个参数既可以是相对路径,也可以是绝对路径
#if 0
if(execlp("ls", "ls", "-l", NULL) == -1)
{
perror("fail to execlp");
exit(1);
}
#endif
// 带v的函数需要使用指针数组来传递
#if 0
char *str[] = {"ls", "-l", NULL};
if(execv("/bin/ls", str) == -1)
{
perror("fail to execv");
exit(1);
}
#endif
// exec函数族调用可执行文件
#if 0
if(execlp("./hello", "./hello", NULL) == -1)
{
perror("fail to execlp");
exit(1);
}
#endif
// exec函数族调用可执行文件 不带p的函数 调用可执行文件 可以是相对路径
#if 0
if(execl("./hello", "./hello", NULL) == -1)
{
perror("fail to execl");
exit(1);
}
#endif
// exec函数族调用shell脚本
#if 1
if(execlp("./myshell.sh", "./myshell.sh", NULL) == -1)
{
perror("fail to execlp");
exit(1);
}
#endif
// exec函数族取代调用进程的数据段、代码段和堆栈段
// 所以当exec函数执行完毕后,当前进程就结束了,所以原本进程中的后续代码不会再执行
printf("hello world\n");
}
return 0;
}
关于信号
进程间通信