Linux进程线程学习笔记:进程创建

各位同学,转换下思维,这里说的是“进程”,不是“线程”,OK,我们开始

“进程”二字似乎总有那么些“只可意会不可言传”的韵味,维基百科是这样来解释的:

进程(英语:Process,台湾译作行程)是计算机中已运行程序的物理。进程本身不会运行,是线程的容器。程序本身只是指令的集合,进程才是程序(那些指令)的真正运行。若干进程有可能与同一个程序相关系,且每个进程皆可以同步(循序)或不同步(平行)的方式独立运行(多线程即每一个线程都代表一个进程)。现代计算机系统可在同一段时间内加载多个程序和进程到存储器中,并借由时间共享(或称多任务),以在一个处理器上表现出同时(平行性)运行的感觉。同样的,使用多线程技术的操作系统或计算机架构,同样程序的平行进程,可在多 CPU 主机或网络上真正同时运行(在不同的 CPU 上)。进程为现今分时系统的基本运作单位。

也有朋友如此来阐述, 

一个可以执行的程序;

和该进程相关联的全部数据(包括变量,内存空间,缓冲区等等); 

程序的执行上下文(execution context)

我更希望将这些简化一下(或许不太准确):指令和执行指令所需的环境,指令可以理解成“代码”,环境可以理解成“上下文”

 

系统用一个叫做“进程表”的东西来维护中系统中的进程,进程表中的一个条目维护着存储着一个进程的相关信息,比如进程号,进程状态,寄存器值等等...

当分配给进程A的“时间片”使用完时,CPU会进行上下文切换以便运行其他进程,比如进程B,这里所谓的“上下文切换”,主要就是在操作那个“进程表”,其将进程A的相关信息(上下文)保存到其对应的进程表项中, 与之相反,其会从对应于进程B的进程表项中读取相关信息并运行之。

那么,如果进程A新建了一个进程C呢?教程表会多这样一个表项,并且该表项拥有一个唯一的ID,也就是进程号(PID),进程表项的其他值大部分与进程A的相同,具体说来,就是C和A共享代码段,并且C将A的数据空间,堆栈等复制一份 ,然后从A创建C的地方开始运行。

A和C的相似度极大,除了以下方面(来自这里: http://opengroup.org/onlinepubs/007908775/xsh/fork.html ):

 

 

从代码角度来看,创建一个新进程的函数声明如下:

pid_tfork(void);

其包含在 unistd.h 头文件中,其中pid_t是表示“type ofprocess id”的32位整数, 至于函数的返回值,取决于在哪个进程中来检测该值,如果是在新创建的进程中,其为0;如果是在父进程中(创建新进程的进程),其为新创建的进程的id; 如果创建失败,则返回负值。

我们看下面的代码:

#include<stdio.h>
#include<unistd.h>
 
int main ()
{
    printf("app start...\n");
   
    pid_t id = fork();
   
    if (id<0) {
        printf("error\n");
    }else if (id==0) {
        printf("hi, i'm in new process, myid is %d \n", getpid());
    }else {
        printf("hi, i'm in old process,the return value is %d\n", id);
    }
   
    return 0;
}

为了方便理解,我在上面使用了getpid函数,其返回当前进程的id。

 

程序输出为:

app start...
hi, i'm in oldprocess, the return value is 5429
hi, i'm in newprocess, my id is 5429


另外,看到不少资料上说“fork函数是少数返回两个值的函数”,我不赞成该说法,我猜想,其之所以看上去有着不同的值,是系统创建新进程并复制父进程相关资源时,故意根据创建状态放入了不同的值。


fork函数失败的原因主要是没有足够的资源来进行创建或者进程表满,如果是非root权限的账户,则可能被管理员设置了最大进程数。一个用户所能创建的最大进程数限制是很重要的,否则一句代码就可能把主机搞当机:for(;;) fork();


再看下面的代码:

 

#include<stdio.h>
#include<unistd.h>
 
int main ()
{
    printf("app start...\n");
   
    int counter = 0;
   
    fork();
   
    counter++;
   
    printf("the counter value %d\n",counter);
   
    return 0;
}


输出如下:

app start...
the countervalue 1
the countervalue 1

 

之所以会这样,画个图就明白了:

 

并且,新进程得到的是父进程的副本,所以,父子进程counter变量不会相互影响。

 

再来一个demo:

#include<stdio.h>
#include<unistd.h>
 
int main ()
{
    printf("app start...");
   
    fork();
   
    return 0;
}

输出为:

app start...appstart...

好奇怪是吧?情况是这样的:

 

当你调用printf时,字符串被写入stdout缓冲区(还没刷到屏幕上的哦),然后fork,子进程复制了父进程的缓冲区,所以子进程的stdout缓冲区中也包含了“app start ...”这个字符串,然后父子进程各自运行,当他们遇到return语句时,缓冲器会被强制刷新,然后就分别将“app start...”刷到了屏幕上。如果想避免,在fork前,调用fflush强制刷新下缓冲区就可以了,在字符串后面加上“\n”也可以,因为stdout是按行缓冲的。


哈,大概就这么多,至于如何创建一个新进程以运行一个新程序,稍候我会谈exec函数,它们两者相结合就可以了~


转载自:http://www.cnblogs.com/zhouyinhui/archive/2010/09/01/1814954.html

你可能感兴趣的:(多线程,c,linux,网络,存储,任务)