进程,线程,互斥锁,信号量

1.进程:

子进程几乎继承了父进程中所有的内容,包括变量,打开的文件等等,但是他们有着独立的地址空间,也就是说在子进程或者父进程中改变相同的那个变量,对另一个进程是没有影响的。

  1. 父进程先结束,子进程会变成孤儿进程,系统会让孤儿进程被init进程收养,这个init进程是内核启动以后创建的第一个用户态进程,进程号是1,也就是说孤儿进程的进程号都是1 ,且会变成后台进程。
  2. 若子进程先结束,父进程没有及时回收,那么子进程会变成僵尸进程
  3. 结束一个进程用exit,并刷新流的缓冲区
  4. int execl(const char *path, const char *arg, …);当使用fork函数创建一个新的进程以后,可以使用execl函数去执行另一个程序,该进程的内容会完全被新的程序替代。因为调用exec函数并不创建新进程,所以前后进程的ID并没有改变。第一个参数是程序的路径,以后的参数都是给这个程序传递的参数,最后的参数是NULL,失败返回-1.
1.if(execl("./test","test",NULL) == -1)
{
        printf("execl failed!\n");      
}
2.char *arg[] = {"./test", "test",NULL};
if(execv("/bin/ls","-a","-l",NULL) == -1)//还要一种execv就是把参数以数组的形式传递
{
        printf("execl failed!\n");      
}
  1. system函数会自动创建一个子进程去执行参数中的程序,等待子进程执行完毕后父进程才会继续执行后面的代码.而前面的exec函数不会执行后面的代码,而是覆盖当前进程。
  2. pid_t wait(int *status);使用wait函数回收子进程,如果子进程没有结束,那么父进程会阻塞。如果有多个子进程,那么哪个子进程先结束就回收哪一个.status保存子进程的返回值和结束方式,如果不需要直接设置为NULL。传参的时候定义一个整形变量然后传地址,不要直接定义整形指针传递
  3. 进程的结束方式有两种,一种正常结束,一种非正常结束。正常结束就是exit,_exit,return的方式来结束,这些函数都有返回值,wait函数会接收到这些返回值。非正常的结束方式就是接收到一个信号然后结束了。
  4. 注意fork在父进程中返回子进程的PID号码,子进程中返回0,也就是说,if(pid > 0)中的pid是子进程的pid号,而不是父进程的pid
  5. 除此以外,还可以通过waitpid函数来回收子进程
pid_t waitpid(pid_t pid,int *status,int option);
成功的时候返回子进程的pid,子进程还没结束返回0,失败返回-1.
pid指定回收对象,如果为-1则回收任意一个子进程
status跟wait函数的参数一样
option指得是回收方式,0或者WNOHANG,0就是阻塞,WNOHANG就是非阻塞,如果子进程没结束函数就会返回0

2. 线程:

为什么需要线程呢?因为进程的地址空间是独立的,当进程时间片执行完了或者进入等待态后,系统要去调度,开销较大。而同一个进程中的线程共享相同的地址空间,就缓解了这种情况,当然不同进程中的线程还是一样的,地址空间不相同。

  1. 线程的使用
1.线程的创建使用函数pthread_create();
2.回收使用函数pthread_join(),如果要回收的线程没有结束则调用的线程会阻塞;
3.结束当前的进程使用函数pthread_exit()或者return,注意这里不可以使用exit,否则当前进程中的所有线
程都会结束
  1. 线程间的机制:线程间的通信很容易,可以直接通过全局变量交换数据,但是多个线程访问共享数据的时候需要同步或者互斥的机制

3. 线程中的同步机制

同步:多个任务按照约定的次序完成某件事,采用信号量的概念来解决这个问题,什么是信号量呢,信号量代表某种资源,其值代表系统中该类资源的数量。信号量只可以采用三种方式来访问,初始化,P操作(申请资源),V操作(释放资源)
P操作:当线程需要访问某种资源,它不确定这种资源是否还有,就使用P操作,P操作去检查信号量的值,如果大于0,那么就执行动作,如果等于0,那么就阻塞,直到大于0再来执行
这是posix信号量,用于相同进程的不同线程之间

if(信号量的值大于0){动作执行;信号量的值减一;}
else{阻塞}

