AUPE学习第八章------进程控制

本文深入探讨了进程管理的基础知识,包括进程ID、父子进程的关系、进程的创建与终止、进程状态的获取、进程权限的更改等内容。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

每个进程都有一个非负整形表示的唯一进程ID。

init进程是一号进程,是第一个用户态的进程。它负责内核启动以后启动一个unix系统,

它读取的配置文件一般在/etc/rc*、/etc/inittab、/etc/init.d中。

下面的函数返回进程的一些标识:

pid_t   getpid(void)   //调用进程的进程ID.

pid_t   getppid(void)   //调用进程的父进程。

uid_t   getuid(void)   //返回调用进程的实际用户ID。

uid_t   geteuid(void)   //返回用户的有效用户id

uid_t   getgid(void)      //调用进程的实际用户组ID。

gid_t    getegid(void)     //调用进程的实际组ID。

8.3fork函数

一个进程可以调用fork来创建新的进程。
pid_t    fork(void)
#include "apue.h"

int glob = 6;
char buf[] = "a write to stdout\n";
int main(void)
{
        int var;
        pid_t pid;
        var = 88;
        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)
        {
                glob++;
                var++;
        }else
        {sleep(2);
        }
        printf("pid = %d, glob = %d, var = %d\n", getpid(), glob, var);
        exit(0);
}
运行结果:
[root@localhost apue]# ./a.out 
a write to stdout
before fork
pid = 7656, glob = 7, var = 89
pid = 7655, glob = 6, var = 88
[root@localhost apue]# ./a.out >fort.out
[root@localhost apue]# cat fort.out 
a write to stdout
before fork
pid = 7671, glob = 7, var = 89
before fork
pid = 7670, glob = 6, var = 88



父进程和子进程共享打开的文件。子进程把父进程的所有打开的文件描述符都复制到子进程中。









子进程和父进程共享一个文件表项。

8.4vfork函数

vfork函数的调用序列和返回值与fork相同。但两个语义不同。
vfork可以保证子进程的先与父进程执行。它调用exec或者exit之后父进程才可能被调度执行。
vforktest.c:
#include "apue.h"
int glob = 6;
int main()
{
        int var;
        pid_t pid;
        var = 88;
        printf("before vfork\n");
        if ((pid = vfork()) < 0){
                err_sys("vfork error");
        }else if (pid == 0){
                glob++;
                var++;
                _exit(0);
        }
        printf("pid = %d, glob = %d, var = %d\n", getpid, glob, var);
        exit(0);
}
运行结果:
[root@localhost apue]# vim vforktest.c
[root@localhost apue]# gcc vforktest.c 
[root@localhost apue]# ./a.out 
before vfork
pid = 134513812, glob = 7, var = 89

8.6wait和waitpid函数

当一个进程正常或者异常终止时,内核就向其父进程发送SIGCHLD信号。
父进程可以忽略这个信号或者调用一个执行的函数。系统默认是忽略它。
pid_t   wait(int  *statloc)
pid_t   waitpid( pid_t   pid,  int   *statloc,   int   options)
若成功返回进程ID,出错则返回-1。
下面为关于子进程退出状态的操作.
exittest.c:
#include "apue.h"
#include <sys/wait.h>

void pr_exit(int status);
int main(void)
{
        pid_t pid;
        int status;
        if ((pid = fork()) < 0)
                err_sys("error fork");
        else if (pid == 0)
                exit(7);
        if (wait(&status) != pid)
                err_sys("wait error");
        pr_exit(status);

        if ((pid = fork()) < 0)
                err_sys("error fork");
        else if (pid == 0)
                abort();
        if (wait(&status) != pid)
                err_sys("wait error");
        pr_exit(status);

        if ((pid = fork()) < 0)
                err_sys("error fork");
        else if (pid == 0)
                status /= 0;
        if (wait(&status) != pid)
                err_sys("wait error");
        pr_exit(status);

        exit(0);
}

