数据结构 进程控制编程

一、进程控制理论基础

1.进程的定义进程是一个具有一定独立功能的程序的一次运行活动,同时也是资源分配的最小单元。

2.进程与程序

进程(程序执行的实例) 程序(放到磁盘的可执行文件)
动态 静态
暂时(状态变化的过程) 长久(长久保存)
组成:包括程序、数据和进程控制块(即进程状态信息)  

                         通过多次执行,一个程序可对应多个进程;

                         通过调用关系,一个进程可包括多个程序。

3. 进程的生命周期

创建:   每个进程都是由其父进程创建,进程可以创建子进程,子进程又可以创建子进程的子进程

运行:   多个进程可以同时存在,进程间可以通信

撤销:    进程可以被撤销,从而结束一个进程的运行

(每个操作系统起来后都有1号进程 init)

4. 进程的状态

执行状态:进程正在占用CPU

就绪状态:进程已具备一切条件,正在等待分配CPU的处理时间片

等待状态:进程不能使用CPU,若等待事件发生则可将其唤醒

5.Linux进程:健壮性,并行性,互不干扰

6.Linux下进程地址空间:

“数据段”存放的是全局变量、常数以及动态数据分配的数据空间;

代码段”存放的是程序代码的数据。

堆栈段”存放的是子程序的返回地址、子程序的参数以及程序的局部变量等。

7.进程ID(PID):标识进程的唯一数字

  父进程ID(PPID)

8.进程互斥:当有若干进程都要使用某一共享资源时,任何时刻最多允许一个进程使用

   临界资源:一次只允许一个进程访问的资源

   临界区:访问临界资源的那段代码

   进程同步:一组并发进程按一定顺序执行的过程

   进程调度:按一定算法,从一组待运行的进程中选出一个来占有CPU运行

9. 死锁:多个进程因资源竞争而形成一种僵局,若无外力作用,这些进程将永远不能向前推进

二、进程的控制编程

1.获取ID:

pid_t getpid(void)      获取本进程ID 

pid_t getppid(void)        获取父进程ID

2. 进程的创建:

(1)pid_t fork(void)    创建子进程

       父进程返回子进程ID,子进程返回0

      子进程拷贝父进程的地址空间

(2)pid_t vfork(void)    创建子进程(与父进程共享地址空间)

       子进程先运行,子进程运行完,父进程才运行

       子进程指定退出方式 exit(1);

例:

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

int main()
{
    pid_t pid; //此时仅有一个进程
    int a = 0;
    pid = fork();  //创建子进程,此时有两个进程
    if(-1 == pid)  //失败返回-1
    {
        perror("fork");
        exit(1);
    }
    else if(0 == pid)  //子进程
    {
        a++;
        printf("This is ChildProcess pid %d ppid %d!\n", getpid(), getppid());
        printf("pid = %d\n", pid);
        printf("a = %d\n", a);
        printf("%p\n", a);
    }
    else  //父进程
    {
        a++;
        printf("This is ParentProcess %d!\n", getpid());
        printf("pid = %d\n", pid);
        printf("a = %d\n", a);
        printf("%p\n", a);
    }

    printf("Hello!\n"); 
    return 0;
}

运行结果:(1)父进程子进程的a均为1,a的地址相同,因为子进程复制父进程的地址空间,拷贝数据

                  (2)打印两次Hello!,因为父进程和子进程执行时都执行进程括号外面的代码 

----------------------------------------------------

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <fcntl.h>

void ReadFile(int fd)
{
    char buf[32] = {0};
    lseek(fd, 0, SEEK_SET);
    int ret = read(fd, buf, sizeof(buf));
    if(-1 == ret)
    {
        perror("read");
        exit(1);
    }
    printf("Child read from txt:%s\n", buf);
}

void WriteFile(int fd)
{
    char buf[32] = "HelloWorld";

    int ret = write(fd, buf, strlen(buf));
    if(-1 == ret)
    {
        perror("write");
        exit(1);
    }

}

int main()
{
    pid_t pid;
    int fd = open("tmp.txt", O_RDWR | O_CREAT | O_EXCL, S_IRWXU);
    if(-1 == pid)  //失败返回-1
    {
        perror("fork");
        exit(1);
    }
    pid = fork();  //创建子进程

    if(0 == pid)  //子进程
    {
        sleep(1);
        ReadFile(fd);
    }

    else  //父进程
    {
        WriteFile(fd);
        sleep(2);
    }

    return 0;
}

 子进程继承父进程打开的文件描述符 

