LinuxC—进程

进程

1 进程标识符pid

基本概念

类型pid_t,是一个有符号16位整数,进程号是顺次向下使用(fd是优先使用当前可用最小的)

shell中的ps命令能够查看当前系统的进程信息快照

相关函数

  • getpid(2)获取当前进程的进程号
/* Get the process ID of the calling process.  */
extern __pid_t getpid (void) __THROW;
  • getppid(2)获取当前进程父进程的进程号
/* Get the process ID of the calling process's parent.  */
extern __pid_t getppid (void) __THROW;

2 进程的产生——fork(2)

fork(2)

  • fork() 创建一个子进程,并返回其pid
/* Clone the calling process, creating an exact copy.
   Return -1 for errors, 0 to the new process,
   and the process ID of the new process to the old process.  */
extern __pid_t fork (void) __THROWNL;
  • fork()是通过复制的方式创建一个和父进程几乎一样的进程,连执行到的代码都一样,这两个进程不一样的的地方如下:

    1. fork()的返回值不一样
    2. 父子进程的pid,ppid不同
    3. 未决信号和文件锁不继承
    4. 资源利用量清0
  • 返回值:

    • 失败返回给父进程-1
    • 成功返回给父进程子进程的pid,子进程中返回0
  • 注意fork()对于内存空间采用了写时复制的技术,就是说在创建子进程时,并没有将父进程的物理内存完全复制一份,而是将其页表复制了一份,并将这块内存的权限设置为只读,当父进程或子进程要对某块内存执行写操作的时候就会触发CPU的写保护中断,操作系统就会执行写保护中断函数,在这个函数中会将执行写操作的数据页复制一份,并重新设置其内存映射关系后将父子进程的这些数据页设置为可读写的

  • 示例1:

int main(int argc, char **argv) {
   

    pid_t pid;
    printf("[%d]:Begin!\n", getpid());
    fflush(NULL); //在fork()前要刷新所有的流

    pid = fork();
    if (pid < 0) {
   
        perror("fork():");
        exit(1);
    }
    sleep(1);
    if (pid == 0) {
   
        printf("[%d]:Child is working...\n", getpid());
    } else {
   
        printf("[%d]:Parent is working...\n", getpid());
    }

    printf("[%d]:End!\n", getpid());

    exit(0);

}

输出结果:可以看到两个进程出现了交替执行的情况,在实际生产环境中,永远不要凭空猜测父子进程哪一个先运行,这是由调度器的调度策略决定的

[8184]:Begin!
[8184]:Parent is working...
[8185]:Child is working...
[8184]:End!
[8185]:End!

注意:在调用fork()前一定要执行fflush()刷新所有的流,若没有fflush()则输出结果如下:可以看到begin执行的两次,且进程号都是父进程的,因为当我们将./main执行结果重定向到out文件中的时候就不再是stdout了,也就不再是行缓冲,而是全缓冲,由于子进程将父进程的内存复制一份,所以缓冲区中的"[10397]:Begin!"也复制了一份

root@VM-24-2-ubuntu:/home/ubuntu/linux# ./main > ./out
root@VM-24-2-ubuntu:/home/ubuntu/linux# cat out
[10397]:Begin!
[10397]:Parent is working...
[10397]:End!
[10397]:Begin!
[10398]:Child is working...
[10398]:End!
  • 示例2:输出30000000到30000200之间的质数

    • 单进程环境下:
    #define LEFT 30000000
    #define RIGHT 30000200
    int main(int argc, char **argv) {
         
    
        int i, j, mark;
    
        for (i = LEFT; i <= RIGHT; i++) {
         
            mark = 1;
            for (j = 2; j < i / 2; j++) {
         
                if (i % j == 0) {
         
                    mark = 0;
                    break;
                }
            }
            if (mark) {
         
                printf("%d is a primer\n", i);
            }
        }
    
        exit(0);
    
    }
    

    通过time命令查看输出的时间:

    root@VM-24-2-ubuntu:/home/ubuntu/linux# time ./main > /dev/null
    
    real	0m0.829s
    user	0m0.829s
    sys		0m0.000s
    
    • 多进程情况,每得到一个i就交给一个进程来计算
    #define LEFT 30000000
    #define RIGHT 30000200
    int main(int argc, char **argv) {
         
    
        int i, j, mark;
        pid_t pid;
    
        for (i = LEFT; i <= RIGHT; i++) {
         
            mark = 1;
            pid = fork();
            if (pid < 0) {
         
                perror("fork():");
                exit(1);
            }
            if (pid == 0) {
         
                for (j = 2; j < i / 2; j++) {
         
                    if (i % j == 0) {
         
                        mark = 0;
                        break;
                    }
                }
                if (mark) {
         
                    printf("%d is a primer\n", i);
                }
                break; //一定得break,不然每个子进程又会进行外层循环从而到再次创建进程,最终可能导致系统崩溃
            }
        }
    
        exit(0);
    
    }
    

    通过time命令查看输出的时间:可以看到real time减少了非常多,同时内核态执行的时间大幅增加

    root@VM-24-2-ubuntu:/home/ubuntu/linux# time ./main > out
    
    real	0m0.058s
    user	0m0.000s
    sys		0m0.009s
    

init进程

init进程:1号进程,是所有进程的祖先进程

3 进程的消亡及释放资源

相关函数

  • wait() 将收尸的子进程的状态放到wstatus中去,该函数是阻塞
/* Wait for a child to die.  When one does, put its status in *wstatus
   and return its process ID.  For errors, return (pid_t) -1.

   This function is a cancellation point and therefore not marked with
   __THROW.  */
extern __pid_t wait (int *wstatus);
  • waitpid() 收尸指定pid的进程,options提供了一些选项来指定wait的状态(阻塞?非阻塞等)
/* Wait for a child matching PID to die.
   If PID is greater than 0, match any process whose process ID is PID.
   If PID is (pid_t) -1, match any process. //收取任何一个子进程
   If PID is (pid_t) 0, match any process with the //收取同一组中该进程的子进程
   same process group as the current process.
   If PID is less than -1, match any process whose //收取PID绝对值对应进程组中的子进程
   process group is the absolute value of PID.
   If the WNOHANG bit is set in OPTIONS, and that child
   is not already dead, return (pid_t) 0.  If successful,
   return PID and store the dead child's status in wstatus.
   Return (pid_t) -1 for errors.  If the WUNTRACED bit is
   set in OPTIONS, return status for stopped children; otherwise don't.

   This function is a cancellation point and therefore not marked with
   __THROW.  */
extern __pid_t waitpid (__pid_t __pid, int *wstatus, int __options);

options

The value of options is an OR of zero or more of the following constants:

WNOHANG     return immediately if no child has exited.

WUNTRACED   also return if a child has stopped (but not traced via ptrace(2)).  Status for traced children which have stopped is provided even if this option is not specified.

WCONTINUED (since Linux 2.6.10)
also return if a stopped child has been resumed by delivery of SI
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值