void pr_exit(int status)
{
        if (WIFEXITED(status))
                printf("normal termination,exit status = %d\n", WEXITSTATUS(status));
        else if (WIFSIGNALED(status))
                printf("abnormal termination, signal number = %d%s\n",WTERMSIG(status),
                #ifdef WCOREDUMP
                        WCOREDUMP(status) ? " (core file generated)" : "");
                #else
                        "");
                #endif
                else if (WIFSTOPPED(status))
                        printf("child stopped, signal number = %d\n", WSTOPSIG(status));
}
运行结果:
[root@localhost apue]# vim exittest.c
[root@localhost apue]# gcc exittest.c 
exittest.c: In function ‘main’:
exittest.c:28: 警告:被零除
[root@localhost apue]# ./a.out 
normal termination,exit status = 7
abnormal termination, signal number = 6
abnormal termination, signal number = 8

如果一个进程有几个子进程,name只要有一个子进程终止,wait函数就返回。
waitpid用于等待一个指定的进程终止。
pid_t   waitpid(pid_t  pid,  int   *statloc,  int   options);
如果pid = -1  等待任一进程终止,此时与wait等效。
pid > 0 等待其进程ID与pid相等的子进程。
pid  == 0  等待其组ID等于调用进程组ID的任一子进程。
pid  < -1  等待组ID等于pid绝对值得任一子进程。

下面的实例实现在父进程终止以后,子进程变为init进程。
waitpidtest.c:
#include "apue.h"
#include  <sys/wait.h>
int main(void)
{
        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 =%d\n", getppid());
                exit(0);
        }
        if (waitpid(pid, NULL, 0) != pid)
                err_sys("waitpid error");
        exit(0);
}
运行结果
[root@localhost apue]# ./a.out >wait.test
[root@localhost apue]# cat wait.test 
second child, parent pid =1

8.7waitid函数

SUS的XSI扩展包含了另一个取进程终止状态的函数,waitid,这个函数的功能类似与waitpid。
int   waitid(  idtype_t   idtype,   id_t  id,   siginfo_t   *infop,   int    option)
这个函数也可以指定要等待的进程。提供专门的参数表示等待的进程的类型。
idtype表示等待的进程的类型:P_PID、P_PGID、P_ALL。
根据idtype的不同,id的意义也不同。

同时我们也可以通过wait3和wait4函数来实现wait函数相似的功能。
pid_t   wait3( int   *statloc,  int   options,  struct   rusage   *rusage);
pid_t   wait4(   pid_t   pid,   int  *statloc,   int   options,   struct   rusage   *rusage)
若成功则返回进程ID,出错返回-1。
其中rusage结构请求内核返回终止进程及其子进程使用的资源的汇总。

8.9竞争条件

当多个进程师徒对共享数据进行某种处理时,而最后的结果取决于进程运行的顺序时,则我们认为这发生了竞争条件。
如果一个进程等待子进程终止,则它必须表用wait函数。如果一个进程要等待其父进程终止,则使用如下模式:
while  (getppid()   !=  1)
sleep(1);
作为上面两种情况的优化,我们可以用信号来处理。

下面案例在子进程和父进程做任何事之前和之后都通知对方:
#include  "apue.h"

TELL_WAIT();    /* set things up for TELL_xxx & WAIT_xxx */

if ((pid = fork()) < 0) {
    err_sys("fork error");
} else if (pid == 0) {            /* child */

    /* child does whatever is necessary ... */

    TELL_PARENT(getppid());     /* tell parent we're done */
    WAIT_PARENT();              /* and wait for parent */

    /* and the child continues on its way ... */

    exit(0);
}

/* parent does whatever is necessary ... */

TELL_CHILD(pid);            /* tell child we're done */
WAIT_CHILD();               /* and wait for child */

/* and the parent continues on its way ... */

exit(0);
下面来测试两个字符串输出在子进程和父进程之前切换。
choicetest.c:
#include "apue.h"
static void charatatime(char *);

int main()
{
        pid_t pid;
        if ((pid = fork()) < 0)
                err_sys("fork error");
        else if (pid == 0)
                        charatatime("output from chlid\n");
                else
                        charatatime("output from parent\n");
        exit(0);
}
static void charatatime(char  *str)
{
        char *ptr;
        int c;
        setbuf(stdout,NULL);
        for (ptr = str; (c = *ptr++) != 0;)
        putc(c, stdout);
}
运行结果:
[root@localhost apue]# vim choicetest.c
[root@localhost apue]# gcc choicetest.c 
[root@localhost apue]# ./a.out 
output from chlid
output from parent
[root@localhost apue]# ./a.out 
output from chlid
output from parent

