Linux_多进程

1.多进程

1.1进程的概念

进程的概念:程序的一次执行过程就是一个进程。进程就是一个正在执行的任务。

进程是一个动态的过程,在执行程序的时候会创建出来进程,当程序执行结束的时

候进程被操作系统销毁。进程是分配资源的最小的单位。每一个进程用于自己的

0-3G的用户空间,这0-3G空间被划分为堆区,栈区,静态区。进程在内核空间通

过一个task_struct结构体表述(进程控制块PCB)
在这里插入图片描述
在这里插入图片描述

1.2进程和程序的区别

进程:进程是动态的,进程是程序的一次执行过程,它是有生命周期的,

 程序会分配自己的内存空间0-3G,程序是在内存上存储的。

程序:程序是静态的,没有所谓的生命周期,它是一个在磁盘存储的二进制文件。

1.3进程的组成

进程是有三个部分组成的:进程控制块,数据段,程序段

进程控制块(pcb):进程控制块就是内核空间分配的task_struct结构体,这个结构体记录进程的所有的信息。

struct task_struct{
    //进程的状态
    //进程的标识符号(PID)识别进程的唯一的标号
    //进程执行的时候的一些寄存器
    ...
};

数据段:数据段就是程序执行时候产生的一些数据存放的位置,例如.data .bss

程序段:在进程中存放可执行程序的文本(.text)

1.4进程的种类

进程的种类有三种:交互进程,批处理进程,守护进程

交互进程:交互进程它是有shell控制的,可以直接和用户进程交互,例如文本编辑器

批处理进程:批处理进程维护了一个队列,被放到队列中的进程,统一被处理,优先级

		低。例如使用gcc编译程序的过程

守护进程:守护进程脱离某个终端,随着系统启动而运行,随着系统的终止而终止。

		例如操作系统中的服务

1.5进程PID概念

进程的PID:进程的pid是进程的唯一的标识符号,它是一个大于等0的整数值。并且进程的

    pid的值是不可能重复的。在一个Linux系统中能够创建的进程数可以通过如下命

	令查看cat /proc/sys/kernel/pid\_max

===>131072。在Linux操作系统的/proc

	目录下看到的以数字命名的目录其实都是一个进程

1.6特殊的进程(pid:0,1,2)

0号进程(idel):它是Linux操作系统启动的第一个进程,这个进程叫做空闲进程,

			当没有其他进程需要执行的时候默认执行idel。

1号进程(init):1号进程是在0号进程中通过kernel_thread创建出来的第一个进程,

			这个进程会完成硬件的一些比较初始化工作,之后是其他进程的父

			进程,可以完成一些进程的收尸工作

2号进程(kthreadd):这个进程也是由0号进程调用kernel_thread创建出来的,它的工作

       是任务的调度

1.7进程相关的命令

1.ps命令(查看进程的命令)
linux@ubuntu:~$ ps -ef  (查看进程的关系)
UID         PID   PPID  C STIME TTY          TIME CMD
root          1      0  0 08:53 ?        00:00:03 /sbin/init auto noprompt
root          2      0  0 08:53 ?        00:00:00 [kthreadd]
root          3      2  0 08:53 ?        00:00:00 [rcu_gp]
root          4      2  0 08:53 ?        00:00:00 [rcu_par_gp]
root          6      2  0 08:53 ?        00:00:00 [kworker/0:0H-kb]
root          9      2  0 08:53 ?        00:00:00 [mm_percpu_wq]
root         10      2  0 08:53 ?        00:00:00 [ksoftirqd/0]
root         11      2  0 08:53 ?        00:00:05 [rcu_sched]
root         12      2  0 08:53 ?        00:00:00 [migration/0]
root         13      2  0 08:53 ?        00:00:00 [idle_inject/0]
root         14      2  0 08:53 ?        00:00:00 [cpuhp/0]
root         15      2  0 08:53 ?        00:00:00 [cpuhp/1]
root         16      2  0 08:53 ?        00:00:00 [idle_inject/1]
root         17      2  0 08:53 ?        00:00:00 [migration/1]
root         18      2  0 08:53 ?        00:00:00 [ksoftirqd/1]
//UID :用户的ID
//PID : 进程号
//PPID: 父进程号
//STIME: 进程开始运行的时间
//TTY :如果是?代表这个程序没有控制终端
//CMD :进程的名字

