文档章节

Linux中的进程和线程

秋风醉了
 秋风醉了
发布于 2016/09/18 23:45
字数 1314
阅读 63
收藏 1

Linux中的进程和线程

Linux进程的创建-fork

fork调用的一个奇妙之处就是它仅仅被调用一次,却能够返回两次,它可能有三种不同的返回值: 1)在父进程中,fork返回新创建子进程的进程ID; 2)在子进程中,fork返回0; 3)如果出现错误,fork返回一个负值;

在fork函数执行完毕后,如果创建新进程成功,则出现两个进程,一个是子进程,一个是父进程。在子进程中,fork函数返回0,在父进程中,fork返回新创建子进程的进程ID。我们可以通过fork返回的值来判断当前进程是子进程还是父进程。

示例一,

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>

int glob_count = 6;
char buf[] = "a write to stdout\n";

int main(int argc, const char * argv[]) {
    
    int var;
    int pid;
    
    var = 88;
    
    if(write(STDOUT_FILENO,buf,sizeof(buf) -1) != sizeof(buf) -1)
    {
        perror("fail to write");
        return -1;
    }
    
    printf("before fork\n");
    
    if((pid = fork()) < 0)
    {
        perror("fail to fork");
        return -1;
    }else if(pid == 0){
        glob_count ++;
        var ++;
    }else{
        sleep(2);
    }
    
    printf("pid = %d,glob = %d,var = %d\n",getpid(),glob_count,var);
    return 0;
}

运行结果(macOS平台运行),

a write to stdout
before fork
pid = 4172,glob = 7,var = 89
pid = 4169,glob = 6,var = 88
Program ended with exit code: 0

示例二

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>

int glob_count = 6;
char buf[] = "a write to stdout\n";

int main(int argc, const char * argv[]) {
    
    int var;
    int pid;
    
    var = 88;
    
    printf("father:\n");
    printf("&glob = %p\n",&glob_count);
    printf("&var = %p\n",&var);
    printf("__________________________________\n");
    
    if((pid = fork()) < 0)
    {
        perror("fail to fork");
        return -1;
        
    }else if(pid == 0){
        printf("child var value not change:\n");
        printf("&glob = %p\n",&glob_count);
        printf("&var = %p\n",&var);
        
        glob_count ++;
        var ++;
        
        printf("__________________________________\n");
        printf("child var value change:\n");
        printf("&glob = %p\n",&glob_count);
        printf("&var = %p\n",&var);
    }
    
    return 0;
}

运行结果,

father:
&glob = 0x100001030
&var = 0x7fff5fbff79c
__________________________________
child var value not change:
&glob = 0x100001030
&var = 0x7fff5fbff79c
__________________________________
child var value change:
&glob = 0x100001030
&var = 0x7fff5fbff79c
Program ended with exit code: 0

Linux进程的创建-vfork

  1. fork要拷贝父进程的数据段;而vfork则不需要完全拷贝父进程的数据段,在子进程没有调用exec和exit之前,子进程与父进程共享数据段
  2. fork不对父子进程的执行次序进行任何限制;而在vfork调用中,子进程先运行,父进程挂起,直到子进程调用了exec或exit之后,父子进程的执行次序才不再有限制。

示例,

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


int main(int argc, const char * argv[]) {
    
    int count;
    int pid;
    
    count = 88;
    
    if((pid=vfork()) < 0){
        perror("fail to vfork");
        exit(-1);
    } else if (pid  == 0){
        count++;
    } else {
        //在父进程中,返回子进程的id,pid 为子进程的id
        printf("child pid = %d \n",pid);
    }
    
    printf("pid = %d ,count = %d \n",getpid(),count);
    exit(0);
}

运行结果,

pid = 4466 ,count = 89 
child pid = 4466 
pid = 4463 ,count = 89 
Program ended with exit code: 0

Linux线程的实现

线程机制是现代编程技术中常用的一种抽象。该机制提供了在同一程序内共享内存地址空间运行的一组线程。这些线程还可以共享打开的文件和其他资源。

Linux实现线程的机制非常独特。内核并没有准备特别的调度算法或是定义特别的数据结构来表征线程。相反,线程仅仅被视为一个与其它进程共享某些资源的进程。每个线程都拥有唯一隶属于自己的task_struct,所以在内核中,他看起来就像一个普通的进程(只是该进程和其他一些进程共享某些资源,如地址空间)。

在其他的操作系统中,相较于重量级的进程,线程被抽象成一种耗费较少资源,运行迅速的执行单元。而对于Linux来说,它只是一种进程间共享资源的手段。举个例子来说,假如我们有一个包含四个线程的进程,在提供专门线程支持的系统中,通常会有一个包含指向四个不同线程的指针的进程描述符。该描述符负责描述像地址空间,打开的文件这样的共享资源。线程本身再去描述它独占的资源。相反,Linux仅仅创建四个进程并分配四个普通的task_struct结构。建立这四个进程时制定他们共享某些资源,这是相当高雅的做法。

