进程与线程

来源:互联网 发布:九一三事件知乎 编辑:程序博客网 时间:2024/05/17 22:29

1:创建进程

linux进程控制-fork()

fork()函数          创建一个子进程

#include<sys/types.h> /* 提供类型pid_t的定义 */
#include<unistd.h> /* 提供函数的定义 */
pid_t fork(void);


/* fork_test.c */
#include<sys/types.h>
#inlcude<unistd.h>
main()
{
pid_t pid;

/*此时仅有一个进程*/
pid=fork();
/*此时已经有两个进程在同时运行*/
if(pid<0)
printf("error in fork!");
else if(pid==0)
printf("I am the child process, my process ID is %d\n",getpid());
else
printf("I am the parent process, my process ID is %d\n",getpid());
}


只看fork的名字,可能难得有几个人可以猜到它是做什么用的。fork系统调用的作用是复制一个进程。当一个进程调用它,完成后就出现两个几乎一模一样的进程,我们也由此得到了一个新进程。据说fork的名字就是来源于这个与叉子的形状颇有几分相似的工作流程。
Linux 中,创造新进程的方法只有一个,就是我们正在介绍的fork。其他一些库函数,如system(),看起来似乎它们也能创建新的进程,如果能看一下它们的源码就会明白,它们实际上也在内部调用了fork。包括我们在命令行下运行应用程序,新的进程也是由shell调用fork制造出来的。



看这个程序的时候,头脑中必须首先了解一个概念:在语句pid=fork()之前,只有一个进程在执行这段代码,但在这条语句之后,就变成两个进程在执行了,这两个进程的代码部分完全相同将要执行的下一条语句都是if(pid==0)…… 

两个进程中,原先就存在的那个被称作父进程,新出现的那个被称作子进程。父子进程的区别除了进程标志符(process ID)不同外,变量pid的值也不相同,pid存放的是fork的返回值。fork调用的一个奇妙之处就是它仅仅被调用一次,却能够返回两次,它可能有三种不同的返回值:

1. 在父进程中,fork返回新创建子进程的进程ID 

2. 在子进程中,fork返回0 

3. 如果出现错误,fork返回一个负值;

fork出错可能有两种原因:(1)当前的进程数已经达到了系统规定的上限,这时errno的值被设置为EAGAIN。(2)系统内存不足,这时errno的值被设置为ENOMEM

:2:创建线程

Linux系统下的多线程遵循POSIX线程接口,称为pthread。编写Linux下的多线程程序,需要使用头文件pthread.h,连接时需要使用库libpthread.a。顺便说一下,Linuxpthread的实现是通过系统调用clone()来实现的。clone()是Linux所特有的系统调用,它的使用方式类似fork


/* example.c*/

#include <stdio.h>

#include <pthread.h>

void thread(void)

{

int i;

for(i=0;i<3;i++)

printf("This is a pthread.\n");

}

 

int main(void)

{

pthread_t id;

int i,ret;

ret=pthread_create(&id,NULL,(void *) thread,NULL);

if(ret!=0){

printf ("Create pthread error!\n");

exit (1);

}

for(i=0;i<3;i++)

printf("This is the main process.\n");

pthread_join(id,NULL);

return (0);

}

gcc example1.c -lpthread -o example1

创建线程的函数的原型是:

extern int pthread_create __P ((pthread_t *__thread, __const pthread_attr_t *__attr,void *(*__start_routine) (void *), void *__arg));

第一个参数为指向线程标识符的指针,第二个参数用来设置线程属性,第三个参数是线程运行函数的起始地址,最后一个参数是运行函数的参数。这里,我们的函数thread不需要参数,所以最后一个参数设为空指针。第二个参数我们也设为空指针,这样将生成默认属性的线程。对线程属性的设定和修改我们将在下一节阐述。当创建线程成功时,函数返回0,若不为0则说明创建线程失败,

创建线程成功后,新创建的线程则运行参数三和参数四确定的函数,原来的线程则继续运行下一行代码。