linux@ubuntu:~$ ps -ajx  (进程的id和进程的运行的状态)
  PPID    PID   PGID    SID TTY       TPGID STAT   UID   TIME COMMAND
     0      1      1      1 ?            -1 Ss       0   0:03 /sbin/init auto noprompt
     0      2      0      0 ?            -1 S        0   0:00 [kthreadd]
     2      3      0      0 ?            -1 I<       0   0:00 [rcu_gp]
     2      4      0      0 ?            -1 I<       0   0:00 [rcu_par_gp]
     2      6      0      0 ?            -1 I<       0   0:00 [kworker/0:0H-kb]
     2      9      0      0 ?            -1 I<       0   0:00 [mm_percpu_wq]
     2     10      0      0 ?            -1 S        0   0:00 [ksoftirqd/0]
     2     11      0      0 ?            -1 I        0   0:05 [rcu_sched]
     2     12      0      0 ?            -1 S        0   0:00 [migration/0]
     2     13      0      0 ?            -1 S        0   0:00 [idle_inject/0]
     2     14      0      0 ?            -1 S        0   0:00 [cpuhp/0]
//SID是会话 ,PGID组ID ,PPID是父进程的ID,PID当前进程的ID
//打开一个终端就开了一个会话
//一个会话中有一个前台进程组和多个后台进程组
//一个进程组内有可以有很多的进程
    
 //TGPID 如果为-1代表这是一个守护进程   
 //STAT:进程的状态
    
 2.kill给进程发信号   
     kill -l //可以查看到系统中所有的信号列表
      2) SIGINT    ctrl+c
     19) SIGSTOP   ctrl+z
     18) SIGCONT   让进程重新变为运行状态
     9) SIGKILL    杀死进程
     ...
     kill -信号号  pid  //给进程发信号 见下图
         
     killall a.out   //将所有a.out命名的进程杀死
3.pidof命令
     pidof 可执行程序的名字 //能查看到当前进程的pid
     pidof a.out
		8313
4.top动态查看运行的进程
     top或者htop
5.查看系统能创建最大进程数的命令
      cat /proc/sys/kernel/pid_max

在这里插入图片描述

1.8进程的状态

1.8.1进程的状态含义

1.进程的状态
D    不可中断的休眠态(不可被信号打断)
R    运行态
S    可中断的休眠态(可以被信号打断)
T    停止态
X    死亡态
Z    僵尸态(进程结束父进程没有为它收尸)

2.进程的附加状态
<    高优先级的进程
N    低优先级的进程
L    进程用到的内存被锁到了内存中
s    会话组长
l    进程内包含多线程
+    这是一个前台进程

1.8.2进程状态切换

在这里插入图片描述

1.8.3进程状态切换的实例

01process_state.c

#include <head.h>

int main(int argc,const char * argv[])
{
    while(1);
    return 0;
}

1.上述的程序运行后可以通过如下命令看到进程是一个正在前台运行的进程。
在这里插入图片描述

2.对着正在执行的进程终端输入ctrl+z(发送了一个停止的信号 kill -19 pid)
在这里插入图片描述

3.停止状态的进程可以通过发信号让他继续运行(后台运行)(./a.out &直接后台运行)

kill -18 pid  **//进程就从停止状态变为运行状态了(后台)**

或

bg 作业号     **//进程就从停止状态变为运行状态了(后台)**

在这里插入图片描述

注:通过jobs -l可以查看作业号
在这里插入图片描述

1.8.4.后台运行的进程变为前台运行的进程

fg 作业号   **//可以让后台运行的进程变为前台运行的进程**

在这里插入图片描述

02process_state.c

#include <head.h>
#include <unistd.h>
int main(int argc,const char * argv[])
{
    while(1){
        sleep(1);
    }
    return 0;
}

执行上述的程序就可以看到进程处于前台休眠
在这里插入图片描述

1.9进程的创建

进程的创建过程是通过拷贝父进程得到的,进程在内核空间是通过task_struct结构体表述的,

新进程的创建直接拷贝父进程,只需要将很少部分数据修改即可,保留了父进程的遗传信息。

所以这个拷贝的过程操作系统通过fork函数来完成。

1.9.1进程创建函数详解

#include <sys/types.h>
#include <unistd.h>
pid_t fork(void);
功能:拷贝父进程得到子进程(创建子进程)
参数:
    @无
