wait和waitpid的区别
wait是waitpid的特殊形式。
pid_t waitpid(pid_t pid,int *status,int options)这是waitpid的函数原型
首先第一个参数pid表示输入一个进程的pid,同时这里有多种情况:
1、pid>0,表示等待指定的pid的子进程退出,回收其资源;
2、pid=-1,表示等待任意一个子进程退出,回收其资源,此时就和wait一样了
3、pid=0,表示等待同一个进程组的任何子进程,如果其中一个子进程中途变成其他进程组了,那么此时不会再将其作为回收服务对象;
4、pid<-1,表示等待执行|pid|绝对值指定的进程组中的任意子进程退出,回收其资源。
status就是表示状态返回值,例如子进程中调用_exit(3);那么父进程中的status>>8之后将会得到值3。status这个整数型指针指向的内存空间是父子进程之间交互的地方,所以子进程的退出值也将存储在在status指向的内存地址中,但是内核的最低8bit是用来供信号使用的,所以信号最多有127个。返回值存储在高8bit,注意只提供了8bit来存储返回值。
_exit(255)在父进程中status>>8得到的值为255.
_exit(256)在父进程中status>>8得到的值为0.
_exit和exit使用的注意事项
平时在fork一个子进程后,尽量使用_exit,而非使用exit退出。
这是因为调用exit函数会调用用户自定义的清除程序atexit函数,在atexit中的函数可以修改进程的最终返回值。而_exit仅仅为进程实施内核清除工作。
例子如下:我们得到的返回值并不是0
#include <stdio.h>
#include <stdlib.h>
void end(void)
{
exit(-1);
}
int main()
{
int pid = 0;
atexit(end);
pid = fork();
if (pid == 0) {
exit(0);
} else if (pid > 0) {
int status = 0;
wait(&status);
printf("%d\n", status);
}
return 0;
}
同样,我们在使用标准输入输出时,如下情况中,我们将会惊奇地得到输出结果为headoolheadool,我们都知道调用fork会创建一个镜像子进程,而printf标准输出一般只有在遇到\n,主动调用flush函数或者缓存文件被关闭时才会被输出到stdout设备文件中。而此时父进程和子进程的io缓存文件中的数据此时均还没有输出到stdout中去,当父子进程分别退出时关闭io缓存文件,此时各自均会输出到stdout设备文件中去,这个时候我们就会看到文字被输出了2遍,由于父进程睡眠了2s可见,先是子进程输出一遍,后续再是父进程输出一遍。
#include <stdio.h>
#include <stdlib.h>
int main()
{
int pid = 0;
printf("headool");
pid = fork();
if (pid == 0) {
exit(0);
} else if (pid > 0) {
int status = 0;
wait(&status);
sleep(2);
}
return 0;
}
进程和线程的同步
首先需要确认一点就是,fork出来的是进程,而通过pthread_create出来的是线程。进程之间并不共享地址空间,而同个进程中线程共享同一个地址空间。因此这进程间的通信方式开销更大。进程之间的通信可以通过管道pipe、共享内存mmap、信号signals等方式,这些通信方式往往开销比较大。线程的通信方式就比较简单,直接通过共享的堆来进行通信。
我们学习的信号量是用于同步使用,在进程和线程同步也都可以使用信号量进行同步,但是这两个信号量是存在差别的。我们都知道管道由分为有名管道和匿名管道。而信号量也是如此,分为有名信号量和匿名信号量。有名信号量一般是用于进程之间的同步互斥,要求创建一个文件。而匿名信号量是保存在内存中,用于线程之间的同步互斥。
需要以下头文件:
#include <fcntl.h>
#include <sys/stat.h>
#include <semaphore.h>
当有名信号量存在时使用:
sem_t *sem_open(const char *name, int oflag);
当有名信号量不存在时使用,创建一个有名信号量:
sem_t *sem_open(const char *name, int oflag, mode_t mode, unsigned int value);
参数介绍
name:信号量文件名。注意,不能指定路径名。因为有名信号量,默认放在/dev/shm 里
flags:sem_open() 函数的行为标志。
mode:文件权限(可读、可写、可执行)的设置。
value:信号量初始值。
返回值:成功:信号量的地址 失败:SEM_FAILED
int sem_close(sem_t *sem);
功能:关闭有名信号量。
参数:sem:指向信号量的指针。
返回值:成功:0 失败:-1
int sem_unlink(const char *name);
功能:删除有名信号量的文件。
参数:name:有名信号量文件名。
返回值:成功:0 失败:-1
而匿名信号量的使用就比较简单,可以通过 sem_init
函数创建,函数说明如下:
int sem_init(sem_t *sem, int pshared, unsigned int value);
返回值:若成功,返回 0 ;若出错,返回-1
pshared 参数指示该信号量是被一个进程的多个线程共享还是被多个进程共享。
如果 pshared 的值为 0 ,那么信号量将被单进程中的多线程共享,并且应该位于某个地址,该地址对所有线程均可见(例如,全局变量或变量在堆上动态分配)。
如果 pshared 非零,那么信号量将在进程之间共享,并且信号量应该位于共享内存区域。
如果无名信号量使用完成,可以调用 sem_destory
函数销毁该信号量。