函数pthread_join用来等待一个线程的结束。函数原型为:

extern int pthread_join __P ((pthread_t __th, void **__thread_return));

  第一个参数为被等待的线程标识符,第二个参数为一个用户定义的指针,它可以用来存储被等待线程的返回值。这个函数是一个线程阻塞的函数,调用它的函数将一直等待到被等待的线程结束为止,当函数返回时,被等待线程的资源被收回。一个线程的结束有两种途径,一种是象我们上面的例子一样,函数结束了,调用它的线程也就结束了;另一种方式是通过函数pthread_exit来实现






3:进程与线程区别

a:进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位。

b:每一个进程都有一个自己独立的地址空间,即进程空间或(虚空间)。比如在linux下面启动一个新的进程,系统必须分配给它独立的地址空间,建立众多的数据表来维护它的代码段、堆栈段和数据段,这是一种非常昂贵的多任务工作方式。而运行一个进程中的线程,它们之间共享大部分数据,使用相同的地址空间,因此启动一个线程,切换一个线程远比进程操作要快,花费也要小得多。当然,线程是拥有自己的局部变量和堆栈(注意不是堆)的,

c:进程至少有 5 种基本状态,它们是:初始态,执行态,等待状态,就绪状态,终止状态。

d:线程之间的通信比较方便。:统一进程下的线程共享数据(比如全局变量,静态变量),通过这些数据来通信不仅快捷而且方便,当然如何处理好这些访问的同步与互斥正是编写多线程程序的难点。而进程之间的通信只能通过进程通信的方式进行。

e;多进程比多线程程序要健壮:因为进程有自己的内存地址空间。而线程没有。

如果子线程的崩溃是由于自己的一亩三分地引起的,那就不会对主线程和其他子线程产生影响,但是如果子线程的崩溃是因为对共享区域造成了破坏,那么大家就一起崩溃了。

g:线程的执行与进程是有区别的:每个独立的线程有有自己的一个程序入口,顺序执行序列和程序的出口,但是线程不能独立执行,必须依附与程序之中由应用程序提供多个线程的并发控制。





1:进程:

多线程中的进程属性:

(1)作为资源分配的单位

(2):可包括多个线程

(3):进程不是一个课执行的实体。

特征:

(1)结构特征:

通常的程序时不能并发执行的。为使程序(含数据)能独立运行,应为之配置进程控制块(PCB)。

        程序段,相关的数据段和pcb构成了进程实体。(所谓创建进程是创建进程实体中的pcb)

(2)动态性:进程的实质是进程实的一次执行过程。

(3):并发性:多个进程实体同村于内存中,且能在一段时间内同时运行。

(4):独立性;

(5):异步性。

状态

创建状态:已有pcb,但无主存资源(未进入主存)不能被调度。

终止状态

就绪

运行

堵塞


2:线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。

线程具有以下属性。

(1)轻型实体

(2):独立调度和分配的基本单位。

(3):可并发执行

(4):共享进程资源。

(5)线程只有 3 个基本状态:就绪,执行,阻塞。





看这个程序的时候,头脑中必须首先了解一个概念:在语句pid=fork()之前,只有一个进程在执行这段代码,但在这条语句之后,就变成两个进程在执行了,这两个进程的代码部分完全相同将要执行的下一条语句都是if(pid==0)…… 

两个进程中,原先就存在的那个被称作父进程,新出现的那个被称作子进程。父子进程的区别除了进程标志符(process ID)不同外,变量pid的值也不相同,pid存放的是fork的返回值。fork调用的一个奇妙之处就是它仅仅被调用一次,却能够返回两次,它可能有三种不同的返回值:

1. 在父进程中,fork返回新创建子进程的进程ID 

2. 在子进程中,fork返回0 

3. 如果出现错误,fork返回一个负值;

fork出错可能有两种原因:(1)当前的进程数已经达到了系统规定的上限,这时errno的值被设置为EAGAIN。(2)系统内存不足,这时errno的值被设置为ENOMEM

1 0