返回值:成功父进程收到子进程的PID,子进程收到0.
    	如果失败父进程收到-1,并置位错误码,子进程没有被创建出来
 /*
On success, the PID of the child process is returned in the parent, and 0 is returned  in
the  child.   On  failure, -1 is returned in the parent, no child process is created, and
errno is set appropriately.
 */

1.9.2使用fork创建进程(不关注返回值)

#include <head.h>

int main(int argc,const char * argv[])
{

    sleep(7); //7秒钟前只有一个进程

    fork(); //创建子进程

    while(1);
    return 0;
}

在前7秒钟执行a.out。a.out进程号是5126是拷贝终端进程得到的,当执行到fork函数的时候拷贝5126进程得到了5168进程。此时相当于a.out就有两个进程,这个两个进程都是在执行while(1)语句。
在这里插入图片描述

#include <head.h>

int main(int argc,const char * argv[])
{
    fork(); //创建子进程
    fork(); //创建子进程
    fork(); //创建子进程
    while(1);
    return 0;
}

上述代码共创建了2^3 = 8个进程
在这里插入图片描述

1.9.3使用fork创建进程(关注返回值)

#include <head.h>

int main(int argc,const char * argv[])
{
    pid_t pid;

    pid = fork();
    if(pid < 0){
        PRINT_ERR("fork error");
    }else if(pid == 0){
        printf("这是子进程\n");  //这里是子进程执行
    }else{ 
        printf("这是父进程\n");  //这里是父进程执行
    }

    printf("helloworld\n"); //父子进程都执行

    return 0;
}

1.9.4fork出来的进程和源进程执行的先后顺序

子进程和父进程执行没有先后顺序,时间片轮询,上下文切换的。

#include <head.h>

int main(int argc, const char *argv[])
{
    pid_t pid;

    pid = fork();
    if (pid < 0)
    {
        PRINT_ERR("fork error");
    }
    else if (pid == 0)
    {
        while (1)
        {
            sleep(1);
            printf("这是子进程\n");
        }
    }
    else
    {
        while (1)
        {
            sleep(1);
            printf("这是父进程\n");
        }
    }

    printf("helloworld\n");

    return 0;
}

1.9.5fork出来的进程内存问题(写时拷贝)

在这里插入图片描述

#include <head.h>
int a=10;
int main(int argc,const char * argv[])
{
    pid_t pid;

    pid = fork();
    if(pid < 0){
        PRINT_ERR("fork error");
    }else if(pid == 0){
        printf("这是子进程\n");
        printf("a = %d,&a =%p\n",a,&a);
        a=20;
        printf("a = %d,&a =%p\n",a,&a);
        printf("************************\n");
    }else{
        printf("这是父进程\n");
        printf("a = %d,&a =%p\n",a,&a);
        a=50;
        printf("a = %d,&a =%p\n",a,&a);
        printf("############################\n");
    }

    return 0;
}

1.9.6父子进程进程号获取

#include <sys/types.h>
#include <unistd.h>

pid_t getpid(void);
功能:获取当前进程的pid
参数:
    无
返回值:成功返回当前进程的pid,不会失败
pid_t getppid(void);
功能:获取父亲进程的pid
参数:
    无
返回值:成功返回当前进程的ppid,不会失败
#include <head.h>
int a=10;
int main(int argc,const char * argv[])
{
    pid_t pid;

    pid = fork();
    if(pid < 0){
        PRINT_ERR("fork error");
    }else if(pid == 0){
        printf("这是子进程pid = %d,ppid=%d\n",
        getpid(),getppid());
        
    }else{
        printf("这是父进程pid=%d,child=%d,ppid=%d\n",
        getpid(),pid,getppid());
     
    }
    return 0;
}

在这里插入图片描述

1.10进程的退出(exit/_exit)

在main函数中进程可以通过return函数退出进程。exit/_exit是专门用来退出进程的,

exit(库函数)退出进程的时候会刷新缓冲区,_exit(系统调用)退出的时候不会刷新缓冲区

#include <stdlib.h>
void exit(int status);
功能:exit(库函数)退出进程的时候会刷新缓冲区
参数:
    @status:进程退出的状态
         EXIT_SUCCESS   0
 		 EXIT_FAILURE   1
返回值:无
void _exit(int status);  
功能:_exit(系统调用)退出的时候不会刷新缓冲区
参数:
    @status:进程退出的状态
         EXIT_SUCCESS   0
 		 EXIT_FAILURE   1