8.10exec函数

exec并不创建新进程,只是一个全新的程序替换了当前进程的正文、数据、堆、栈。
int   execl( const   char   *pahtname, const  char  *arg0, ...)
int   execv( const  char  *pathname,  char  *const   argv[])
int   execle( const  char  *pathname,  const  char  *arg0, .../*char  *const  envp[] */)    
int   execve( const  char  *pathname,  char  *const  argv[],  char  *const  envp[])
int  execlp( const   char  *filename,  const  char  *arg0,  ...)
int  execvp( const  char  *filename,  char  *const  argv[])
函数中有字母p表示取filename作为参数,并且用PATH环境变量寻找可执行文件,字母l表示该函数取一个参数表,它与字母v互斥,v表示argv[]。
字母e表示envp[]数组,而不用当前环境。

#include "apue.h"
#include <sys/wait.h>
char  *env_init[] = {"USER=unknown", "PATH=/tmp", NULL};
int main(void)
{
        pid_t pid;
        if ((pid = fork()) < 0)
                err_sys("fork error");
        else if (pid == 0)
        {
                if (execle("/home/huangcd/echoall", "echoall", "myarg1", "MY ARG2", (char *)0, env_init) < 0)
                        err_sys("execle error");
        }
        if (waitpid(pid, NULL, 0) < 0)
                err_sys("execle error");

        if ((pid = fork()) < 0)
                err_sys("fork error");
        else if (pid == 0)
        {
                if (execlp("echoall", "echoall", "only 1 arg", (char *)0) < 0)
                        err_sys("execlp error");
        }
        exit(0);
}

8.11更改用户ID和组ID

unix对文件权限的管理师基于用户和组ID的。
在设计应用程序时,我们试图使用最小权限。
我们可以通过下面的函数在设置实际和有效用户、组ID。
int   setuid(  uid_t   uid)
int   setgid(  gid_t   gid)
成功返回0,失败返回-1。
只有超级用户可以修改实际用户ID。

我们可以用下面两个函数来交换实际用户ID和有效用户ID的值。
int   setreuid(   uid_t   ruid,   uid_t   euid)
int   setregid(   gid_t   rgid,   gid_t  egit)
可以用下面两个函数来更改有效用户ID和有效组ID。
int   seteuid(uid_t   uid)
int   setegid(gid_t   gid)

我们一定要区分解释器和解释器文件:解释器文件的第一行表示的就是解释器的地址。

8.13system函数

我们可以通过system函数来执行一个命令:
int   system(const   char   *cmdstring)

下面的程序根据不同的命令执行不同的结果,同时根据返回状态显示不同的内容:
systemtest.c:
#include "apue.h"
#include <sys/wait.h>
#include <errno.h>
#include <unistd.h>

int system(const  char  *cmdstring);
void pr_exit(int status);

int main(void)
{
        int status;
        if ((status = system("date")) < 0)
                err_sys("system() error");
        pr_exit(status);
        if ((status = system("nosuckcommand")) < 0)
                err_sys("system() error");
        pr_exit(status);
        if ((status = system("who; exit 44")) < 0)
                err_sys("system() error");
        pr_exit(status);
        exit(0);
}
void pr_exit(int status)
{
        if (WIFEXITED(status))
                printf("normal termination,exit status = %d\n", WEXITSTATUS(status));
        else if (WIFSIGNALED(status))
                printf("abnormal termination, signal number = %d%s\n", WTERMSIG(status),
        #ifdef WCOREDUMP
                WCOREDUMP(status) ? "(core file generated)" : "");
        #else
                "");
        #endif
                else if (WIFSTOPPED(status))
                        printf("child stopped, signal number = %d\n", WSTOPSIG(status));
}