线程的创建和普通进程的创建类似,只不过在调用clone()的时候需要传递一些参数标志来知名需要共享的资源。

clone(CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND, 0);

上面的代码产生的结果和调用fork差不多,只是父子俩共享地址空间、文件系统资源、文件描述符和信号处理程序。换个说法就是,新建的进程和他的父进程就是流行的所谓线程。 对比一下,一个普通的fork()实现是:

clone(SIGCHLD, 0);

而vfork()的实现是:

clone(CLONE_VFORK | CLONE_VM | SIGCHLD, 0);

传递给clone()的参数标志决定了新创建进程的行为方式和父子进程之间共享的资源种类。

Linux 线程模型的比较:LinuxThreads 和 NPTL

http://www.ibm.com/developerworks/cn/linux/l-threading.html

http://blog.csdn.net/adcxf/article/details/3940982

参考:

http://hustpawpaw.blog.163.com/blog/static/18422832420134694142375/

http://blog.chinaunix.net/uid-26833883-id-3222794.html

http://blog.csdn.net/feiyangyangfei/article/details/12577661

=======END=======

© 著作权归作者所有

秋风醉了
粉丝 246
博文 543
码字总数 412294
作品 0
朝阳
程序员
私信 提问
Linux内核学习笔记(1)-- 进程管理概述

一、进程与线程 进程是处于执行期的程序,但是并不仅仅局限于一段可执行程序代码。通常,进程还要包含其他资源,像打开的文件,挂起的信号,内核内部数据,处理器状态,一个或多个具有内存映...

tongye
2018/08/20
0
0
linux用户空间和内核exit的语义--linux没有线程

如果你在程序中调用了exit,那么很显然你的程序会退出,可是至于为何会退出那就是库的事情了,我为什么说只是库的事情而不关linux内核的事情呢?那是因为linux内核根本不管用户空间的行为策略...

晨曦之光
2012/04/10
329
0
操作系统的理念—以windows和linux为例

linux的线程和进程并不区分,其实这完全是设计思想造成的,在linux中,其朴素地继承了unix的思想,就是“任务”代理用户操作“资源”,这里的任务就是进程,而资源就是文件,unix为了纯朴的不...

晨曦之光
2012/04/10
202
0
Android 多线程系统概述及与Linux系统的关系

线程系统的分类 1.1 操作系统内核实现了线程模型(核心型线程) - Windows - 线程与进程的多对多模型 线程效率比较高 Window Thread结构如下图所示: 1.2 操作系统核外实现的线程(用户进程)...

长平狐
2012/09/03
220
0
请教个进程和线程的问题

各位OSC的朋友: 大家好。 我被线程和进程有点整晕乎了,请朋友们指点下。 Linux中的多线程,是不是用轻量级进程来模拟线程,而这些进程有相同的Thread Group ID?那么实际上该线程还是个进程...

ChenQi
2012/04/02
740
14

没有更多内容

加载失败,请刷新页面

加载更多

利用mybatis generator生成实体类、Mapper接口以及对应的XML文件

项目中通常会遇到数据的持久化,如果是采用mybatis的orm,就会涉及到生成xml的问题,刚好mybatis官网提供了这么个插件MyBatis Generator,效果简直是棒呆。 1. 首先需要在build.gradle文件中...

啊哈关关
今天
2
0
SpringSocial相关的知识点

使用SprigSocial开发第三方登录 核心类 ServiceProvider(AbstractOauth2ServiceProvider):主要负责实现server提供商(例如QQ,微信等共有的东西),默认实现类是AbstractOauth2ServiceProvider...

chendom
今天
2
0
Java并发之AQS详解

一、概述   谈到并发,不得不谈ReentrantLock;而谈到ReentrantLock,不得不谈AbstractQueuedSynchronizer(AQS)!   类如其名,抽象的队列式的同步器,AQS定义了一套多线程访问共享资源...

群星纪元
昨天
2
0
Fabric-sdk-java最新教程

Fabric Java SDK是Fabric区块链官方提供的用于Java应用开发的SDK,全称为Fabric-sdk-java,网上可用资料不多,本文列出了精心整理的针对Fabric Java SDK的最新精选教程。 如果希望快速掌握F...

汇智网教程
昨天
3
0
react 子组件监听props 变化

componentWillReceiveProps //已经被废弃 getDerivedStateFromProps// 推荐使用//如果条件不存在必须要返回null static getDerivedStateFromProps(props, current_stat...

一箭落旄头
昨天
3
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部