8、进程控制(1)

来源:互联网 发布:知乎笔记本推荐 编辑:程序博客网 时间:2024/06/14 15:11

1、进程ID总是唯一的,常将其作为其他标识符的一部分以确保其唯一性。一个进程结束后ID可以复用,一般采用延迟复用法。ID为0是调度进程(交换进程),是内核的一部分属于系统进程。ID=1是init进程,在内核自举后启动UNIX系统,他通常读取与系统相关的初始化文件(/etc/rc*,/etc/inittab,/etc/init.d中文件等)。init进程不会停止,虽然是普通进程却是以root权限允许。

#include <unistd.h>pid_t getpid(void);pid_t getppid(void);//返回父进程IDuid_t getuid(void);uid_t geteuid(void);//返回调用进程的有效用户IDgid_t getgid(void);gid_t getegid(void);

2、fork
子进程是父进程的副本,子进程获得父进程数据空间、堆、栈的副本,共享正文段。子进程和父进程都会继续执行fork之后的部分。fork之后子进程先执行还父进程先执行取决于调度算法。

int globvar=6;char buf[]="a write to stdout\n";int main(void){        int var;        pid_t pid;        var = 88;        //strlen不包含终止null字节,需要进行一次函数调用        //sizeof计算包含终止null的长度,sizeof是在编译时计算缓冲区长度。        if(write(STDOUT_FILENO, buf, sizeof(buf)-1) != sizeof(buf)-1)                err_sys("write error");        printf("before fork\n");        if((pid = fork()) < 0){                err_sys("fork error");        }else if(pid == 0){                globvar++;                var++;        }else{                sleep(2);        }        printf("pid=%ld,glob=%d,var=%d\n",(long)getpid(),globvar,var);        exit(0);}
//标准输出连到终端设备,只得到一次printf输出,因为缓冲区由换行符冲洗$ ./myforka write to stdoutbefore forkpid=2114,glob=7,var=89pid=2113,glob=6,var=88//当输出重定向到一个文件,printf输出2次,因为fork时缓冲区数据也被复制到子进程。每个进程终止时缓冲区输出到各文件。$ ./myfork  >temp$ cat tempa write to stdoutbefore forkpid=2116,glob=7,var=89before forkpid=2115,glob=6,var=88

fork父进程和子进程共享一个文件偏移量,文件表项位于内核
这里写图片描述

3、vfork
vfork在调用exec或exit之前和父进程在相同空间(父进程空间,不复制地址空间)运行。vfork保证子进程先运行,调用exec或exit后父进程才可能运行。

4、exit,_exit由exit调用,_exit是系统调用,exit是C库函数
内核为每个终止子进程保存一定量的信息,当其父进程调用wait或waitpid时可以得到这些信息。
僵尸进程:一个进程已经终止,但是其父进程没有做善后操作(获取终止进程的相关信息,释放它仍占用的资源)。
一个进程的父进程先于子进程终止,则子进程的父进程变为init(收养)。只要init进程的一个子进程终止,它就会调用一个wait函数取得其终止状态。

5、父进程执行wait和waitpid
    一般有进程终止时内核向其父进程发送SIGCHLD信号,父进程再调用wait/waitpid。所有子进程都还在运行则阻塞;一个进程已终止则得到其终止状态并返回;没有任何子进程则出错返回。

#include <sys/wait.h>//返回第一个终止的子进程ID,子进程信息存于statlocpid_t wait(int *statloc);//等待特定子进程pid_t waitpid(pid_t pid, int *sataloc, int options);
//父进程先终止,子进程的父进程变为init(ID=1#include "apue.h"#include <sys/wait.h>int main(){        pid_t pid;        if((pid = fork()) < 0)                err_sys("fork error");        else if(pid == 0){                if((pid = fork()) < 0)                        err_sys("fork error");                else if(pid > 0)                        exit(0);                sleep(2);                printf("second child,parent pid=%ld\n",(long)getppid());                exit(0);        }        if(waitpid(pid, NULL, 0) != pid)                err_sys("waitpid error");        exit(0);}

6、exec
调用exec函数使新进程执行另一个程序。该新程序从一个新文件的main处开始执行(用磁盘上的一个新程序替代了当前进程的正文段、数据段、堆段、栈段)。如果找到的非可执行文件,而是一个shell脚本则调用/bin/sh
linux中只有execve是内核的系统调用,其他6个都是库函数

#include <unistd.h>//l代表参数列表,v代表参数矢量(数组),e代表环境变量//p代表第一个参数为文件名,在PATH路径中寻找//f开头表示第一个参数为文件标识符int execl(const char *pathname, const char *arg0,...);int execv(const char *pathname, const char *arg[]);int execle(const char *pathname, const char *arg0,..., char *const envp[]);int execve(const char *pathname, const char *arg[], char *const envp[]);int execlp(const char *filename, const char *arg0,...);int execvp(const char *filename, const char *arg[]);int fexecve(int fd, const char *arg[], char *const envp[]);
//在对应目录中分别编译两个文件//exec调用的是可执行文件#include "apue.h"#include <sys/wait.h>char *env_init[] = {"USER=unknown", "PATHA=/temp", NULL};int main(void){        pid_t pid;        if((pid = fork()) < 0){                err_sys("fork error");        }else if(pid == 0){                if(execle("/home/john/temp/echoall", "echoall", "myarg1", "MYARG2", (char *)0, env_init) < 0)                        err_sys("execle error");        }        if(waitpid(pid, NULL, 0) < 0)                err_sys("wait error");        if((pid = fork()) < 0){                err_sys("fork error");        }else if(pid == 0){        //execlp在PATH寻找该文件                if(execlp("echoall", "echoall", "only 1 arg", "MYARG2", (char *)0) < 0)                        err_sys("execlp error");        }        exit(0);}
int main(int argc, char *argv[]){        int i;        char **ptr;        extern char **environ;        for(i = 0; i < argc; i++)                printf("argv[%d]: %s\n", i, argv[i]);        for(ptr = environ; *ptr != 0; ptr++)                printf("%s\n", *ptr);        exit(0);}
原创粉丝点击