天天看点

unix/linux多进程编程2-进程控制

主要内容

①进程创建

②执行程序

③进程终止

④进程属性

1:进程标识符

1.1

①每个进程都有一份非负整数表示的唯一进程ID

②进程ID可以重新,一个进程结束之后可以,这个ID可以被其他进程所使用,当UNIX普遍都采用了延迟重用算法。使得某一个进程结束之后其ID不会马上被新的进程所使用,以防止将新进程误认为之前结束的进程.

③进程ID为0的通常是调度进程,常称为交换进程(swapper),属于内核的一部分,不执行磁盘上的任何程序,因而也称为系统进程。

④进程ID为1的通常是init进程,负责自举内核之后,启动一个UNIX系统。注意它是一个普通用户进程,但是以超级用户的权限运行。

⑤ID为2的是页守护进程,负责支持虚拟存储系统的分页操作

1.2获取进程标识的函数

#include<unistd.h>

pid_t getpid(); //获取进程ID

pid_t getppid(); //获取父进程ID

pid_t getuid(); //获取实际用户ID,运行这个进程的用户是谁

pid_t geteuid(); //获取有效用户ID,用于文件访问权限检查,决定了对于某个文件的访问权限

pid_t getgid();

pid_t getegid();

2:fork函数

2.1

①函数原型

#include<unistd.h>

pid_t fork(void);

②创建一个新的进程

③调用一次,返回两次子进程中返回0,父进程当中返回子进程的ID

④子进程是父进程的副本,获得了父进程的进程空间数据,包括数据段,bss段,堆,栈。而正文段则是父子进程共享。现在很多系统的实现并不采用完全复制的方法,而是采用写时复制技术。父子进程共享,内核将此区域设置为只读,父子进程任何一个试图修改,内核都将为修改区域的内存制作一个副本。

2.2文件共享

①父子进程共享文件表项

②通常对父子进程对文件描述符的处理方法

a:父进程等待子进程操作文件

b:父子进程使用不同的代码段,即在fork之后,父子进程各自关闭不需要的文件描述符。

③父子进程文件共享图示

unix/linux多进程编程2-进程控制
unix/linux多进程编程2-进程控制

3.3fork的两种常见用法

①父进程希望复制自己,父子进程执行不同代码.例如网络服务器,父进程等待请求,当请求到来之后,子进程处理请求,而父进程继续等待下次请求。

②一个进程需要执行一个不同的程序, 子进程fork返回之后立刻调用exec

3:vfork函数

3.1 vfork和fork的区别

①vfork的目的就是执行一个新的程序.

②vfork并不复制父进程的地址空间,相反它在调用exit或者exec之前它在父进程的地址空间中运行.

③vfork保证子进程先执行,只有当子进程调用exec或者exit之后,父进程才有可能被调度运行。

4:进程的结束

4.1

无论是正常退出还是异常终止的进程,其父进程都可以获得其终止状态.正常退出的进程,通过调用exit _exit 或者_Exit将其退出状态作为参数传递给函数而对于异常终止的进程,内核产生一个指示异常终止原因的终止状态

4.2

父进程已经结束的进程都将会被init进程收养

4.3僵死进程

一个 已经终止,但父进程没有对其进行善后处理的进程称为僵死进程.

一个进程结束时,如果父进程仍然存在,而且父进程在fork之前既没有安装SIGCHID信号处理函数调用wait/waitpid等待子进程结束,也没有显示的忽略该信号,则这个子进程在父进程结束之前都为僵死进程,它已经放弃了几乎所有内存空间,没有任何可执行代码,也不能被调度,仅仅在进程列表中保留一个位 置,记载该进程的退出状态等信息供其他进程收集,除此之外,僵尸进程不再占有任何内存空间.

5:wait和waitpid

5.1函数原型

#include<sys/wait.h>

pid_t wait(int *statloc)

pid_t wait(pid_t pid, int *statloc, int options)

5.2

一个进程正常或者异常终止,内核都会向其父进程发送一个SIGCHLD信号,由于子进程的终止可能发生在任何时候,所以这也是个异步信号。对于这个信号父进程可以选择忽略,或者提供一个信号处理函数。如果什么不做,系统是默认忽略,如果采用默认忽略的方式那么子进程就会成为一个僵死进程(父进程为init除外)

。如果父进程调用了wait或者waitpid则会发送如下情况。

a:如果所有子进程均在运行,则阻塞

b:如果一个子进程已经终止,正等待父进程获取其终止状态,则取得该子进程的终止状态立即返回。

c:如果没有任何子进程则立即返回错误

example:

#include<stdio.h>

#include<sys/wait.h>

int main()

{

    int stats;

    pid_t pid;

    if( (pid = fork()) < 0){ 

        printf("fork error\n");

        return 1;

    }   

    else if(pid == 0){ 

        int i;

        printf("this is the child process\n");

        for(i=0; i<10; ++i){

            sleep(1);

            printf("ok!\n");

        }   

    }   

    else{

        if(wait(&stats) != pid){

            printf("wait error\n");

        }   

        printf("stats:%d\n",stats);

        return 0;

    }   

}运行结果如下

unix/linux多进程编程2-进程控制

5.3 wait和waitpid的区别

wait在第一个子进程终止之前会使父进程一直处于阻塞状态而waitpid不会,waitpid有若干个选项可以控制所等待的进程。

注意调用一次之会等待第一个进程的结束,

5.4

wait.h中定义了四个互斥的宏来获取终止原因。p180

5.5fork两次,一种避免产生僵死进程的方法

原理:进程A fork出进程B,之后进程B发 fork出进程C,进程B立即退出,进程C调用sleep函数保证进程B先退出,进程A中调用waitpid函数等待进程B退出。

我们实际需要的新的进程是C,进程B做个中间进程,因为进程B结束之后,进程C成为init的子进程,所以它不会成为僵死进程,而进程A在进程B结束之后它就不会再阻塞。

6:exec函数

6.1:总共有六个exec函数,区别如下

p:使用环境变量中的PATH

l:使用list

v:使用数组

e:使用自定义的环境变量

6.2:大部分unix实现当中只有一个execve是系统调用,其他六个均为库函数

继续阅读