返回值:无

#include <head.h>
#include <stdlib.h>

int main(int argc, const char *argv[])
{
    pid_t pid;

    pid = fork();
    if (pid < 0)
    {
        PRINT_ERR("fork error");
    }
    else if (pid == 0)
    {
        printf("111111111111");
        sleep(2);
        printf("55555555555\n"); //换行符号会刷新标准输出的缓冲区
        _exit(EXIT_SUCCESS);  //没有刷新缓冲区
    }
    else
    {
        pid_t pid1;
        pid1 = fork();
        if (pid1 < 0)
        {
            PRINT_ERR("fork 1 error");
        }
        else if (pid1 == 0)
        {
            printf("22222222222222");
            exit(EXIT_SUCCESS); //会刷新缓冲区
        }
    }
    sleep(1);
    printf("333333333333333"); //当程序结束后也会刷新缓冲区

    return 0;
}

上述程序执行的结果是:

先打印2,然后打印3,然后打印1和5.

1.11进程的资源回收(wait/waitpid)

僵尸进程:

如果子进程结束了,父进程没有为它收尸,子进程就变成了僵尸进程了

孤儿进程:

如果一个进程的父进程死了,子进程变成了孤儿进程,被init进程收养,当孤儿进程结束后init进程为它收尸。

pid_t wait(int *wstatus);
功能:阻塞等待回收子进程的资源
参数:
    @wstatus:接收到_exit/exit退出时候的值
返回值:成功返回回收掉的pid,失败返回-1
    
pid_t waitpid(pid_t pid, int *wstatus, int options);
功能:可以阻塞,也可以不阻塞回收指定pid进程的资源
参数:
    @pid:
       < -1   回收绝对值为这个相同的组ID的进程
       -1     等待回收任意子进程的资源
       0      回收和当前进程相同组ID的进程
       > 0    等待回收pid这个进程的资源
	@wstatus:接收到_exit/exit退出时候的值
    @options:
			0:阻塞回收资源
            WNOHANG:非阻塞回收资源
返回值:成功返回回收掉的pid,失败返回-1
 wait(NULL)< === >waitpid(-1,NULL,0)
1.11.1如果不适用wait或waitpid会产生僵尸进程
#include <head.h>
#include <stdlib.h>

int a=10;
int main(int argc,const char * argv[])
{
    pid_t pid;

    pid = fork();
    if(pid < 0){
        PRINT_ERR("fork error");
    }else if(pid == 0){
        printf("这是子进程pid = %d,ppid=%d\n",
        getpid(),getppid());
        
    }else{
        printf("这是父进程pid=%d,child=%d,ppid=%d\n",
        getpid(),pid,getppid());
        while(1);
    }

    return 0;
}

当上述程序执行的时候,子进程变成了僵尸进程,

当使用ctrl+c杀死父进程的时候,子进程(变成孤儿进程)

被init进程收养,init进程为它收尸

1.11.2wait或waitpid回收资源
#include <head.h>
#include <stdlib.h>
#include <wait.h>
int a=10;
int main(int argc,const char * argv[])
{
    pid_t pid;

    pid = fork();
    if(pid < 0){
        PRINT_ERR("fork error");
    }else if(pid == 0){
        printf("这是子进程pid = %d,ppid=%d\n",
        getpid(),getppid());
        exit(EXIT_SUCCESS);
    }else{
        printf("这是父进程pid=%d,child=%d,ppid=%d\n",
        getpid(),pid,getppid());
       // wait(NULL);
       waitpid(pid,NULL,0);
        while(1);
    }

    return 0;
}

2.守护进程

2.1守护进程创建过程(原理)

守护进程(daemon):相当于一个服务,随着系统的启动而启动,随着系统的终止而终止,守护进程要脱离终端执行。守护进程创建的流程如下:

1.创建一个孤儿进程

2.重新设置孤儿进程的会话和组的id

3.修改守护进程操作的目录为根目录/

4.修改创建文件的掩码(给最大权限0666)

5.将标准输入,输出,出错重定义到fd中dup2

6.fd对应的文件就是日志文件
在这里插入图片描述

注:如果在一个终端上运行程序,当终端被关闭的时候,在这个终端上执行的所有程序都会退出。

2.2守护进程相关API

#include <sys/types.h>
#include <unistd.h>