int system(const char *cmdstring)
{
        pid_t pid;
        int status;
        if (cmdstring == NULL)
                return(1);
        if ((pid = fork()) < 0)
        {
                status = -1;
        }else if (pid ==0)
        {
                execl("/bin/bash", "sh", "-c", cmdstring, (char *)0);
                _exit(127);
        }else{
                while (waitpid(pid, &status, 0) < 0){
                        if (errno != EINTR){
                                status = -1;
                                break;
                        }
                }
        }
        return(status);
}
运行结果:
[root@localhost apue]# vim systemtest.c
[root@localhost apue]# gcc systemtest.c 
[root@localhost apue]# ./a
a.out   apue.h  
[root@localhost apue]# ./a.out 
2014年 01月 04日 星期六 01:05:36 CST
normal termination,exit status = 0
sh: nosuckcommand: command not found
normal termination,exit status = 127
root     :0           2014-01-03 18:07
root     pts/1        2014-01-03 18:13 (:0.0)
normal termination,exit status = 44

我们可以通过下面的函数来得到登陆的用户名:

char  *  getlogin(void)

我们通过调用此函数的进程来得到实际用户的登录名。

8.16进程时间

我们可以测量一个进程的墙上时钟时间、用户cpu时间、系统cpu时间、任一进程都可以调用times函数以获得它自己及已经终止子进程的上述值。
clock_t   times(struct   tms   *buf)
若成功返回墙上时钟时间,出错返回-1。
此函数把相应值填入buf中,此机构如下:
struct   tms{
clock_t   tms_utime;
clock_t   tms_stime;
clock_t tms_cutime
clock_t   tms_cstime;
}
下面的实例将用于测试:
timestest.c:
#include "apue.h"
#include <sys/wait.h>
#include <sys/times.h>

void pr_exit(int);
static void do_cmd(char *);
static void pr_times(clock_t, struct tms *, struct tms *);

int main(int argc, char *argv[])
{
        int i;
        setbuf(stdout, NULL);
        for (i = 1; i < argc; i++)
                do_cmd(argv[i]);
        exit(0);
}

static void do_cmd(char *cmd)
{
        struct tms tmsstart,tmsend;
        clock_t start,end;
        int status;
        printf("\ncommand:%s\n",cmd);
        if ((start = times(&tmsstart)) == -1)
                err_sys("times error");
        if ((status = system(cmd)) < 0)
                err_sys("system() error");
        if ((end = times(&tmsend)) < 0)
                err_sys("times error");
        pr_times(end-start, &tmsstart, &tmsend);
        pr_exit(status);
}

static void pr_times(clock_t real, struct tms *tmsstart, struct tms * tmsend)
{
        static long clktck = 0;
        if (clktck == 0)
                if ((clktck = sysconf(_SC_CLK_TCK)) < 0)
                        err_sys("sysconf error");
        printf("  real: %7.2f\n", real / (double)clktck);
        printf(" user: %7.2f\n", (tmsend->tms_utime - tmsstart->tms_utime) / (double)clktck);
        printf(" sys: %7.2f\n", (tmsend->tms_stime - tmsstart->tms_stime) / (double)clktck);
        printf(" child user: %7.2f\n", (tmsend->tms_cutime - tmsstart->tms_cutime) / (double)clktck);
        printf(" child sys: %7.2f\n", (tmsend->tms_cstime - tmsstart->tms_cstime) / (double)clktck);
}

void pr_exit(int status)
{
        if (WIFEXITED(status))
                printf("normal termination,exit status = %d\n", WEXITSTATUS(status));
        else if (WIFSIGNALED(status))
                printf("abnormal termination, signal number = %d%s\n",WTERMSIG(status),
                #ifdef WCOREDUMP
                        WCOREDUMP(status) ? " (core file generated)" : "");
                #else
                        "");
                #endif
                else if (WIFSTOPPED(status))
                        printf("child stopped, signal number = %d\n", WSTOPSIG(status));
}

运行结果:
[root@localhost apue]# ./a.out  "sleep 5" "date"

command:sleep 5
  real:    5.00
 user:    0.00
 sys:    0.00
 child user:    0.00
 child sys:    0.00
normal termination,exit status = 0

command:date
2014年 01月 04日 星期六 11:48:50 CST
  real:    0.01
 user:    0.00
 sys:    0.00
 child user:    0.00
 child sys:    0.00
normal termination,exit status = 0













评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值