V操作:当任务产生了一个资源或者不再占据某个资源,就使用V操作,告诉操作系统,可以唤醒那些等待的任务了

信号量的值加一;
if(有任务在等待){唤醒任务}

举例应用

典型的生产者消费者问题:两个线程同步读写缓冲区,读线程就是消费者,写线程就是生产者
void *func(void *arg);

char buf[32] = {0};

sem_t r;//代表是否有可读的缓冲区
sem_t w;//代表是否有可写的缓冲区,防止还没读就再次写

int main(void)
{
    pthread_t tid;

    //第二个参数0代表进程内部的线程间通信,1代表进程间
    //最后一个参数0代表初值为0
    //初始化要放在线程创建之前
    sem_init(&r, 0, 0);
    sem_init(&w, 0, 1);
    do
    {
	//写之前检测一下是否有可以写的缓冲区
	sem_wait(&w);
	pthread_create(&tid, NULL, func, NULL);
    fgets(buf, 32, stdin);
	//写之后执行V操作,信号量的值加一
	 sem_post(&r);
    }while(strncmp(buf, "quit", 4) != 0);

    return 0;
}
void *func(void *arg)
{
    while(1)
    {
	//读之前先进行一个P操作,检测一下,等于0则阻塞
	sem_wait(&r);
	printf("You enter %s\n", buf);
	//读之后执行V操作,信号量的值加一,可以写了,唤醒写线程
	sem_post(&w);
    }
}

信号量可以用于进程和线程,另外一种同步机制叫做条件变量,可在本专栏中搜索到

4.线程中的互斥机制

互斥的意思就是在同一时刻只允许一个任务(进程,线程)访问共享资源,Linux中主要采用互斥锁的方式来实现。即访问之前先申请锁,访问之后再释放锁。可以理解为进入房间之后就将门反锁,别人也就进不去了,要出来再把锁打开,保证同一时刻只有一个人在房间里。

1.互斥锁的初始化:pthread_mutex_init
int pthread_mutex_init(pthread_mutex_t *mptr, const pthread_mutexattr_t *attr);
mutex指向要初始化的锁对象,attr代表互斥锁属性,NULL为缺省属性,成功返回0,失败返回错误码
2.申请上锁
int pthread_mutex_lock(pthread_mutex_t *mptr);
mutex指向上锁对象,成功返回0,失败返回错误码
如果无法获得锁,会阻塞,直到能够获得锁为止
3.int pthread_mutex_unlock(pthread_mutex_t *mptr);
mutex指向要释放锁的对象,成功返回0,失败返回错误码
在访问完共享资源之后要即使释放锁,否则会一直持有,使别的任务无法访问该资源

代码示例

主线程每次都将count加一,然后赋值给value1和value2,子线程比较两个值是否相等,不相等就打印。
value1,value2是共享资源,在访问value1和value2的时候加锁,访问完之后解锁。这样保证value1和value2
始终相等。如果不加锁,那么就无法保证主线程每次都能给两个变量都加上1,那就可能会导致变量的值不相等,
但是加锁之后,只有主线程访问完两个变量,也就是都加上1之后,子线程才会访问,保证了两个值始终相等,也
就不会打印出来
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>

#define LOCK

unsigned int count;
unsigned int value1, value2;
pthread_mutex_t lock;

void *func(void *arg)
{
    while(1)
    {
#ifdef LOCK
	pthread_mutex_lock(&lock);
#endif
	if(value1 != value2)
	{
	    printf("value1 = %u\t,value2 = %u\n",value1, value2);
	    usleep(100000);
	}
#ifdef LOCK
	pthread_mutex_unlock(&lock);
#endif
    }
    return NULL;
}

int main(void)
{
    pthread_t p;

    if(pthread_mutex_init(&lock,NULL) != 0)
    {
	perror("pthread_mutex_init\n");
	exit(-1);
    }

    if(pthread_create(&p, NULL, func, NULL) != 0)
    {
	perror("pthread_create\n");
	exit(-1);
    }

    while(1)
    {
	count++;
#ifdef LOCK
	pthread_mutex_lock(&lock);
#endif
	value1 = count;
	value2 = count;
#ifdef LOCK
	pthread_mutex_unlock(&lock);
#endif
    }
    return 0;
}

互斥锁仅在线程中使用

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值