-------------------------------------------------------------

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

int main()
{
    pid_t pid;
    int a = 0;
    pid = vfork();  //创建子进程
    if(-1 == pid)  //失败返回-1
    {
        perror("vfork");
        exit(1);
    }
    else if(0 == pid)  //子进程
    {
        a++;
        printf("This is ChildProcess pid %d ppid %d!\n", getpid(), getppid());
        printf("pid = %d\n", pid);
        printf("a = %d\n", a);
        printf("%p\n", &a);
        exit(1);  //子进程指定退出方式
    }
    else  //父进程
    {
        a++;
        printf("This is ParentProcess %d!\n", getpid());
        printf("pid = %d\n", pid);
        printf("a = %d\n", a);
        printf("%p\n", &a);
    }

    printf("Hello!\n"); 
    return 0;
}

运行结果 :(1)父进程a = 1, 子进程a = 2,a的地址相同,因为子进程共享父进程的地址空间

                   (2)打印两次Hello!,因为父进程和子进程执行时都执行进程括号外面的代码 

----------------------------------------------------- 

fork 与 vfork 的区别:

                           fork                          vfork

子进程拷贝父进程的数据

子进程与父进程共享数据

父、子进程的执行次序不确定

子进程先运行,父进程后运行

3. exec函数族

    exec启动一个新程序,替换原有的进程,因此进程的PID不会改变

(1)int execl(const char * path,const char * arg1, ...)

         execl("进程的绝对路径","进程名", 命令行参数,NULL)

 参数:path:被执行程序名(含完整路径)

            arg1 – argn: 被执行程序所需的命令行参数,含程序名。以空指针(NULL)结束。

(2)int execv (const char * path, char * const argv[ ])

 参数:path:被执行程序名(含完整路径)。

            argv[ ]: 被执行程序所需的命令行参数数组。

例:

被执行的程序:

#include <stdio.h>
int main(int argc, char *argv[])
{
    int i;
    printf("%d\n", getpid());
    for(i = 1; i < argc; i++)
    {
        printf("%s\n", argv[i]);
    }
    return 0;
}

用 execl 执行 :

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

int main()
{
    pid_t pid;
    pid = vfork();
    if(-1 == pid)
    {
        perror("vfork");
        exit(1);
    }
    else if(0 == pid)
    {
        execl("/mnt/hgfs/share/class/Linux/process/5test", "./5test", "Hello",
                "World", NULL);
    }
    else
    {
        printf("This is Parent Process %d\n", getpid());
    }
    return 0;
}

用 execv 执行:

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

int main()
{
    pid_t pid;
    char *argv[] = {"./test", "1234","Hello!", NULL};
    pid = vfork();
    if(-1 == pid)
    {
        perror("vfork");
        exit(1);
    }
    else if(0 == pid)
    {
        execv("/mnt/hgfs/share/class/Linux/process/5test", argv);
    }
    else
    {
        printf("This is Parent Process %d\n", getpid());
    }
    return 0;
}

4. 进程等待

(1)pid_t wait (int   * status)

         例:wait(&status);

功能:等待任意一个子进程结束,回收子进程资源

        宏函数:WIFEXITED :判断子进程是否正常退出,正常退出返回非0

                      WEXITSTATUS :获取子进程的退出状态

(2)pid_t waitpid (pid_t pid, int   * status, int options)

         例:waitpid(pid, &status, 0);

功能:父进程指定等待某一子进程结束

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>

int main()
{
    pid_t pid;

    pid = fork();  //创建子进程
    if(-1 == pid)
    {
        perror("fork");
        exit(1);
    }
    else if(0 == pid)  //子进程
    {
        sleep(1);
        printf("This is ChildProcess!\n");
        exit(100);
    }
    else //父进程
    {
        int status;
        printf("This is ParentProcess!\n");
        wait(&status);//1.等待子进程结束(阻塞)2.回收子进程资源
        if(WIFEXITED(status))  //判断子进程是否正常退出
        {
            printf("Child Exit Normally! %d\n", WEXITSTATUS(status));//获取子进程退出状态
        }
    }
    return 0;
}

5. 进程退出 exit(1);

6.孤儿进程:父进程结束,此时子进程为孤儿进程,默认父进程为init

   僵尸进程: 子进程结束,父进程没有回收其资源

 

猜你喜欢

转载自blog.csdn.net/qq_41998576/article/details/81709507