UNIX环境C语言编程(7)-进程控制

1、进程标识

#include < unistd.h >
pid_t getpid (void); # 获取进程 ID
pid_t getppid (void); # 获取父进程 ID
uid_t getuid (void); # 获取实际用户 ID
uid_t geteuid (void); # 获取有效用户 ID
gid_t getgid (void); # 获取实际组 ID
gid_t getegid (void); # 获取有效组 ID

 

2、fork函数

#include < unistd.h >
pid_t fork (void);
一次调用,两次返回
子进程是父进程的一个拷贝,如数据空间、堆、堆栈,子进程对变量的修改并不能影响父进程
fork 之后,不能确定父子进程谁先执行
如果标准输出是终端设备,那么是行缓冲的,否则是全缓冲的
依次采用下述两种方式运行,比较输出结果
./ a.out
./ a.out > 1.out
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int     glob = 6;               /* external variable in initialized data */

int main(void)
{
    int       var = 8;          /* automatic variable on the stack */
    pid_t     pid;

    printf("before fork\n");    /* we don't flush stdout */

    if( (pid = fork()) < 0 )
    {
        perror("fork");
        exit(0);
    }
    else if( pid == 0 )         /* child */
    {
        glob++;                 /* modify variables */
        var++;
    }
    else
    {
        sleep(2);               /* parent */
    }

    printf("pid = %d, glob = %d, var = %d\n", getpid(), glob, var);
    exit(0);
}

文件共享,父进程打开的文件描述符会被子进程继承

 

3、exit函数

不管进程如何终止,内核都会为其关闭所有打开的描述符,释放它所使用的内存等
子进程退出(不管正常退出,还是异常终止)后,父进程可以获取子进程的退出状态
几个宏定义(下一节描述),用以分析子进程的退出状态
WIFEXITED (status), WEXITSTATUS (status)
WIFSIGNALED (status), WTERMSIG (status)
考虑几个情况:
1 、父进程先于子进程终止,子进程被系统进程 init 接管
2 、子进程先于父进程终止,子进程成为僵尸进程 defunct
3 、被系统进程 init 接管的进程终止,不会成为僵尸

 

4、waitwaitpid函数

#include <sys/ wait.h >
pid_t wait ( int * statloc );
pid_t waitpid ( pid_t pid , int * statloc , int options);
由父进程调用,获取子进程的退出状态
如果没有子进程, wait 失败返回
如果子进程在 wait 之前就已经终止, wait 立即成功返回
如果所有子进程都在运行, wait 将阻塞
避免产生僵尸进程的方法:调用 fork 两次

  (我们不关心子进程的退出状态,不想调用wait

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

int main(void)
{
    pid_t   pid;

    if( (pid = fork()) < 0 )
    {
        perror("fork");
        exit(0);
    }
    else if( pid == 0 )    /* first child */
    {
        if ((pid = fork()) < 0)
        {
            perror("fork");
            exit(0);
        }
        else if( pid > 0 )
        {
            exit(0);       /* parent from second fork == first child */
        }

        /*
         * We're the second child; our parent becomes init as soon
         * as our real parent calls exit() in the statement above.
         * When we're done, init will reap our status.
         */
        sleep(2);
        printf("second child, parent pid = %d\n", getppid());
        exit(0);
    }

    /* wait for first child */
    if( waitpid(pid, NULL, 0) != pid )
    {
        perror("waitpid");
        exit(0);
    }

    /*
     * We're the parent (the original process); we continue executing,
     * knowing that we're not the parent of the second child.
     */
    exit(0);
}


5、竞态条件

竞态:多个进程访问共享的数据,最终结果依赖于各进程的执行顺序
看一下这个程序,多次执行时结果很可能不一致
#include <stdio.h>
#include <stdlib.h>

static void charatatime(char *);

int main(void)
{
    pid_t   pid;

    setbuf(stdout, NULL);           /* set unbuffered */

    if( (pid = fork()) < 0 )
    {
        perror("fork");
        exit(0);
    }
    else if( pid == 0 )
    {
        charatatime("output from child\n");
    }
    else
    {
        charatatime("output from parent\n");
    }

    exit(0);
}

static void charatatime(char *str)
{
    while( *str ) putc(*(str++), stdout);
}

修正一下,加入同步函数,避免竞态条件
#include <stdio.h>
#include <stdlib.h>

static void charatatime(char *);

int main(void)
{
    pid_t   pid;

    TELL_WAIT();            /* 初始化同步结构 */
    setbuf(stdout, NULL);  /* set unbuffered */

    if( (pid = fork()) < 0 )
    {
        perror("fork");
        exit(0);
    }
    else if( pid == 0 )
    {
        WAIT_PARENT();      /* 等待父进程完成 */
        charatatime("output from child\n");
    }
    else
    {
        charatatime("output from parent\n");
        TELL_CHILD(pid);    /* 通知子进程已完成 */
    }

    exit(0);
}

static void charatatime(char *str)
{
    while( *str ) putc(*(str++), stdout);
}
注:同步函数的实现,参见后续章节

 

6、exec函数族

exec 用于执行一个程序文件,执行后新的程序文件将完全替代现有进程
执行前后,进程 ID 没有改变,因为并没有创建新的进程
#include < unistd.h >
int execlp ( const char *filename, const char *arg0, ... /* (char *)0 */ );
int execvp ( const char *filename, char * const argv []);
exec 函数族共有 6 个变种,其它的类似,有的允许指定新的环境
回忆一下 FD_CLOEXEC 标志
程序示例

被执行的程序,编译为echoall:

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

int main(int argc, char *argv[])
{
    int         i;

    for( i = 0; i < argc; i++ )       /* echo all command-line args */
        printf("argv[%d]: %s\n", i, argv[i]);

    exit(0);
}


执行程序:

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

int main(void)
{
    pid_t   pid;

    if( (pid = fork()) < 0 )
    {
        perror("fork");
        exit(0);
    }
    else if( pid == 0 )    /* specify pathname */
    {
        if( execlp("./echoall", "abc", "myarg1", "MY ARG2", (char *)0) < 0 )
        {
            perror("execlp");
            exit(0);
        }
    }

    if( waitpid(pid, NULL, 0) < 0 )
    {
        perror("waitpid");
        exit(0);
    }

    exit(0);
}


 

7、改变用户ID、组ID

Unix 系统中,基于用户 ID 、组 ID 进行权限的检查
#include <unistd.h>
int setuid (uid_t uid);
int setgid (gid_t gid);
对于 root 用户, setuid 将改变实际用户 ID 、有效用户 ID 、保存的用户 ID
对于普通用户, setuid 仅改变有效用户 ID

 

8、解释器文件

当代 Unix 系统都支持解释器文件,文本文件,首行格式:
#! pathname [ optional-argument ]
例如: #! /bin/ ksh
真正执行的是 pathname 指定的解释器,而不是文本文件自身

 

9、system()函数

#include < stdlib.h >
int system( const char * cmdstring );
方便的执行一个系统命令,如: system("date > file");
任何一个在命令行执行的命令,都可以通过 system 函数调用
例子,使用 system() 函数调用 date 命令输出当前时间
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char **argv)
{
        if( argc != 2 ) exit(0);
        system(argv[1]);
        return 0;
}


10、用户标识

获取登录用户名称
#include < unistd.h >
char * getlogin (void);
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值