others linux服务器运维 django3 监控 k8s golang 数据库 大数据 前端 devops 理论基础 java oracle 运维日志

进程、线程、fork、exec、互斥锁、自旋锁

访问量:1343 创建时间:2021-01-06

进程、线程

进程:各个进程的内存地址空间相互独立,只能通过请求操作系统内核帮助(CPU要切换到内核态)来完成进程间的通信。切换进程时需要保存\恢复进程进程的运行环境,需要切换内存地址空间开销大。 线程:进程是资源分配的基本单位,同一个进程下的各个线程共享(进程的资源)内存地址空间,可以直接通过读写内存空间进行通信。线程并发开销很小。进程并发开销很大。

引入线程后线程是cpu调度基本单位。

fork函数

描述: fork()通过复制调用进程创建一个新的进程。子进程是父进程的副本,子进程除了ID与父进程不同,其他都相同。

测试:

[root@oracledb ~]# vim forktest.c 
#include <stdio.h>
#include <unistd.h>
int main()
{
  fork();
  printf("fork test\n");
  return 0 ;
}
[root@oracledb ~]# gcc forktest.c
[root@oracledb ~]# ./a.out 
fork test
[root@oracledb ~]# fork test

根据返回值判断父子进程,让父子进程做不同的逻辑:

[root@oracledb ~]# vim forktest1.c 
#include <stdio.h>
#include <unistd.h>
int main()
{
  pid_t pid;
  pid=fork();
  if (pid>0) //parent
  {
    printf("parent\n");
  }
  if (pid==0) //son
  {
    printf("son\n");
  }
  if (pid<0)
  {
    printf("error\n");
  }
  printf("fork test\n");
  return 0 ;
}
[root@oracledb ~]# gcc forktest1.c 
[root@oracledb ~]# ./a.out 
parent
fork test
[root@oracledb ~]# son
fork test

程序执行完后,命令行提示符没有正常,是因为子进程结束后,子进程占用的资源没有被及时回收,成为僵尸进程,被init进程回收后恢复正常。可以在父进程添加wait函数等待子进程退出,父进程退出前先处理子进程。

[root@oracledb ~]# cp forktest1.c forktest2.c
#include <stdio.h>
#include <unistd.h>
int main()
{
  pid_t pid;
  pid=fork();
  if (pid>0) //parent
  {
    printf("parent\n");
    wait(NULL);
  }
  if (pid==0) //son
  {
    printf("son\n");
  }
  if (pid<0)
  {
    printf("error\n");
  }
  printf("fork test\n");
  return 0 ;
}
[root@oracledb ~]# gcc forktest2.c 
[root@oracledb ~]# ./a.out 
parent
son
fork test
fork test

exec函数

功能: exec()函数家族用新进程替换当前进程,并从新进程的main入口开始执行,无返回值,除非exec()发生错误返回-1。

example:

[root@oracledb ~]# vim exectest.c
#include <stdio.h>
#include <unistd.h>
int main()
{
  pid_t pid;
  pid=fork();
  if (pid>0) //parent
  {
    printf("parent\n");
    wait(NULL);
  }
  if (pid==0) //son
  {
    execlp("/bin/touch","touch","aaaas.txt",NULL);
    printf("son\n");
  }
  if (pid<0) 
  {
    printf("error\n");
  }
} 
[root@oracledb ~]# gcc exectest.c
[root@oracledb ~]# ./a.out
parent
[root@oracledb ~]# ls aaaas.txt
aaaas.txt

互斥锁

多线程并发访问同一资源,可能导致数据异常。通过互斥锁确保共享资源每次只能被一个线程访问。

例如商品剩余5件:

pthread_create参数: (1)pthread_t thread:事先创建好的pthread_t类型的参数。成功时tidp指向的内存单元被设置为新创建线程的线程ID。 (2)const pthread_attr_t attr:用于定制各种不同的线程属性。通常直接设为NULL。 (3)void (start_routine) (void ):新创建线程从此函数开始运行。 (4)void arg:函数的参数。无参数时设为NULL即可。有参数时输入参数的地址。当多于一个参数时应当使用结构体传入

pthread_join: 用于等待一个线程结束。

[root@oracledb ~]# vim threadtest.c
#include <stdio.h>
#include <pthread.h>
#define M 3

static tickets=5;
void * fun(void * args)
{
  while(tickets>0)
  {
    sleep(1);
    tickets--;
    printf("tickets:%d\n",tickets);
  }
  return NULL;
}

int main()
{
  pthread_t id[M];
  int i;
  for(i=0;i<M;i++)
    pthread_create(&id[i],NULL,fun,NULL);
  for(i=0;i<M;i++)
    pthread_join(id[i],NULL);
}
[root@oracledb ~]# gcc threadtest.c -pthread -o testpthread
[root@oracledb ~]# ./testpthread 
tickets:4
tickets:3
tickets:2
tickets:1
tickets:0
tickets:-1
tickets:-2

互斥锁使用:

[root@oracledb ~]# cp threadtest.c threadtest1.c 
[root@oracledb ~]# vim threadtest1.c 

#include <stdio.h>
#include <pthread.h>
#define M 3
pthread_mutex_t  mutex = PTHREAD_MUTEX_INITIALIZER;
static tickets=5;
void * fun(void * args)
{
  pthread_mutex_lock(&mutex);
  while(tickets>0)
  {
    sleep(1);
    tickets--;
    printf("tickets:%d\n",tickets);
  }
  pthread_mutex_unlock(&mutex);
  return NULL;
}

int main()
{
  pthread_t id[M];
  int i;
  for(i=0;i<M;i++)
    pthread_create(&id[i],NULL,fun,NULL);
  for(i=0;i<M;i++)
    pthread_join(id[i],NULL);
}
[root@oracledb ~]# gcc threadtest1.c -pthread -o testpthread
[root@oracledb ~]# ./testpthread 
tickets:4
tickets:3
tickets:2
tickets:1
tickets:0

自旋锁

互斥锁多线程:一个线程访问加锁的操作区域时,如果已经被其他线程加锁,则线程切换到其他线程执行。损耗上下文切换的性能。 自旋锁多线程:一个线程访问加锁的操作区域时,如果已经被其他线程加锁,则线程一直轮训访问锁,不切换线程。(nginx)

登陆评论: 使用GITHUB登陆