pid_t setsid(void);
功能:创建一个会话(将当前的进程pid,设置为pgid,和sid)
参数:
   @无
返回值:成功返回会话id,失败返回-1置位错误码

int chdir(const char *path);
功能:切换目录
参数:
   @path:目录的字符串
返回值:成功返回0,失败返回-1置位错误码
       
#include <sys/types.h>
#include <sys/stat.h>
mode_t umask(mode_t mask);
功能:修改掩码
参数:
   @mask:掩码的值
返回值:总是成功

#include <unistd.h>
int getdtablesize(void);
功能:获取进程中能打开的最大的文件描述符的值(1024)
参数:
   @无
返回值:返回最大文件描述符的值,总会成功

2.3守护进程创建的实例

#include <head.h>

int main(int argc,const char * argv[])
{
    pid_t pid;

    pid = fork();
    if(pid < 0){
        PRINT_ERR("fork error");
    }else if(pid == 0){
        int fd;
        //1.创建孤儿进程
        printf("创建了一个孤儿进程\n");
        //2.重新设置id
        setsid();
        //3.修改日志文件存放的位置为根目录
        chdir("/");
        //4.修改掩码
        umask(0);

        //作用,关闭fork前打开的文件描述符,如果fork前没有调用
        //open函数,这一步不做也是可以的
        //getdtablesize()返回一个进程能打开的最大的文件描述符
        // for(int i=3;i< getdtablesize();i++){
        //     close(i);
        // }

        //5.打开一个文件作为日志文件
        if((fd = open("log.txt",O_RDWR|O_CREAT|O_APPEND,0664))==-1){
            PRINT_ERR("open log.txt error");
        }
        //6.重定向文件描述符(关闭原来的文件描述符)
        dup2(fd,0);
        dup2(fd,1);
        dup2(fd,2);

        //7.在循环中做服务的操作
        while(1){
            printf("i am daemon process.\n");
            sleep(1);
        }
    }else{
       
       printf("父进程退出\n");
       exit(EXIT_SUCCESS);
    }
    return 0;
}

3.替换进程中执行的程序

3.1system函数的使用

#include <stdlib.h>

int system(const char *command);
功能:在c程序中执行一条命令(fork创建一个进程,在进程中执行这条命令)
参数:
    @command:命令  "ls -l"
        
返回值:
       1.如果command为NULL,如果shell可用,则为非零值,如果没有shell可用,则为02.如果无法创建子进程,或者无法检索其子进程的状态,则返回值为-13.如果一个shell不能在子进程中执行,那么返回值就好像子进程通过调用_exit(2)终止,状态为1274.如果所有系统调用都成功,则返回值是用于执行命令的子shell的终止状态。
        (shell的终止状态是它执行的最后一个命令的终止状态。)
       5.在最后两种情况下,返回值是一个“等待状态”,
        可以使用waitpid(2)中描述的宏来检查它。
#include <head.h>

int main(int argc,const char * argv[])
{
    //system("ls -l");
    system("ifconfig");
    return 0;
}

3.2exec函数族的使用

int execl(const char *path, const char *arg, ...
               /* (char  *) NULL */);
功能:执行一个可执行程序
参数:
    @path:执行程序的路径及名字   /bin/ls
    @arg:执行的可执行程序的参数 以NULL结尾       "ls","-l",NULL
返回值:失败返回-1置位错误码
int execv(const char *path, char *const argv[]);
功能:执行一个可执行程序
参数:
    @path:执行程序的路径及名字   /bin/ls
    @argv:执行的可执行程序的参数 以NULL结尾 ,"ls","-l",NULL写在argv的指针数组中
返回值:失败返回-1置位错误码

        
int execlp(const char *file, const char *arg, ...
               /* (char  *) NULL */);
int execvp(const char *file, char *const argv[]);
功能:在执行程序的之后,将可执行执行程序的路径传递过去
      在PATH中的可执行程序可以直接执行
参数:
    @file:可执行程序的名字
	@arg:执行的可执行程序的参数 以NULL结尾       "ls","-l",NULL
    @argv:执行的可执行程序的参数 以NULL结尾 ,"ls","-l",NULL写在argv的指针数组中
返回值:失败返回-1置位错误码
        
int execle(const char *path, const char *arg, ...
               /*, (char *) NULL, char * const envp[] */);
int execvpe(const char *file, char *const argv[],
               char *const envp[]);
