8、进程控制(1)

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);//返回父进程ID

uid_t getuid(void);
uid_t geteuid(void);//返回调用进程的有效用户ID

gid_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输出,因为缓冲区由换行符冲洗
$ ./myfork
a write to stdout
before fork
pid=2114,glob=7,var=89
pid=2113,glob=6,var=88

//当输出重定向到一个文件,printf输出2次,因为fork时缓冲区数据也被复制到子进程。每个进程终止时缓冲区输出到各文件。
$ ./myfork  >temp
$ cat temp
a write to stdout
before fork
pid=2116,glob=7,var=89
before fork
pid=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,子进程信息存于statloc
pid_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);

}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值