Linux系统编程-进程-进程控制

147 阅读4分钟

基本概念

进程和程序

程序是一个剧本,进程是演绎成戏

程序

程序:死的,只占用磁盘空间

进程

运行:活的,运行起来的程序,占用内存、CPU等系统资源

并发

单核的情况下,分时复用

单道程序设计

必须一个进程执行完

多道程序设计

有时钟中断控制并发

虚拟内存系统

进程 = 内核空间(PCB之类的) + 用户空间(数据段之类的)
虚地址 = 虚页号 + 页内地址
用户空间的相同虚拟地址映射到不同物理区域,虚页号不同。
内核空间的相同虚拟地址映射到相同物理区域,估计虚页号相同。因为人家叫内核空间,你又只有一个内核,当然共享一个区域。

这样做的好处是,你买个内存条也没有分块,但是你可以人为规定一下:

  • 相同区域可以相互访问,即进程之间可以通信
  • 使不同区域不能互相访问,即可以通信,但你不能随便访问我的数据,也可以依此实现权限控制。

fork函数

原理

image.png

相当于某个时间点出现了相同的平行时间线

  1. 使用fork之前只有father在运行
  2. fork之后相当于复制了一份完整的父进程
  3. 然后父进程执行fork之后的剩余部分。
  4. 子进程不执行fork之前的部分,也并发执行fork之后的剩余的部分。

fork

pid_t fork()

返回值:

  • 成功:在子进程里的返回值是0,在父进程里的返回值是子进程pid
  • 失败:-1

getpid()

获得自身的pid

getppid()

获得父进程的pid

示例

如果并发执行的过程中,父进程先关闭,那么子进程变成孤儿进程,其父进程号会有特殊值

创建一个子进程

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>


int main(){
//父进程1
    printf("before fork-1-\n");
    printf("before fork-2-\n");
    printf("before fork-3-\n");
    printf("before fork-4-\n");

//fork
    pid_t pid=fork();


    if(pid==-1){ //失败
            perror("fork error");
            exit(1);
    }else if(pid==0){ //如果是子进程
            printf("--child is created, pid=%d, parent-pid=%d\n",getpid(),getppid());
    }else if(pid>0){  //如果是父进程
            printf("---parent process: my child is %d, my pid:%d, my parent pid:%d\n",pid,getpid(),getppid());
    }
    printf("=====end of file\n");
    return 0;
}

循环创建子进程

image.png

//你子进程里也会继续执行未执行完的循环,你生,你的儿子也会生
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

int main(){
	int i;
	for(i=0;i<5;i++){
		pid_t pid=fork();
		if(pid==-1){
			perror("fork error");
			exit(1);
		}else if(pid==0){
			printf("--child is created, pid=%d, parent-pid=%d\n",getpid(),getppid());
		}else if(pid>0){
			printf("---parent process: my child is %d, my pid:%d, my parent pid:%d\n",pid,getpid(),getppid());
		}
	}
	printf("=====end of file\n");
}

指定创建N个子进程

在循环创建子进程的基础上,不允许子进程继续创建

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

int main(){
	int i;
	for(i=0;i<5;i++){
		pid_t pid=fork();
		if(pid==-1){
			perror("fork error");
			exit(1);
		}else if(pid==0){
			break;
		}
	}
	if(i==5){ //自然结束,是父亲进程
		sleep(i);  //睡眠,相当于阻塞,保证并发时依然最后结束
		printf("父进程结束");
	}else{
		sleep(i); //保证顺序结束
		printf("这是第%d个子进程\n",i);
	}
}

进程共享

刚fork时

父子进程不同:
进程id,fork返回值,各自的父进程,进程创建创建时间,闹钟,未决信号集合

fork之后

父子进程之间遵循 读时共享写时复制 的原则。
虽然说上面fork原理说的子进程完全拷贝一份父进程,但是你总不能干什么都把人家的用户空间和内核空间都复制一份吧,这样进程一多,内存就爆了。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

//父子进程不共享全局变量
//读时共享写时复制
int var =100;

int main(){
        pid_t pid;
        pid = fork();

        if(pid==-1){
                perror("fork error");
                exit(1);
        }else if(pid>0){
                //父进程,只进行了读,所以一直用的是共享的那一份
                sleep(1);
                printf("parent, var = %d\n", var); 
                printf("I'am parent pid = %d, getppid = %d\n",getpid(),getppid());
        }else if(pid==0){
                //子进程进行了写,所以拷贝了一份自己用
                var=200;
                printf("child, var=%d\n",var);
                printf("I'am child pid= %d, ppid = %d\n",getpid(),getppid());
        }
}