功能:在执行程序的之后,向可执行程序中传递环境变量
参数:
  	@path:可执行程序的路径和名字
    @arg:参数列表
    @file:可程序的名字
    @argv:可执行程序的参数
    @envp:向程序中传递的环境变量
返回值:失败返回-1置位错误码

3.2.1execl/execv的使用

#include <head.h>

int main(int argc,const char * argv[])
{
    pid_t pid;

    pid=fork();
    if(pid < 0){
        PRINT_ERR("fork error");
    }else if(pid == 0){
        if(execl("/bin/ls","ls","-l",NULL)==-1)
            PRINT_ERR("execl error");
        //以下的语句不会执行了,因为ls的可执行程序的文件替换了a.out的课执行程序
        //的文件
        printf("abcd1234567\n");
        exit(EXIT_SUCCESS);
        printf("12345678\n");
    }else{
        wait(NULL);
    }

    return 0;
}
#include <head.h>

char *const arggv[] = {"ls","-l",NULL};
int main(int argc,const char * argv[])
{
    pid_t pid;

    pid=fork();
    if(pid < 0){
        PRINT_ERR("fork error");
    }else if(pid == 0){
        
        if(execv("/bin/ls",arggv)==-1)
            PRINT_ERR("execl error");
        //以下的语句不会执行了,因为ls的可执行程序的文件替换了a.out的课执行程序
        //的文件
        printf("abcd1234567\n");
        exit(EXIT_SUCCESS);
        printf("12345678\n");
    }else{
        wait(NULL);
    }


    return 0;
}

3.2.2execlp/execvp的使用(传递PATH环境变量)

#include <head.h>

int main(int argc,const char * argv[])
{
    pid_t pid;

    pid=fork();
    if(pid < 0){
        PRINT_ERR("fork error");
    }else if(pid == 0){
       // if(execlp("ls","ls","-l",NULL)==-1)
        	PRINT_ERR("execlp error");
         if(execlp("a.out","a.out",NULL)==-1)
             PRINT_ERR("execlp error");
         //需要在/etc/environment中添加a.out的路径
        //以下的语句不会执行了,因为ls的可执行程序的文件替换了a.out的课执行程序
        //的文件
        printf("abcd1234567\n");
        exit(EXIT_SUCCESS);
        printf("12345678\n");
    }else{
        wait(NULL);
    }


    return 0;
}
#include <head.h>

char *const arggv[] = {"a.out",NULL};
int main(int argc,const char * argv[])
{
    pid_t pid;

    pid=fork();
    if(pid < 0){
        PRINT_ERR("fork error");
    }else if(pid == 0){
        if(execvp("a.out",arggv)==-1)
            PRINT_ERR("execvp error");
        //以下的语句不会执行了,因为ls的可执行程序的文件替换了a.out的课执行程序
        //的文件
        printf("abcd1234567\n");
        exit(EXIT_SUCCESS);
        printf("12345678\n");
    }else{
        wait(NULL);
    }


    return 0;
}

3.2.2execle/execve的使用

myfile.sh

#!/bin/bash
echo $0
echo $1
echo $2
echo $3
echo ${VAR1}
echo ${ADDR}
echo ${HOME}
echo ${PATH}

传递自己的环境变量(execle)

#include <head.h>

char * const envp[] = {"VAR1=yiqinggankuaijieshu",
    "ADDR=beijingshihaidianqu",NULL};
int main(int argc,const char * argv[])
{
    pid_t pid;

    pid=fork();
    if(pid < 0){
        PRINT_ERR("fork error");
    }else if(pid == 0){
        if(execle("/home/linux/work/day5/myfile.sh","myfile.sh",
        "nihao","beijing","zhenbang",NULL,envp) ==-1)
            PRINT_ERR("execve error");
    }else{
        wait(NULL);
    }


    return 0;
}

传递自己的环境变量(execve)

#include <head.h>
char *const arggv[] = {"myfile.sh","nihao","beijing","zhenbang",NULL};
char * const envp[] = {"VAR1=yiqinggankuaijieshu",
    "ADDR=beijingshihaidianqu",NULL};
int main(int argc,const char * argv[])
{
    pid_t pid;

    pid=fork();
    if(pid < 0){
        PRINT_ERR("fork error");
    }else if(pid == 0){
        if(execve("/home/linux/work/day5/myfile.sh",arggv,envp) ==-1)
            PRINT_ERR("execve error");

    }else{
        wait(NULL);
    }

    return 0;
}

