进程控制-

写时拷贝

对于父子进程来说,代码是共享的,数据是各自私有的。(其中代码是共享的,是整个进程中的代码都是共享的(并不是fork之后才共享),但是有的时候由于if /if else/else 进行了分流,导致父子进程所执行了不同的代码块,但是其实在子进程中依旧是拥有父进程那一块的代码的,但不执行
程序=代码(逻辑)+ 数据,代码存在内存中的代码段,被设置为了只读属性,父子进程都各自私有一份代码,OS内存空间的浪费(因为不能修改,看到的都是一样的内容,没有必要私有)
数据私有(其实也是相对的,当父子进程的数据都没有发生修改的时候,数据是共有的)
修改子进程的数据,父进程相对于的数据也发生了修改,这种情况是不允许的。违反进程之间独立性。
在没有修改前数据段被设置为只读状态,父子进程谁先写入,就会报错,OS就会去处理错误,OS以写时拷贝的方式向需要修改的那个数据段写入数据,目的:节省空间、时间。
写时拷贝意义:数据是很多的,不是所有的数据都立马要使用,且不是所有的数据都需要进行拷贝。但是如果立马要独立,就需要将数据全部拷贝,把本来可以在后面拷贝的,甚至不需要拷贝的数据,都拷贝了
写时拷贝的本质:修改页表和物理地址之间的映射关系,
数据10M,有1M要发生改变,写时拷贝时,拷贝1M。

终止

终止:释放进程创建时建的那套数据结构+代码、数据
main函数退出时,return的数字:进程的退出码,而0在函数设计中,一般代表的是正确,非零则是错误的(当退出码是1,2,3,4等的时候,每一个退出码的数字则代表了一种错误的情况)
exit:任何地方调用,终止整个进程;exit(C的库函数)刷新缓冲区
eixt(参数):参数=退出码
return:终止函数(只有在main函数中代表进程终止,在函数里面代表函数返回),不会杀死整个进程。
_exit(系统调用)不把缓冲区数据刷新出来,进去之后直接杀死进程
父子进程中,通常让子进程先退出,父进程很容易对子进程进行管理(垃圾回收),子进程被创建出来的目的是去处理业务,需要父进程帮我们拿到子进程执行的结果。
进程退出:释放PCB、释放内存空间,僵尸状态:PCB被保留

int main(){
	pid_t id = fork();
	if (id == 0){
		while (1){
			sleep(1);
			cout << "child.. ." << endl;
			break;
		}exit(0);
	}
	else{
		while (1){
			sleep(1);
			cout << "father ...." << endl; break;
		}
	}
	cout << " hello world" << endl;
}

结果:father … . .
hello world

等待wait

进程等待:父进程通过wait等系统调用,等待子进程状态的一种现象,这个等待是必须的
目的:1.防止子进程发生僵尸问题,产生内存泄漏;2.读取子进程状态。
父进程没有回收子进程的情况,子进程一直在资源浪费,子进程已经死(Z状态)就不要占着资源了:

   #include<iostream>
   #include<sys/types.h>
   #include<unistd.h>
   #include<stdlib.h>
  using namespace std;
      int main()
  {    pid_t id = fork();
    if(id < 0){
      cerr<< "error"<<endl;
    }
    else if(id == 0){
      int count = 0;
      while(1){
        sleep(1);
        cout<< "child... :"<<count<<endl;
        count++;
        if(count >= 3){
          break;
        }
      }
      exit(0);
    }
    else{
      while(1){
        sleep(1);
        cout<<"father..."<<endl;
      }
    }
    cout << "hello world"<<endl;                                                          
  }
[s@VM-8-3-centos lesson_12]$ ./myproc
father. . .
child.. . :0
father. . .
child. . . :1
father. . .
child. . .:2
father...
father. . .
father. ..
father. ..

父进程等待子进程,并回收:

int main()
{
	pid_t id = fork();
if (id == 0){
	int count = 0; 
	while (1){
		sleep(1);
	cout <<"child...: " << count << endl; 
	if (count >= 15){
		break;
	}
	count++;
}
exit(0);
}
else{
	int count = 0;
	while (1){
		sleep(1);
		cout << "father	...: " << count << endl;
		if (count == 20){
			wait(NULL);
//阻塞方式等待,0:阻塞方式:不给结果则不返回,一直监视着等待,中间没有间隔			
		}
		count++;
	}
}

释放子进程状态

while :; do ps axj | head -1 && ps axj | grep myproc|grep -v grep; sleep 1;echo"###" ; done
##########
PPID PID PGID  SID TTY   TPGID STAT UID TIME COMMAND
2298 4353 4353 2298 pts/0 4353 S+  1005 0:00 ./ myproc
4353 4354 4353 2298 pts/0 4353 z+ 1005 0:00 [myproc]
<defunct>
##########
PPID PID PGID  SID TTY TPGID STAT  UID TIME COMMAND
2298 4353 4353 2298 pts/0 4353 S+ 1005 0:00 ./ myproc

程序替换

程序替换不换外面的壳子,只换里面的,不创建新进程。
fork创建子进程目的:1.代码共享:子进程执行父进程代码的一部分(富二代子承父业);2.程序替换:子进程执行和父进程完全不同的事情(富二代创业)
进程程序替换:
把磁盘上的数据和代码覆盖式的以写时拷贝的方式写入物理内存,相当于直接把进程全部替换掉了。卡住:hang住,挂了=宕机。

void myfun( char *arg1,char *arg2,char *arg3);
myfun(a1,a2,a3)//list形式
void myfun(char *arg[]); //no list形式
char *arg[] = {al,a2,a3};
myfun(arg);

exec

exec系列函数能调用系统程序,能调用自己的程序。
一般exel系列函数,不会自己调用,一般是fork,让子进程调用,父进程只需要wait就行,一旦命令本身有问题,就不会波及父进程。
she1l 运行原理:王婆->委派实习生->处理介绍事宜->王婆只要关心结果就行->搞砸了,不会影响王婆
一旦替换成功,接下来进程就开始执行mycmd的代码了,后面的代码,早已经被替换。
用myexe执行(调用)mycmd:

.PHONY:all
all : myexe mycmd
myexe : myexe.c
	gcc - o $@ $^
mycmd:mycmd.c
	gcc - o $@ $^
.PHONY:clean
clean :
	rm - f myexe mycmd    

myexe.c:

#include<stdio.h>
#include<unistd.h>
int main(int argc, char* argv[], char* env[])
{
	printf("begin\n");//会被打印出来
	execl("./mycmd", "./mycmd", NULL);
	printf("running\n");//不会被打印出来
	return 0;
}

mycmd.c:

 #include<stdio.h>
 int main()
	  {	  int i = 0;
	   int sum = 0;
	   for (; i <= 100; i++){
		     sum += i;
		}
	   printf("result[1~100] : %d\n", sum);
	   return 0; }
[s@VM-8-3-centos ]$ make
gcc -o myexe myexe.c
gcc -o mycmd mycmd.c
[s@VM-8-3-centos ]
Makefile mycmd mycmd.c myexe myexe.c
[s@VM-8-3-centos ]$ ./myexe
begin 
result[1-100]:5050
[s@VM-8-3-centos ]$ ./mycmd
result[1-100]: 5050

制作简易shell

 #include<stdio.h>                                                                                                                                                                     
 #include<string.h>
 #include<stdlib.h>
 #include<unistd.h>
 #define SIZE 256
 #define NUM 16
int main()
 {
   char cmd[SIZE];
   const char* cmd_line = "[temp@VM-8-3-centos lesson_15]# ";
   while(1){
     cmd[0] = 0; //字符串以'\0'结束,把第一个位置设置为'\0',后面的字符都是无效
     printf("%s",cmd_line);
     fgets(cmd,SIZE,stdin);
     cmd[strlen(cmd)-1] = '\0';
     char *args[NUM];
     args[0] = strtok(cmd," ");
     int i = 1;
     do{
         args[i] = strtok(NULL," ");
         if(args[i] == NULL){
           break;
         }
         ++i;
     }while(1);
       //创建子进程
     pid_t id = fork();
     if(id < 0){
       perror("fork error\n");
       continue;
     }
      if(id == 0){
       //child
       execvp(args[0],args);
       //子进程替换失败,退出
       exit(1);
     }
     
     //parent
     int status = 0;
     pid_t ret = waitpid(id,&status,0);
     if(ret > 0 ){
       printf("status exit code : %d\n",(status>>8)&0xff);
     }
   }  
   return 0;
 }                                                       
[s@vM-8-3-centos ]$ ls
Makefile myshell.c
[s@VM-8-3-centos ]$ make
gcc -o myshell myshell.c
[s@vM-8-3-centos ]$ ls
Makefile myshell myshell.c
[s@VM-8-3-centos ]$ ./myshell
[tempavM-8-3-centos ]# ls -a -l
total 28
drwxrwxr-x2 s s 4096 Mar 2218;52
drwx---- -- 26 s s 4096 Mar 2216:55 .
--rw-rw-r--1 s s 67 Mar 22 16:58 Makefile
-rwxrwxr-x1 s s 8816 Mar 22 18:52 myshell
- rw-rw-r- -l s s 1722 Mar 22 18:51 myshell.c
status exit code : 0
[ tempaVM-8-3-centos lesson_15]#ps -a
PID TTY        TIME CMD
12887 pts/9ee ;00;00 myshell
12933 pts/9ee ;00 ;00 ps
status exit code : 0

strlen(cmd)-1:fgets在获取字符串的时候,最后将回车敲下之后补了’\0’结束,字符串的最后是以n\0结束的,\n换行 改为\0字符串结束,
strtok() :命令行拆解为多个字符串,然后把每个字符串放在一个对应的字符指针数组里面,访这个数组的下标可以拿到对应的命令行内容
char *fgets(char *s,int size,FILE * stream):
获得字符串,放到缓冲区;缓冲区大小;从哪种流方式获得
continue:跳过剩下的语句,而直接回到最初重新进行程序的判断;break是直接跳出循环体,不再执行

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值