子进程的创建

      1.fork()函数     include<unistd.h>
     一个现有进程可以调用fork函数穿件一个新进程,子进程返回0,父进程返回子进程ID,出错返回-1
     子进程是父进程的副本,子进程获得父进程的数据空间、堆和栈的副本,父子进程不共享这些空间。父子进程共享正文段。对于子进程,现在很多实现并不执行父进程的数据段。栈和堆的完全复制,而是使用写时拷贝技术,这些区域由父子进程共享,而且内核将他们的访问权限变为只读。若父子进程中任意个试图修改这些区域,则内核只为修改区域的那块内存制作一个副本。
例1:

直接输出


将fork2输出内容重定位到文件中


   fork的一个特性是父进程的所有打开文件描述符都被复制到子进程中。父进程每个相同的打开描述符共享一个文件表项

例2



 创建子进程后,多了一个进程(多了一套PCB),调度器选择一个进程执行,具体谁先运行,只有调度器知道,但是我们希望子进程先结束(僵尸进程),若父进程先结束则会导致孤儿进程
     父进程和子进程有一块共享的代码区(fork完后不做任何事情),数据不共享(写实拷贝技术-->任意进程试图)
*物理地址到虚拟地址--》页表和MMU,MMU->硬件,系统只有一个,
     
     父子进程之间区别:1.fork返回值不同(父进程返回子进程pid子进程返回1)
                                   2.进程ID不同
                                   3.具有不同的父进程ID
                                   4.子进程的tms_utime  tms_stime  tms_cuitime  tms_ustime均被置为0
                                   5.子进程设置的文件锁不会被子进程继承
                                   6.子进程未处理闹钟被清除
                                   7.子进程的未处理信号集被设置为空集
     fork用法:1.一个进程需要复制自己,使父子进程同时执行不同的代码(父进程等待客户端请求,生成子进程  
                         来处理请求)
                    2.一个进程要执行一个不同的程序(子进程从fork返回后调用exec函数)
     fork调用失败原因:
                    1.系统中太多进程
                    2.实际用户进程数超过限制。
     2.文件描述符文件指针
     在上面例1中,我们提到了文件描述符。fork的父进程的所有打开文件描述符都被复制到子进程中。父子进程的每个相同的打开描述符共享一个文件表项。
     文件描述符是一个整形,文件指针是一个指针。
     进程在创建时默认打开三个文件:标准输入。标准输出和标准错误(stdin,stdout,stderr),标准输入设备一般是键盘,标准输出设备一般是键盘显示器。
     文件描述符返回的是整形,这个整数是个小整数,其他文件是从3开始(标准输入0,标准输出1,标准错误2,是系统默认打开的,库函数不能干涉),且按顺序排序,若关掉0和2,则其他文件从0开始,但是没有1(因为1被占用)且和打开文件顺序有关,只要打开文件,就会有文件描述符,打开错误返回-1
     文件描述符表其实就是一个数组,文件描述符就是数组下标。
     在以前打开文件时我们这么写 : FILE *pf=fopen('./log','r');
     查看fopen:  

 fopen是C标准库的库函数。fp即为文件指针。
      open是系统调用,open返回int,打印成功返回文件描述符(整数),返回失败返回-1

open的模式字段有:O_RDONLY(open_readonly只读)、O_WRONLY、O_RDWR....当文件不存在可以使用O_CREAT创建
    *库函数和系统调用:系统调用是操作系统提供的接口,C库是基于系统调用接口做的二次开发,时操作更简便
    
     printf和fwrite

write和fwrite

例3 open和fopen

gcc编译后运行可执行文件,当前目录下会产生一个log.txt文件


由于上面程序中没有修改权限导致这个文件权限为S,当在open函数参数后面加上“0644”权限被改为:


例4 write和fwrite

int main()
{
char *p="hello word";
write(1,p.strlen(p));//打印到标准输出

char*msg="hello"
fwrite(msg,1,strlen(msg),std);//标准输出设备
}
例5 关于缓冲区


将本来输出到屏幕输出到文件中


printf,fwrite会打印两次。 只要用库函数,都有缓冲区,在标准输出上直接被刷出来,所以若重定位到文件中,全缓冲,遇到\n不会被甩出来,printf调用完毕,输出内容被保存到父进程的缓冲区,由于父子进程不共享数据区,若有谁想修改数据区,系统不允许修改,fork参照父进程来创建,子进程缓冲区的数据和父进程相同,当父子进程退出,会强制刷一份缓冲区内容。,标准输出是行缓冲,\n会将数据刷出来。而系统调用不带缓冲区,所以直接输出到标准输出。
printf默认缓冲方式为行缓冲,但是当printf往显示器或标准输出上打印为航缓冲,若重定向到文件时,它的默认刷新方式变为全缓冲(初始时不刷新,直到满了再刷新)


*三种缓冲方式:无缓冲、行缓冲、全缓冲
1.fwrite等不是系统调用
2.系统调用和文件指针
3.缓冲方式

     3.vfork
     vfork用于创建一个新进程,该新进程的目的进程是exec一个新程序,vfork和fork都创建一个子进程,但它不将父进程的地址空间复制到子进程中,因为子进程会立即调用exec。在子进程调用exec和exit之前,它在父进程空间中运行,会更改父进程数据段。堆和栈。且vfork保证子进程先运行,在调用exec或exit之后父进程才可以被调度运行。
 1.父子进程数据段和代码段共享
 2.子进程先运行

int g_value;
int main
{
pid_ id =vfork();
if(id<0)
{
          printf("fork error,%s\n",streror(error));
}
else if(id==0)
{
g_value=200;
printf("child:%d",pid)//子进程修改数据后,父进程数据被修改(共享数据段)
sleep(5);
exit();//先运行子进程,打印后休眠5秒退出后运行父进程//若改为return 0会每次打印多了两条消息
}
else
{
         printf("father")
}
}

线程-->当前进程的线程会在当前进程的地址空间运行,vfork和线程相当有关



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值