传递系统的环境变量(env查看到的所有内容)

#include <head.h>
char *const arggv[] = {"myfile.sh","nihao","beijing","zhenbang",NULL};

extern char **environ; //终端向环境变量传给了当前的进程及fork出来的进程,

int main(int argc,const char * argv[])
{
    pid_t pid;

    pid=fork();
    if(pid < 0){
        PRINT_ERR("fork error");
    }else if(pid == 0){
        // for(int i=0;environ[i]!=NULL;i++){
        //     printf("%s\n",environ[i]);
        // }
        #if 1   //使用execve(,,,environ)将当前程序的所有环境变量在传递给myfile.sh的脚本
        if(execve("/home/linux/work/day5/myfile.sh",arggv,environ) ==-1)
            PRINT_ERR("execve error");
        #endif
    }else{
        wait(NULL);
    }


    return 0;
}

将系统和自己写的环境变量全部传递过去

#include <head.h>
char *const arggv[] = {"myfile.sh","nihao","beijing","zhenbang",NULL};
char *  envp[100] = {"VAR1=yiqinggankuaijieshu",
    "ADDR=beijingshihaidianqu",NULL};

extern char **environ;

int main(int argc,const char * argv[])
{
    pid_t pid;

    pid=fork();
    if(pid < 0){
        PRINT_ERR("fork error");
    }else if(pid == 0){
        for(int i=0;environ[i]!=NULL;i++){
           //printf("%s\n",environ[i]);
            envp[i+2]=environ[i];
        }
        #if 1
        if(execve("/home/linux/work/day5/myfile.sh",arggv,envp) ==-1)
            PRINT_ERR("execve error");
        #endif
    }else{
        wait(NULL);
    }

    return 0;
}

作业:

1.使用父子进程拷贝同一个文件,父进程拷贝前半部分,子进程拷贝后半部分

2.使用三个进程拷贝同一个文件,每个进程拷贝文件的1/3.

3.使用父进程创建两个子进程(3个进程),在父进程中为两个子进程进程收尸

1.1多进程拷贝一个文件(2个进程)

#include <head.h>
#include <sys/wait.h>
int get_src_file_len(const char *srcfile,const char * destfile)
{
    int sfd,dfd;
    int len=0;
    if((sfd = open(srcfile,O_RDONLY))==-1){
        PRINT_ERR("open srcfile error");
    }
    if((dfd = open(destfile,O_RDWR|O_CREAT|O_TRUNC,0664))==-1){
        PRINT_ERR("open destfile error");
    }

    len = lseek(sfd,0,SEEK_END);

    close(sfd);
    close(dfd);

    return len;
}

int copy_file(const char *srcfile,const char * destfile,int start,int len)
{
    int sfd,dfd;
    char buf[10] = {0};
    int ret=0,count=0;
    if((sfd = open(srcfile,O_RDONLY))==-1){
        PRINT_ERR("open srcfile error");
    }
    if((dfd = open(destfile,O_RDWR))==-1){
        PRINT_ERR("open destfile error");
    }

    lseek(sfd,start,SEEK_SET);
    lseek(dfd,start,SEEK_SET);

    while(1){
        ret = read(sfd,buf,sizeof(buf));

        count+=ret;
        if(ret==0 || count>len) {
           write(dfd,buf,ret-(count-len)); 
           break;
        }  
        write(dfd,buf,ret); 
    }
    close(sfd);
    close(dfd);
    return 0;
}
int main(int argc,const char * argv[])
{
    pid_t pid;
    int len;
    if(argc != 3){
        fprintf(stderr,"input error,try again\n");
        fprintf(stderr,"usage:./a.out srcfile,destfile\n");
        return -1;
    }
    len = get_src_file_len(argv[1],argv[2]);
    printf("len = %d\n",len);
    pid = fork();
    if(pid < 0){
        PRINT_ERR("fork error");
    }else if(pid == 0){
        printf("child start = %d,len=%d\n",0,len/2);
        copy_file(argv[1],argv[2],0,len/2);
        exit(EXIT_SUCCESS);
    }else{
        printf("parent start = %d,len=%d\n",len/2,len-len/2);
        copy_file(argv[1],argv[2],len/2,(len-len/2));
        wait(NULL);
    }
    return 0;
}

1.2多进程拷贝文件(3个进程)

#include <head.h>
#include <sys/wait.h>

int get_src_file_len(const char *srcfile,const char * destfile)
{
    int sfd,dfd;
    int len=0;
    if((sfd = open(srcfile,O_RDONLY))==-1){
        PRINT_ERR("open srcfile error");
    }
    if((dfd = open(destfile,O_RDWR|O_CREAT|O_TRUNC,0664))==-1){
        PRINT_ERR("open destfile error");
    }

    len = lseek(sfd,0,SEEK_END);

    close(sfd);
    close(dfd);

    return len;
}

int copy_file(const char *srcfile,const char * destfile,int start,int len)
{
    int sfd,dfd;
    char buf[10] = {0};
    int ret=0,count=0;
    if((sfd = open(srcfile,O_RDONLY))==-1){
        PRINT_ERR("open srcfile error");
    }
    if((dfd = open(destfile,O_RDWR))==-1){
        PRINT_ERR("open destfile error");
    }

    lseek(sfd,start,SEEK_SET);
    lseek(dfd,start,SEEK_SET);

    while(1){
        ret = read(sfd,buf,sizeof(buf));

        count+=ret;
        if(ret==0 || count>len) {
           write(dfd,buf,ret-(count-len)); 
           break;
        }  
        write(dfd,buf,ret); 
    }
    close(sfd);
    close(dfd);
    return 0;
}
int main(int argc,const char * argv[])
{
    pid_t pid,pid1;
    int len;
    
    if(argc != 3){
        fprintf(stderr,"input error,try again\n");
        fprintf(stderr,"usage:./a.out srcfile,destfile\n");
        return -1;
    }
    len = get_src_file_len(argv[1],argv[2]);
    printf("len = %d\n",len);

    pid = fork();
    if(pid < 0){
        PRINT_ERR("fork error");
    }else if(pid == 0){
        printf("child1 start = %d,len=%d\n",0,len/3);
        copy_file(argv[1],argv[2],0,len/3);
        exit(EXIT_SUCCESS);
    }else{
        pid1=fork();
        if(pid1<0){
            PRINT_ERR("fork pid1 process error");
        }else if(pid1 == 0){
            printf("child2 start = %d,len=%d\n",len/3,(len/3)*2);
            copy_file(argv[1],argv[2],len/3,(len/3)*2);
            exit(EXIT_SUCCESS);
        }

        printf("parent start = %d,len=%d\n",(len/3)*2,len-(len/3)*2);
        copy_file(argv[1],argv[2],(len/3)*2,len-(len/3)*2);
        wait(NULL); //阻塞回收子进程,但是回收那个子进程的资源不确定,只要回收一个就会退出
        wait(NULL); //阻塞回收子进程,但是回收那个子进程的资源不确定,只要回收一个就会退出
    }
    return 0;
}

;
}
write(dfd,buf,ret);
}
close(sfd);
close(dfd);
return 0;
}
int main(int argc,const char * argv[])
{
pid_t pid,pid1;
int len;

if(argc != 3){
    fprintf(stderr,"input error,try again\n");
    fprintf(stderr,"usage:./a.out srcfile,destfile\n");
    return -1;
}
len = get_src_file_len(argv[1],argv[2]);
printf("len = %d\n",len);

pid = fork();
if(pid < 0){
    PRINT_ERR("fork error");
}else if(pid == 0){
    printf("child1 start = %d,len=%d\n",0,len/3);
    copy_file(argv[1],argv[2],0,len/3);
    exit(EXIT_SUCCESS);
}else{
    pid1=fork();
    if(pid1<0){
        PRINT_ERR("fork pid1 process error");
    }else if(pid1 == 0){
        printf("child2 start = %d,len=%d\n",len/3,(len/3)*2);
        copy_file(argv[1],argv[2],len/3,(len/3)*2);
        exit(EXIT_SUCCESS);
    }

    printf("parent start = %d,len=%d\n",(len/3)*2,len-(len/3)*2);
    copy_file(argv[1],argv[2],(len/3)*2,len-(len/3)*2);
    wait(NULL); //阻塞回收子进程,但是回收那个子进程的资源不确定,只要回收一个就会退出
    wait(NULL); //阻塞回收子进程,但是回收那个子进程的资源不确定,只要回收一个就会退出
}
return 0;

}

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值