1.进程
进程是一个独立可调度的任务,在系统执行某个程序时会分配和释放许多资源,进程是一个程序的一次执行过程,进程是程序执行和资源管理的最小单位。与进程相关的概念:进程包含 了正文段: (指令, c语言中的语句)就是elf的.text段,用户数据段: (全局变量,const修饰的常量,字符串, static 修饰的局部变量)在进程中叫静态区,静态生存期,系统数据段: 堆区 : 动态内存区, malloc 申请内存的区域栈区 : 函数内部定义的局部变量, 在函数被调用时, 会创建这些局部变量, 在栈区创建,函数调用结束后所有的局部变量都会被销毁, 动态生存期。进程标识 :进程号PID,父进程号PPID,PID唯一标识一个进程。进程的类型 :交互进程:该类进程是由shell控制和运行的。交互进程既可以在前台运行,也可以在后台运行。例如: 正常的程序 批处理进程:该类进程不属于某个终端,它被提交到一个队列中以便顺序执行。例如: shell脚本程序守护进程:该类进程在后台运行。它一般在Linux启动时开始执行,系统关闭时才结束。例如: window中的服务。进程运行的状态 :运行态:此时进程或者正在运行,或者准备运行。等待态:此时进程在等待一个事件的发生或某种系统资源。可中断不可中断停止态:此时进程被中止。死亡态(僵尸态):这是一个已终止的进程,但还在进程向量数组中占有一个task_struct结构。任务调度的算法 :原则: 微观上串行, 宏观上并行。时间片轮转的算法对系统的进程进行调度 。进程的模式:进程的执行模式分为用户模式和内核模式。
了解了进程相关的概念,我们来看看与进程相关的一些函数的使用和程序的实现:
cpp
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
int main(int argc, char const *argv[])
{
pid_t pid = fork();
if(pid < 0)
{
perror("fork");
exit(-1);
}
else if(pid == 0) //子进程
{
printf("child: pid=%d\n",getpid());
printf("child:ppid=%d\n",getppid());
while(1)
{
printf("child is running\n");
sleep(1);
}
}
else//父进程
{
printf("parent: pid=%d\n",getpid());
printf("parent:ppid=%d\n",getppid());
while(1)
{
printf("parent is running\n");
sleep(1);
}
}
return 0;
}
该段程序用来创建一个父子进程在进程内输出相应的内容到终端。fork:创建一个子进程,返回值父进程返回子进程的进程号,子进程返回0,失败返回-1.getpid:获取当前进程的进程号,返回值获取到的进程号。getppid:获取当前进程父进程的进程号,返回值获取的进程号。sleep:让当前进程暂停执行一段指定的时间,参数需要暂停的秒数。
cpp
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
int main(int argc, char const *argv[])
{
int count = 100;
pid_t pid = fork();
if(pid < 0)
{
perror("fork");
exit(-1);
}
else if(pid == 0) //子进程
{
printf("child: pid=%d\n",getpid());
printf("child:ppid=%d\n",getppid());
while(1)
{
count ++;
printf("child is running:%d\n",count);
sleep(1);
}
}
else//父进程
{
printf("parent: pid=%d\n",getpid());
printf("parent:ppid=%d\n",getppid());
while(1)
{
count --;
printf("parent is running:%d\n",count);
sleep(1);
}
}
return 0;
}
该段程序用来验证fork后该段程序变成了两个进程其中的变量属于各自进程。
cpp
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
int main(int argc, char const *argv[])
{
pid_t pid = fork();
if (pid < 0)
{
perror("fork");
exit(-1);
}
else if (pid == 0) // 子进程
{
printf("child: pid=%d\n", getpid());
printf("child:ppid=%d\n", getppid());
sleep(1);
execl("/home/linux/work/02-linuxapp/33-myls","33-myls","-l","/home/linux",NULL);
exit(0);
}
else // 父进程
{
printf("parent: pid=%d\n", getpid());
printf("parent:ppid=%d\n", getppid());
while (1)
{
printf("parent is running\n");
sleep(1);
}
}
return 0;
}
该段程序是在该进程中执行另一段程序。execl:执行指定的可执行文件,第一个参数:文件路径名,第二个参数:要执行的程序,后面的参数:要执行该文件需要的参数,以NULL作为最后一个参数。
cpp
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
int main(int argc, char const *argv[])
{
pid_t pid = fork();
if (pid < 0)
{
perror("fork");
exit(-1);
}
else if (pid == 0) // 子进程
{
printf("child: pid=%d\n", getpid());
printf("child:ppid=%d\n", getppid());
sleep(1);
//不需要使用路径,可以直接使用环境变量里的可执行程序
execlp("ls","ls","-l","/home/linux",NULL);
exit(0);
}
else // 父进程
{
printf("parent: pid=%d\n", getpid());
printf("parent:ppid=%d\n", getppid());
while (1)
{
printf("parent is running\n");
sleep(1);
}
}
return 0;
}
该段程序是在一个进程中执行一个系统环境变量可执行程序。execlp:执行指定的环境变量中的可执行文件,第一个参数要执行的文件名,第二个参数要执行的程序名,后面的参数执行可执行文件需要的参数,以NULL作为最后一个参数。
cpp
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
int main(int argc, char const *argv[])
{
pid_t pid = fork();
if (pid < 0)
{
perror("fork");
exit(-1);
}
else if (pid == 0) // 子进程
{
printf("child: pid=%d\n", getpid());
printf("child:ppid=%d\n", getppid());
sleep(1);
//后面的参数可以全部放在一个字符串指针数组里面
char* const arg[] = {"33-myls","-l","/home/linux",NULL};
execv("/home/linux/work/02-linuxapp/33-myls",arg);
exit(0);
}
else // 父进程
{
printf("parent: pid=%d\n", getpid());
printf("parent:ppid=%d\n", getppid());
while (1)
{
printf("parent is running\n");
sleep(1);
}
}
return 0;
}
该段程序也是在一个进程中执行一个可执行文件。execv:执行指定可执行文件,第一个参数文件路径名,第二个参数要执行的程序和所需要的参数都放在里面的一个数组。
cpp
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
int main(int argc, char const *argv[])
{
pid_t pid = fork();
if (pid < 0)
{
perror("fork");
exit(-1);
}
else if (pid == 0) // 子进程
{
printf("child: pid=%d\n", getpid());
printf("child:ppid=%d\n", getppid());
sleep(1);
//不需要使用路径,可以直接使用环境变量里的可执行程序
//且后面的参数都可以放到一个字符串指针数组里
char* const arg[] = {"ls","-l","/home/linux",NULL};
execvp("ls",arg);
exit(0);
}
else // 父进程
{
printf("parent: pid=%d\n", getpid());
printf("parent:ppid=%d\n", getppid());
while (1)
{
printf("parent is running\n");
sleep(1);
}
}
return 0;
}
该段程序是在一个进程中执行一个可执行文件。execvp:执行指定的可执行文件,第一个参数要执行的环境变量里的可执行程序,第二个参数要执行的程序和所需要的参数都放入的一个数组。
cpp
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
int main(int argc, char const *argv[])
{
pid_t pid = fork();
if (pid < 0)
{
perror("fork");
exit(-1);
}
else if (pid == 0) // 子进程
{
printf("child: pid=%d\n", getpid());
printf("child:ppid=%d\n", getppid());
sleep(5);
exit(0); //子进程退出,父进程还在,但是父进程并没有回收子进程所以产生僵尸态
}
else // 父进程
{
printf("parent: pid=%d\n", getpid());
printf("parent:ppid=%d\n", getppid());
while (1)
{
printf("parent is running\n");
sleep(1);
}
}
return 0;
}
该段程序用来产生一个僵尸态进程。当子进程退出父进程还在,但是父进程没有回收子进程就会产生僵尸态。
cpp
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
int main(int argc, char const *argv[])
{
pid_t pid = fork();
if (pid < 0)
{
perror("fork");
exit(-1);
}
else if (pid == 0) // 子进程
{
printf("child: pid=%d\n", getpid());
printf("child:ppid=%d\n", getppid());
sleep(5);
printf("child is exit");
exit(0);
}
else // 父进程
{
printf("parent: pid=%d\n", getpid());
printf("parent:ppid=%d\n", getppid());
pid_t child_pid = wait(NULL);
printf("child_pid=%d\n",child_pid);
while (1)
{
printf("parent is running\n");
sleep(1);
}
}
return 0;
}
该程序用来探究子进程产生僵尸态。wait:阻塞该进程当子进程退出或者该进程接收到信号后该函数就不再阻塞,参数指向的对象用来保存子进程退出时的状态,返回值子进程的进程号。
cpp
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/stat.h>
int main(int argc, char const *argv[])
{
pid_t pid = fork();
if (pid < 0)
{
perror("fork");
exit(-1);
}
else if (pid > 0) //父进程
{
//1.创建子进程父进程退出
exit(0);
}
else // 子进程
{
//2.子进程创建新会话,独立于终端
setsid();
//3.改变目录为根目录,防止目录被删除后出现的错误
chdir("/tmp");
//4.重设文件权限,创建文件时,权限不受限
umask(0);
//5.把所有已经打开的文件描述符关闭,防止错误
for (int i = 0; i < getdtablesize(); i++)
{
close(i);
}
while(1)
{
system("date >> log");
sleep(1);
}
}
return 0;
}
该段程序用来创建一个守护进程。setsid:创建一个新会话。 getdtablesize:获取当前进程打开文件描述符最大数量,返回值返回获取的数量。
2.线程
线程是操作系统进行调度的基本单位,也是程序执行的最小单位。它是进程内的一个执行流,共享进程的大部分资源。一个进程中的多个线程共享以下资源:可执行的指令,静态数据,进程中打开的文件描述符,信号处理函数,当前工作目录用户ID用户组ID,每个线程私有的资源如下:线程ID (TID),PC(程序计数器)和相关寄存器,堆栈,局部变量,返回地址,错误号 (errno),信号掩码和优先级,执行状态和属性。线程通过第三方库(NPTL)来实现,NPTL线程库中提供了如下基本操作:创建线程 pthread_create, 回收线程 pthread_join,退出线程 pthread_exit,取消线程 pthread_cancel,线程间同步和互斥机制:信号量互斥锁条件变量。
下面我们来看看与线程相关的函数和程序实现:
cpp
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
void* pthread_handler1(void* arg)
{
printf("pthtread_handler1:arg=%d\n",*(int*)arg);
while(1)
{
printf("pthread_handler1 is running\n");
sleep(1);
}
}
void* pthread_handler2(void* arg)
{
printf("pthtread_handler2:arg=%d\n",*(int*)arg);
while(1)
{
printf("pthread_handler2 is running\n");
sleep(1);
}
}
void* pthread_handler3(void* arg)
{
printf("pthtread_handler3:arg=%d\n",*(int*)arg);
while(1)
{
printf("pthread_handler3 is running\n");
sleep(1);
}
}
int main(int argc, char const *argv[])
{
pthread_t ptd1,ptd2,ptd3;
int data1=100,data2=200,data3=300;
int ret = pthread_create(&ptd1,NULL,pthread_handler1,&data1);
if(ret != 0)
{
printf("pthread_create error\n");
exit(-1);
}
ret = pthread_create(&ptd2,NULL,pthread_handler2,&data2);
if(ret != 0)
{
printf("pthread_create error\n");
exit(-1);
}
ret = pthread_create(&ptd3,NULL,pthread_handler3,&data3);
if(ret != 0)
{
printf("pthread_create error\n");
exit(-1);
}
while(1)
{
printf("main_pthread is running\n");
sleep(1);
}
return 0;
}
该段程序是创建三个线程三个线程分别将要显示的内容输出终端。pthread_create:创建一个线程,第一个参数创建线程的线程号,第二个参数线程的属性一般设置为NULL,第三个参数创建线程要调用的函数,第四个参数调用函数所需的参数,返回值成功返回0失败返回错误号。
cpp
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
void *pthread_handler1(void *arg)
{
static int retval = 0; // 默认正常退出
printf("pthtread_handler1:arg=%d\n", *(int *)arg);
printf("pthread_handler1 is running\n");
sleep(10);
pthread_exit(&retval);
}
void *pthread_handler2(void *arg)
{
static int retval = -1; // 默认失败退出
printf("pthtread_handler2:arg=%d\n", *(int *)arg);
printf("pthread_handler2 is running\n");
sleep(10);
pthread_exit(&retval);
}
void *pthread_handler3(void *arg)
{
printf("pthtread_handler3:arg=%d\n", *(int *)arg);
while (1)
{
printf("pthread_handler3 is running\n");
sleep(1);
}
}
int main(int argc, char const *argv[])
{
pthread_t ptd1, ptd2, ptd3;
int data1 = 100, data2 = 200, data3 = 300;
int ret = pthread_create(&ptd1, NULL, pthread_handler1, &data1);
if (ret != 0)
{
printf("pthread_create error\n");
exit(-1);
}
ret = pthread_create(&ptd2, NULL, pthread_handler2, &data2);
if (ret != 0)
{
printf("pthread_create error\n");
exit(-1);
}
ret = pthread_create(&ptd3, NULL, pthread_handler3, &data3);
if (ret != 0)
{
printf("pthread_create error\n");
exit(-1);
}
printf("ptd1:%ld\n",ptd1);
printf("ptd1:%ld\n",ptd2);
printf("ptd1:%ld\n",ptd3);
sleep(5);
pthread_cancel(ptd3); //取消线程3
void* retval;
pthread_join(ptd1,&retval);//回收线程1
pthread_join(ptd2,&retval);//回收线程2
while (1)
{
printf("main_pthread is running\n");
sleep(1);
}
return 0;
}
该段程序是演示线程的退出取消和回收。pthread_exit:退出线程,参数线程退出要返回的内容。pthread_cancel:取消线程,参数要取消的线程的线程号。pthread_join:回收线程,第一个参数要回收的线程的线程号,第二个参数线程的返回值。
cpp
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <string.h>
#define Nsize 64
char buf[Nsize+1];
void *pthread_handler1(void *arg)
{
printf("pthtread_handler1:arg=%d\n", *(int *)arg);
while(1)
{
memset(buf,'O',32);
usleep(1000);
memset(buf+32,'O',32);
usleep(1000);
}
}
void *pthread_handler2(void *arg)
{
printf("pthtread_handler2:arg=%d\n", *(int *)arg);
while(1)
{
memset(buf,'-',32);
usleep(1000);
memset(buf+32,'-',32);
usleep(1000);
}
}
void *pthread_handler3(void *arg)
{
printf("pthtread_handler3:arg=%d\n", *(int *)arg);
while (1)
{
printf("%s\n",buf);
sleep(1);
}
}
int main(int argc, char const *argv[])
{
pthread_t ptd1, ptd2, ptd3;
int data1 = 100, data2 = 200, data3 = 300;
int ret = pthread_create(&ptd1, NULL, pthread_handler1, &data1);
if (ret != 0)
{
printf("pthread_create error\n");
exit(-1);
}
ret = pthread_create(&ptd2, NULL, pthread_handler2, &data2);
if (ret != 0)
{
printf("pthread_create error\n");
exit(-1);
}
ret = pthread_create(&ptd3, NULL, pthread_handler3, &data3);
if (ret != 0)
{
printf("pthread_create error\n");
exit(-1);
}
printf("ptd1:%ld\n",ptd1);
printf("ptd1:%ld\n",ptd2);
printf("ptd1:%ld\n",ptd3);
while (1)
{
printf("%s\n",buf);
sleep(1);
}
return 0;
}
该段程序是演示线程间使用共享资源出现的问题,即线程间写入内容到数组内并没有完成写完就会调度到另一个线程执行输出数组内容有两个线程成分的问题。
cpp
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <string.h>
#include <semaphore.h>
#define Nsize 64
char buf[Nsize+1];
sem_t sem; //定义一个信号量
void *pthread_handler1(void *arg)
{
printf("pthtread_handler1:arg=%d\n", *(int *)arg);
while(1)
{
sem_wait(&sem); //P操作,申请一个信号量
memset(buf,'O',32);
usleep(1000);
memset(buf+32,'O',32);
usleep(1000);
sem_post(&sem); //V操作,释放一个信号量
}
}
void *pthread_handler2(void *arg)
{
printf("pthtread_handler2:arg=%d\n", *(int *)arg);
while(1)
{
sem_wait(&sem); //P操作,申请一个信号量
memset(buf,'-',32);
usleep(1000);
memset(buf+32,'-',32);
usleep(1000);
sem_post(&sem); //V操作,释放一个信号量
}
}
void *pthread_handler3(void *arg)
{
printf("pthtread_handler3:arg=%d\n", *(int *)arg);
while (1)
{
sem_wait(&sem); //P操作,申请一个信号量
printf("%s\n",buf);
sem_post(&sem); //V操作,释放一个信号量
sleep(1);
}
}
int main(int argc, char const *argv[])
{
sem_init(&sem,0,1); //初始化一个信号量
int sval = 0;
sem_getvalue(&sem,&sval); //获得信号量的值
printf("sval=%d\n",sval);
pthread_t ptd1, ptd2, ptd3;
int data1 = 100, data2 = 200, data3 = 300;
int ret = pthread_create(&ptd1, NULL, pthread_handler1, &data1);
if (ret != 0)
{
printf("pthread_create error\n");
exit(-1);
}
ret = pthread_create(&ptd2, NULL, pthread_handler2, &data2);
if (ret != 0)
{
printf("pthread_create error\n");
exit(-1);
}
ret = pthread_create(&ptd3, NULL, pthread_handler3, &data3);
if (ret != 0)
{
printf("pthread_create error\n");
exit(-1);
}
printf("ptd1:%ld\n",ptd1);
printf("ptd1:%ld\n",ptd2);
printf("ptd1:%ld\n",ptd3);
while (1)
{
sem_wait(&sem); //P操作,申请一个信号量
printf("%s\n",buf);
sem_post(&sem); //V操作,释放一个信号量
sleep(1);
}
return 0;
}
该程序用无名信号量来使线程互斥来解决线程间使用共享资源的问题。sem_init:初始化一个无名信号量,第一个参数要初始化的信号量,第二个参数用来决定该信号量在线程间使用还是非线程间使用,0线程间使用,1非线程间使用,第三个参数信号量的值。sem_getvalue:获取无名信号量的值,第一个参数要获取的信号量,第二个参数获取的信号量的值。sem_wait:申请一个信号量,参数要申请的信号量。sem_post:释放一个信号量,参数要释放的信号量。
cpp
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <string.h>
#include <semaphore.h>
#define Nsize 64
char buf[Nsize+1];
sem_t sem_w1,sem_w2,sem_r1,sem_r2; //定义信号量
void *pthread_handler1(void *arg)
{
printf("pthtread_handler1:arg=%d\n", *(int *)arg);
while(1)
{
sem_wait(&sem_w1); //P操作,申请一个信号量
memset(buf,'O',32);
usleep(1000);
memset(buf+32,'O',32);
usleep(1000);
sem_post(&sem_r1); //V操作,释放一个信号量
}
}
void *pthread_handler2(void *arg)
{
printf("pthtread_handler2:arg=%d\n", *(int *)arg);
while(1)
{
sem_wait(&sem_w2); //P操作,申请一个信号量
memset(buf,'-',32);
usleep(1000);
memset(buf+32,'-',32);
usleep(1000);
sem_post(&sem_r2); //V操作,释放一个信号量
}
}
void *pthread_handler3(void *arg)
{
printf("pthtread_handler3:arg=%d\n", *(int *)arg);
while (1)
{
sem_wait(&sem_r1); //P操作,申请一个信号量
printf("%s\n",buf);
sem_post(&sem_w2); //V操作,释放一个信号量
sleep(1);
}
}
int main(int argc, char const *argv[])
{
sem_init(&sem_w1,0,1); //初始化信号量
sem_init(&sem_w2,0,0); //初始化信号量
sem_init(&sem_r1,0,0); //初始化信号量
sem_init(&sem_r2,0,0); //初始化信号量
int sval = 0;
sem_getvalue(&sem_w1,&sval); //获得信号量的值
printf("sval=%d\n",sval);
sem_getvalue(&sem_w2,&sval); //获得信号量的值
printf("sval=%d\n",sval);
sem_getvalue(&sem_r1,&sval); //获得信号量的值
printf("sval=%d\n",sval);
sem_getvalue(&sem_r2,&sval); //获得信号量的值
printf("sval=%d\n",sval);
pthread_t ptd1, ptd2, ptd3;
int data1 = 100, data2 = 200, data3 = 300;
int ret = pthread_create(&ptd1, NULL, pthread_handler1, &data1);
if (ret != 0)
{
printf("pthread_create error\n");
exit(-1);
}
ret = pthread_create(&ptd2, NULL, pthread_handler2, &data2);
if (ret != 0)
{
printf("pthread_create error\n");
exit(-1);
}
ret = pthread_create(&ptd3, NULL, pthread_handler3, &data3);
if (ret != 0)
{
printf("pthread_create error\n");
exit(-1);
}
printf("ptd1:%ld\n",ptd1);
printf("ptd1:%ld\n",ptd2);
printf("ptd1:%ld\n",ptd3);
while (1)
{
sem_wait(&sem_r2); //P操作,申请一个信号量
printf("%s\n",buf);
sem_post(&sem_w1); //V操作,释放一个信号量
sleep(1);
}
return 0;
}
该段程序用来实现线程间的同步,即让线程按照指定的顺序来运行其中的程序。
cpp
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <string.h>
#include <semaphore.h>
#define Nsize 64
char buf[Nsize+1];
sem_t sem; //定义一个信号量
pthread_mutex_t mutex;//定义一个互斥锁
void *pthread_handler1(void *arg)
{
printf("pthtread_handler1:arg=%d\n", *(int *)arg);
while(1)
{
pthread_mutex_lock(&mutex); //P操作,上锁
memset(buf,'O',32);
usleep(1000);
memset(buf+32,'O',32);
usleep(1000);
pthread_mutex_unlock(&mutex); //V操作,解锁
}
}
void *pthread_handler2(void *arg)
{
printf("pthtread_handler2:arg=%d\n", *(int *)arg);
while(1)
{
pthread_mutex_lock(&mutex); //P操作,上锁
memset(buf,'-',32);
usleep(1000);
memset(buf+32,'-',32);
usleep(1000);
pthread_mutex_unlock(&mutex); //V操作,解锁
}
}
void *pthread_handler3(void *arg)
{
printf("pthtread_handler3:arg=%d\n", *(int *)arg);
while (1)
{
pthread_mutex_lock(&mutex); //P操作,上锁
printf("%s\n",buf);
pthread_mutex_unlock(&mutex); //V操作,解锁
sleep(1);
}
}
int main(int argc, char const *argv[])
{
//初始化一个互斥锁,初始化后锁是解锁状态
int ret = pthread_mutex_init(&mutex,NULL);
if(ret != 0)
{
printf("pthread_mutex_init error\n");
exit(-1);
}
pthread_t ptd1, ptd2, ptd3;
int data1 = 100, data2 = 200, data3 = 300;
ret = pthread_create(&ptd1, NULL, pthread_handler1, &data1);
if (ret != 0)
{
printf("pthread_create error\n");
exit(-1);
}
ret = pthread_create(&ptd2, NULL, pthread_handler2, &data2);
if (ret != 0)
{
printf("pthread_create error\n");
exit(-1);
}
ret = pthread_create(&ptd3, NULL, pthread_handler3, &data3);
if (ret != 0)
{
printf("pthread_create error\n");
exit(-1);
}
printf("ptd1:%ld\n",ptd1);
printf("ptd1:%ld\n",ptd2);
printf("ptd1:%ld\n",ptd3);
while (1)
{
pthread_mutex_lock(&mutex); //P操作,上锁
printf("%s\n",buf);
pthread_mutex_unlock(&mutex); //V操作,解锁
sleep(1);
}
return 0;
}
该段程序使用互斥锁来使线程间互斥来解决线程间使用共享资源的问题。pthread_mutex_init:初始化互斥锁,第一个参数要初始化的互斥锁初始化后默认是解锁状态,第二个参数锁的属性默认选择NULL,返回值成功返回0失败返回错误号。pthread_mutex_lock:上锁,参数要上锁的互斥锁。pthread_mutex_unlock:解锁,参数要解锁的互斥锁。
cpp
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <string.h>
#include <semaphore.h>
#define Nsize 64
char buf[Nsize+1];
sem_t sem; //定义一个信号量
pthread_mutex_t mutex_w1,mutex_w2,mutex_r1,mutex_r2;//定义互斥锁
void *pthread_handler1(void *arg)
{
printf("pthtread_handler1:arg=%d\n", *(int *)arg);
while(1)
{
pthread_mutex_lock(&mutex_w1); //P操作,上锁
memset(buf,'O',32);
usleep(1000);
memset(buf+32,'O',32);
usleep(1000);
pthread_mutex_unlock(&mutex_r1); //V操作,解锁
}
}
void *pthread_handler2(void *arg)
{
printf("pthtread_handler2:arg=%d\n", *(int *)arg);
while(1)
{
pthread_mutex_lock(&mutex_w2); //P操作,上锁
memset(buf,'-',32);
usleep(1000);
memset(buf+32,'-',32);
usleep(1000);
pthread_mutex_unlock(&mutex_r2); //V操作,解锁
}
}
void *pthread_handler3(void *arg)
{
printf("pthtread_handler3:arg=%d\n", *(int *)arg);
while (1)
{
pthread_mutex_lock(&mutex_r1); //P操作,上锁
printf("%s\n",buf);
pthread_mutex_unlock(&mutex_w2); //V操作,解锁
sleep(1);
}
}
int main(int argc, char const *argv[])
{
//初始化互斥锁,初始化后锁是解锁状态
int ret = pthread_mutex_init(&mutex_w1,NULL);
if(ret != 0)
{
printf("pthread_mutex_init error\n");
exit(-1);
}
ret = pthread_mutex_init(&mutex_w2,NULL);
if(ret != 0)
{
printf("pthread_mutex_init error\n");
exit(-1);
}
ret = pthread_mutex_init(&mutex_r1,NULL);
if(ret != 0)
{
printf("pthread_mutex_init error\n");
exit(-1);
}
ret = pthread_mutex_init(&mutex_r2,NULL);
if(ret != 0)
{
printf("pthread_mutex_init error\n");
exit(-1);
}
pthread_mutex_lock(&mutex_w2);
pthread_mutex_lock(&mutex_r1);
pthread_mutex_lock(&mutex_r2);
pthread_t ptd1, ptd2, ptd3;
int data1 = 100, data2 = 200, data3 = 300;
ret = pthread_create(&ptd1, NULL, pthread_handler1, &data1);
if (ret != 0)
{
printf("pthread_create error\n");
exit(-1);
}
ret = pthread_create(&ptd2, NULL, pthread_handler2, &data2);
if (ret != 0)
{
printf("pthread_create error\n");
exit(-1);
}
ret = pthread_create(&ptd3, NULL, pthread_handler3, &data3);
if (ret != 0)
{
printf("pthread_create error\n");
exit(-1);
}
printf("ptd1:%ld\n",ptd1);
printf("ptd1:%ld\n",ptd2);
printf("ptd1:%ld\n",ptd3);
while (1)
{
pthread_mutex_lock(&mutex_r2); //P操作,上锁
printf("%s\n",buf);
pthread_mutex_unlock(&mutex_w1); //V操作,解锁
sleep(1);
}
return 0;
}
该段程序是使用互斥锁来实现线程间的同步。
cpp
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <string.h>
#include <semaphore.h>
#define Nsize 64
char buf[Nsize + 1];
pthread_mutex_t mutex; // 定义互斥锁
pthread_cond_t cond1, cond2, cond3; // 定义条件变量
void *pthread_handler1(void *arg)
{
printf("pthtread_handler1:arg=%d\n", *(int *)arg);
while (1)
{
pthread_mutex_lock(&mutex); // 上锁
pthread_cond_wait(&cond1, &mutex); // 判断条件变量是否唤醒,不唤醒则释放锁,线程睡眠,唤醒则上锁,线程运行
pthread_mutex_unlock(&mutex); // 解锁
printf("pthread_handler1 is running\n");
}
}
void *pthread_handler2(void *arg)
{
printf("pthtread_handler2:arg=%d\n", *(int *)arg);
while (1)
{
pthread_mutex_lock(&mutex); // 上锁
pthread_cond_wait(&cond2, &mutex); // 判断条件变量是否唤醒,不唤醒则释放锁,线程睡眠,唤醒则上锁,线程运行
pthread_mutex_unlock(&mutex); // 解锁
printf("pthread_handler2 is running\n");
}
}
void *pthread_handler3(void *arg)
{
printf("pthtread_handler3:arg=%d\n", *(int *)arg);
while (1)
{
pthread_mutex_lock(&mutex); // 上锁
pthread_cond_wait(&cond3, &mutex); // 判断条件变量是否唤醒,不唤醒则释放锁,线程睡眠,唤醒则上锁,线程运行
pthread_mutex_unlock(&mutex); // 解锁
printf("pthread_handler3 is running\n");
}
}
int main(int argc, char const *argv[])
{
// 初始化互斥锁,初始化后锁是解锁状态
int ret = pthread_mutex_init(&mutex, NULL);
if (ret != 0)
{
printf("pthread_mutex_init error\n");
exit(-1);
}
// 初始化条件变量,初始化后是不唤醒状态
ret = pthread_cond_init(&cond1, NULL);
if (ret != 0)
{
printf("pthread_cond_init error\n");
exit(-1);
}
ret = pthread_cond_init(&cond2, NULL);
if (ret != 0)
{
printf("pthread_cond_init error\n");
exit(-1);
}
ret = pthread_cond_init(&cond3, NULL);
if (ret != 0)
{
printf("pthread_cond_init error\n");
exit(-1);
}
pthread_t ptd1, ptd2, ptd3;
int data1 = 100, data2 = 200, data3 = 300;
ret = pthread_create(&ptd1, NULL, pthread_handler1, &data1);
if (ret != 0)
{
printf("pthread_create error\n");
exit(-1);
}
ret = pthread_create(&ptd2, NULL, pthread_handler2, &data2);
if (ret != 0)
{
printf("pthread_create error\n");
exit(-1);
}
ret = pthread_create(&ptd3, NULL, pthread_handler3, &data3);
if (ret != 0)
{
printf("pthread_create error\n");
exit(-1);
}
usleep(1000 * 100);
printf("ptd1:%ld\n", ptd1);
printf("ptd1:%ld\n", ptd2);
printf("ptd1:%ld\n", ptd3);
int num;
while (1)
{
printf("1.waken handler1 2.waken handler2 3.waken handler3 4.waken all\n");
printf(">:");
scanf("%d",&num);
switch (num)
{
case 1:
pthread_cond_signal(&cond1);
break;
case 2:
pthread_cond_signal(&cond2);
break;
case 3:
pthread_cond_signal(&cond3);
break;
case 4:
pthread_cond_signal(&cond1);
pthread_cond_signal(&cond2);
pthread_cond_signal(&cond3);
break;
default:
break;
}
sleep(1);
}
return 0;
}
该段程序是使用条件变量来验证当满足条件后线程执行一次的功能。pthread_cond_init:初始化条件变量,第一次参数要初始化的条件变量初始化后默认是不唤醒状态,第二个参数条件变量的属性默认选择NULL,返回值成功返回0失败返回错误号。pthread_cond_signal:激活条件变量,参数要激活的条件变量。pthread_cond_wait:等待一个条件变量,第一个参数要等待的条件变量,第二个参数保护条件变量要用到的锁。
3.进程间的通信
进程间通信是指不同进程之间传递数据或信号的机制。在Linux中,每个进程拥有独立的地址空间,IPC允许进程协同工作、共享信息和资源。Linux中进程间通信的方式有无名管道,有名管道,信号,共享内存,消息队列,信号量等。
下面我们来看看进程间通信的方式相关函数和程序:
cpp
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#define Nsize 128
int main(int argc, char const *argv[])
{
char buf[Nsize] = {0};
char buf2[Nsize] = {0};
int ret;
int fd[2];
ret = pipe(fd); //创建一个管道
if(ret < 0)
{
perror("pipe");
exit(-1);
}
pid_t pid = fork();
if(pid < 0)
{
perror("fork");
exit(-1);
}
else if(pid == 0) //子进程
{
printf("child: pid=%d\n",getpid());
printf("child:ppid=%d\n",getppid());
//fork后管道有两个端口,子进程发送数据,关闭读端口
close(fd[0]);
while(1)
{
printf("child:>");
fgets(buf,Nsize,stdin); //换行符也会被读取
buf[strlen(buf)-1] = 0; //消除换行符
ret = write(fd[1],buf,Nsize);
if(ret < 0)
{
perror("write");
exit(-1);
}
usleep(1000*100);
}
}
else//父进程
{
printf("parent: pid=%d\n",getpid());
printf("parent:ppid=%d\n",getppid());
//fork后管道有两个端口,父进程接收数据,关闭写端口
close(fd[1]);
while(1)
{
ret = read(fd[0],buf2,Nsize);
if(ret < 0)
{
perror("read");
exit(-1);
}
printf("parent:buf2=%s\n",buf2);
sleep(1);
}
}
return 0;
}
该段程序使用无名管道来实现进程间的半双工通信,即一个进程发一个进程收。pipe:创建一个无名管道,参数需要的数组地址。
cpp
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#define Nsize 128
int main(int argc, char const *argv[])
{
char buf[Nsize] = {0};
int ret;
int fd_send[2];
int fd_recv[2];
ret = pipe(fd_send); //创建一个管道
if(ret < 0)
{
perror("pipe");
exit(-1);
}
ret = pipe(fd_recv); //创建一个管道
if(ret < 0)
{
perror("pipe");
exit(-1);
}
pid_t pid = fork();
if(pid < 0)
{
perror("fork");
exit(-1);
}
else if(pid == 0) //子进程
{
printf("child: pid=%d\n",getpid());
printf("child:ppid=%d\n",getppid());
//fork后管道有两个端口,子进程发送数据,关闭读端口
close(fd_send[0]);
close(fd_recv[1]);
while(1)
{
printf("child >:");
fgets(buf,Nsize,stdin); //换行符也会被读取
buf[strlen(buf)-1] = 0; //消除换行符
ret = write(fd_send[1],buf,Nsize);
if(ret < 0)
{
perror("write");
exit(-1);
}
memset(buf,0,Nsize);
ret = read(fd_recv[0],buf,Nsize);
if(ret < 0)
{
perror("read");
exit(-1);
}
printf("child readfrom parent buf:%s\n",buf);
}
}
else//父进程
{
printf("parent: pid=%d\n",getpid());
printf("parent:ppid=%d\n",getppid());
//fork后管道有两个端口,父进程接收数据,关闭写端口
close(fd_send[1]);
close(fd_recv[0]);
while(1)
{
ret = read(fd_send[0],buf,Nsize);
if(ret < 0)
{
perror("read");
exit(-1);
}
printf("parent readfrom child buf:%s\n",buf);
memset(buf,0,Nsize);
printf("parent >:");
fgets(buf,Nsize,stdin); //换行符也会被读取
buf[strlen(buf)-1] = 0; //消除换行符
ret = write(fd_recv[1],buf,Nsize);
if(ret < 0)
{
perror("write");
exit(-1);
}
}
}
return 0;
}
该段程序通过创建两个无名管道实现进程间的全双工通信,即该进程既可以发送也可以接收,但是无名管道只能在有亲缘关系的两个进程间使用。
cpp
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#define Nsize 128
int main(int argc, char const *argv[])
{
char buf[Nsize] = {0};
int ret;
if (argc != 2)
{
printf("运行时请带入参数(./app filename)\n");
exit(-1);
}
// 判断文件是否存在,不存在创建文件
if (access(argv[1], F_OK) < 0)
{
// 创建有名管道
ret = mkfifo(argv[1], 0664);
if (ret < 0)
{
perror("mkfifo");
exit(-1);
}
}
// 打开管道文件
int fd = open(argv[1], O_RDWR);
if (fd < 0)
{
perror("open");
exit(-1);
}
printf("fd=%d\n", fd);
// 向管道文件内写入数据
while (1)
{
printf(">:");
fgets(buf, Nsize, stdin); // 换行符也会被读取
buf[strlen(buf) - 1] = 0; // 消除换行符
ret = write(fd, buf, Nsize);
if (ret < 0)
{
perror("write");
exit(-1);
}
if (strncmp(buf, "quit", 4) == 0)
break;
}
close(fd);
return 0;
}
该段程序用有名管道来实现两个任意进程之间的半双工通信,这段程序是通信中的发送数据方。mkfifo:创建一个有名管道,第一个参数要创建的管道文件名,第二个参数创建文件的权限。
cpp
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#define Nsize 128
int main(int argc, char const *argv[])
{
char buf[Nsize] = {0};
int ret;
if (argc != 2)
{
printf("运行时请带入参数(./app filename)\n");
exit(-1);
}
// 判断文件是否存在,不存在创建文件
if (access(argv[1], F_OK) < 0)
{
// 创建有名管道
ret = mkfifo(argv[1], 0664);
if (ret < 0)
{
perror("mkfifo");
exit(-1);
}
}
// 打开管道文件
int fd = open(argv[1], O_RDWR);
if (fd < 0)
{
perror("open");
exit(-1);
}
printf("fd=%d\n", fd);
// 向管道文件内写入数据
while (1)
{
ret = read(fd, buf, Nsize);
if (ret < 0)
{
perror("read");
exit(-1);
}
printf("readfrom:%s\n", buf);
if (strncmp(buf, "quit", 4) == 0)
break;
}
close(fd);
return 0;
}
该段程序是用有名管道实现进程间半双工通信的读数据程序。
cpp
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#define Nsize 128
int main(int argc, char const *argv[])
{
char buf[Nsize] = {0};
int ret,fd_send,fd_recv;
if (argc != 3)
{
printf("运行时请带入参数(./app filename)\n");
exit(-1);
}
// 判断文件是否存在,不存在创建文件
if (access(argv[1], F_OK) < 0)
{
// 创建有名管道
ret = mkfifo(argv[1], 0664);
if (ret < 0)
{
perror("mkfifo");
exit(-1);
}
}
if (access(argv[2], F_OK) < 0)
{
// 创建有名管道
ret = mkfifo(argv[2], 0664);
if (ret < 0)
{
perror("mkfifo");
exit(-1);
}
}
// 打开管道文件
fd_send = open(argv[1], O_RDWR);
if (fd_send < 0)
{
perror("open");
exit(-1);
}
fd_recv = open(argv[2], O_RDWR);
if (fd_recv < 0)
{
perror("open");
exit(-1);
}
printf("fd=%d\n", fd_send);
printf("fd=%d\n", fd_recv);
// 向管道文件内写入数据
while (1)
{
printf(">:");
fgets(buf, Nsize, stdin); // 换行符也会被读取
buf[strlen(buf) - 1] = 0; // 消除换行符
ret = write(fd_send, buf, Nsize);
if (ret < 0)
{
perror("write");
exit(-1);
}
if (strncmp(buf, "quit", 4) == 0)
break;
ret = read(fd_recv, buf, Nsize);
if (ret < 0)
{
perror("read");
exit(-1);
}
printf("readfrom:%s\n", buf);
}
close(fd_send);
close(fd_recv);
return 0;
}
//////////////////////////////////////////////////////////
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#define Nsize 128
int main(int argc, char const *argv[])
{
char buf[Nsize] = {0};
int ret,fd_send,fd_recv;
if (argc != 3)
{
printf("运行时请带入参数(./app filename)\n");
exit(-1);
}
// 判断文件是否存在,不存在创建文件
if (access(argv[1], F_OK) < 0)
{
// 创建有名管道
ret = mkfifo(argv[1], 0664);
if (ret < 0)
{
perror("mkfifo");
exit(-1);
}
}
if (access(argv[2], F_OK) < 0)
{
// 创建有名管道
ret = mkfifo(argv[2], 0664);
if (ret < 0)
{
perror("mkfifo");
exit(-1);
}
}
// 打开管道文件
fd_send = open(argv[1], O_RDWR);
if (fd_send < 0)
{
perror("open");
exit(-1);
}
fd_recv = open(argv[2], O_RDWR);
if (fd_recv < 0)
{
perror("open");
exit(-1);
}
printf("fd=%d\n", fd_send);
printf("fd=%d\n", fd_recv);
// 向管道文件内写入数据
while (1)
{
ret = read(fd_send, buf, Nsize);
if (ret < 0)
{
perror("read");
exit(-1);
}
printf("readfrom:%s\n", buf);
if (strncmp(buf, "quit", 4) == 0)
break;
printf(">:");
fgets(buf, Nsize, stdin); // 换行符也会被读取
buf[strlen(buf) - 1] = 0; // 消除换行符
ret = write(fd_recv, buf, Nsize);
if (ret < 0)
{
perror("write");
exit(-1);
}
}
close(fd_send);
close(fd_recv);
return 0;
}
该段程序是用两个有名管道来实现两个进程间全双工通信。
cpp
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#define Nsize 128
int main(int argc, char const *argv[])
{
char buf[Nsize] = {0};
int ret,size=0;
int fd[2];
ret = pipe(fd); //创建一个管道
if(ret < 0)
{
perror("pipe");
exit(-1);
}
while(1)
{
//当管道满后,write不能再往管道里面写内容,阻塞
ret = write(fd[1],buf,1);
if(ret < 0)
{
perror("write");
exit(-1);
}
size ++;
printf("size=%d (%d KB)\n",size,size/1024);
}
return 0;
}
该段程序用来验证出无名管道缓存区的大小,验证所得无名管道缓存区的大小为64kb。
cpp
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#define Nsize 128
int main(int argc, char const *argv[])
{
char buf[Nsize] = {0};
int ret;
if (argc != 2)
{
printf("运行时请带入参数(./app filename)\n");
exit(-1);
}
// 判断文件是否存在,不存在创建文件
if (access(argv[1], F_OK) < 0)
{
// 创建有名管道
ret = mkfifo(argv[1], 0664);
if (ret < 0)
{
perror("mkfifo");
exit(-1);
}
}
// 打开管道文件
int fd = open(argv[1], O_RDWR);
if (fd < 0)
{
perror("open");
exit(-1);
}
printf("fd=%d\n", fd);
int size = 0;
while(1)
{
//当管道满后,write不能再往管道里面写内容,阻塞
ret = write(fd,buf,1);
if(ret < 0)
{
perror("write");
exit(-1);
}
size ++;
printf("size=%d (%d KB)\n",size,size/1024);
}
return 0;
}
该段程序用来验证出有名管道缓存区的大小,验证得有名管道的缓存区大小也是64kb。
cpp
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#define Nsize 128
int main(int argc, char const *argv[])
{
char buf[Nsize] = {0};
int ret;
int fd[2];
ret = pipe(fd); // 创建一个管道
if (ret < 0)
{
perror("pipe");
exit(-1);
}
#if 0
//读端关闭,写管道,管道崩溃
close(fd[0]);
ret = write(fd[1], buf, 1);
if (ret < 0)
{
perror("write");
exit(-1);
}
#endif
#if 1
//写端关闭,读管道,返回0
close(fd[1]);
ret = read(fd[0], buf, 1);
printf("ret=%d\n",ret);
if (ret < 0)
{
perror("write");
exit(-1);
}
#endif
printf("test pipe broken\n");
return 0;
}
该段程序用来验证无名管道崩溃和读取数据返回0的情况,当无名管道读端关闭写管道时管道崩溃,当无名管道写端关闭,读管道时返回0。
cpp
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>
void signal_handler(int signum)
{
printf("signum=%d\n", signum);
}
int main(int argc, char const *argv[])
{
//设置信号处理函数
signal(SIGINT, signal_handler); // crtl+c 发出的信号
signal(SIGTSTP, signal_handler); // crtl+z 发出的信号
signal(SIGUSR1, signal_handler); // 使用kill命令发出的信号
signal(SIGQUIT, signal_handler); // crtl+\ 发出的信号
while(1)
{
sleep(1);
}
return 0;
}
该段程序用来捕捉信号,设置信号处理函数。signal:设置信号处理函数当信号发生后,会调用指定的函数来处理该信号,第一个参数要捕捉的信号,第二个参数信号处理函数。
cpp
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>
int child1_pid, child2_pid;
void parent_signal_handler(int signum)
{
static int count = 0;
if (signum == SIGINT)
{
kill(child1_pid, SIGUSR1);
kill(child2_pid, SIGUSR1);
}
if (signum == SIGCHLD)
{
count++;
if (count == 2)
{
printf("parent process exit!\n");
exit(0);
}
}
}
void child1_signal_handler(int signum)
{
if (signum == SIGUSR1)
{
printf("child process1 is killed by parent!\n");
exit(0);
}
}
void child2_signal_handler(int signum)
{
if (signum == SIGUSR1)
{
usleep(1000 * 100);
printf("child process2 is killed by parent!\n");
exit(0);
}
// 子进程结束后会向父进程发送SIGCHLD信号
}
int main(int argc, char const *argv[])
{
pid_t pid;
pid = fork();
if (pid < 0)
{
perror("fork");
exit(-1);
}
else if (pid == 0) // 子进程1
{
signal(SIGINT, SIG_IGN); // 子进程1忽略这个信号
signal(SIGUSR1, child1_signal_handler); // 子进程捕捉这个信号
while (1)
{
sleep(1);
}
}
else
{
child1_pid = pid; // 父进程返回子进程号
pid = fork();
if (pid < 0)
{
perror("fork");
exit(-1);
}
else if (pid == 0) // 子进程2
{
signal(SIGINT, SIG_IGN); // 子进程2忽略这个信号
signal(SIGUSR1, child2_signal_handler); // 子进程捕捉这个信号
while (1)
{
sleep(1);
}
}
else // 父进程
{
child2_pid = pid; // 父进程返回子进程号
signal(SIGINT, parent_signal_handler); // 父进程捕捉crtl+c 发出的信号
signal(SIGCHLD, parent_signal_handler); // 父进程捕捉子进程结束发出的信号
while (1)
{
sleep(1);
}
}
}
return 0;
}
该段程序是用信号来实现进程间的通信。kill:给进程发送指定信号,第一个参数指定的进程,第二个参数要发送的信号。
cpp
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>
void signal_handler(int signum)
{
pid_t pid = waitpid(-1,NULL,0);
printf("回收子进程:%d\n",pid);
}
int main(int argc, char const *argv[])
{
//使用无缓存的信号,会导致信号丢失,子进程回收失败产生僵尸态
//signal(SIGCHLD,signal_handler);//信号处理函数
//使用有缓存的信号来解决上面的问题
signal(SIGRTMIN,signal_handler);
//创建多个子进程
for(int i= 0;i<10;i++)
{
if(fork() == 0)
{
printf("child=%d\n",getpid());
sleep(10);
kill(getppid(),SIGRTMIN);
exit(0);
}
}
printf("parent is running!\n");
while(1)
{
sleep(1);
}
return 0;
}
该段程序用来验证使用无缓存信号会丢失信号的情况。waitpid:等待子进程结束,第一个参数指定等待的进程>0等待指定进程号的进程-1任意子进程0等待同一进程组的子进程<-1等待指定进程组的子进程,第二个参数子进程的状态信息NULL标识不获取子进程状态信息,第三个参数控制函数的行为。
cpp
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>
// void signal_handler(int signum)
// {
// pid_t pid = waitpid(-1,NULL,0);
// printf("回收子进程:%d\n",pid);
// }
int main(int argc, char const *argv[])
{
//使用无缓存的信号,会导致信号丢失,子进程回收失败产生僵尸态
//signal(SIGCHLD,signal_handler);//信号处理函数
//使用有缓存的信号来解决上面的问题
//signal(SIGRTMIN,signal_handler);
//忽略子进程信号,脱离父子关系,缺点不能获取到子进程退出时的状态
signal(SIGCHLD,SIG_IGN);
//创建多个子进程
for(int i= 0;i<10;i++)
{
if(fork() == 0)
{
printf("child=%d\n",getpid());
sleep(10);
//kill(getppid(),SIGRTMIN);
exit(0);
}
}
printf("parent is running!\n");
while(1)
{
sleep(1);
}
return 0;
}
该段程序是当子进程退出后不会产生僵尸态的处理方法之一,即脱离父子关系子进程退出后由系统回收,不会产生僵尸态。
cpp
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#define SHM_NAME "myshm"
#define SHM_SIZE 4096
int main(int argc, char const *argv[])
{
int fd,ret;
//打开或创建一个共享内存
fd = shm_open(SHM_NAME,O_RDWR|O_CREAT,0664);
if(fd < 0)
{
perror("shm_open");
exit(-1);
}
printf("fd:%d\n",fd);
system("ls -l /dev/shm");
//共享内存申请空间
ret = ftruncate(fd,SHM_SIZE);
if(ret < 0)
{
perror("ftruncate");
exit(-1);
}
system("ls -l /dev/shm");
//将共享内存映射到进程
void* shmaddr = mmap(NULL,SHM_SIZE,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
if(shmaddr == MAP_FAILED)
{
perror("mmap");
exit(-1);
}
//往共享内存里面写内容
memset(shmaddr,0,64+1);
memset(shmaddr,'O',32);
memset(shmaddr+32,'-',32);
//显示该共享内存里面的内容
printf("shmaddr=%s\n",(char*)shmaddr);
//取消共享内存映射
munmap(shmaddr,SHM_SIZE);
//删除共享内存
shm_unlink(SHM_NAME);
system("ls -l /dev/shm");
return 0;
}
该段程序是展示进程间通信机制中的共享内存的创建使用到删除过程。shm_open:打开或创建一个共享内存,第一个参数要打开或创建的共享内存文件名,第二个参数打开方式,第三个参数创建共享内存文件的权限,返回值成功返回文件描述符失败返回-1.ftruncate:设置共享内存空间大小,第一个参数要操作的共享内存描述符,第二个参数要设置的大小,返回值成功返回0失败返回-1.mmap:将共享内存映射到进程,第一个参数想要映射的内存起始地址NULL为让系统自动选择,第二个参数映射到进程的地址空间,第三个参数映射区域的保护方式,第四个参数映射区的类型标志,第五个参数共享内存文件描述符,第六个参数相对文件的起始偏移量,返回值成功返回映射后的地址失败返回-1。munmap:取消共享内存映射,第一个参数要取消的映射内存地址,第二个参数映射区的大小。shm_unlink:删除共享内存,参数要删除的共享内存的文件名。
cpp
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#define SHM_NAME "myshm"
#define SHM_SIZE 4096
int main(int argc, char const *argv[])
{
int fd, ret;
// 打开或创建一个共享内存
fd = shm_open(SHM_NAME, O_RDWR | O_CREAT, 0664);
if (fd < 0)
{
perror("shm_open");
exit(-1);
}
printf("fd:%d\n", fd);
system("ls -l /dev/shm");
// 共享内存申请空间
ret = ftruncate(fd, SHM_SIZE);
if (ret < 0)
{
perror("ftruncate");
exit(-1);
}
system("ls -l /dev/shm");
// 将共享内存映射到进程
void *shmaddr = mmap(NULL, SHM_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (shmaddr == MAP_FAILED)
{
perror("mmap");
exit(-1);
}
memset(shmaddr, 0, 64 + 1);
pid_t pid = fork();
if (pid < 0)
{
perror("fork");
exit(-1);
}
else if (pid == 0)
{
while (1)
{
// 往共享内存里面写内容
memset(shmaddr, 'O', 32);
usleep(1000);
memset(shmaddr + 32, 'O', 32);
usleep(1000);
}
}
else
{
while (1)
{
// 往共享内存里面写内容
memset(shmaddr, '-', 32);
usleep(1000);
memset(shmaddr + 32, '-', 32);
usleep(1000);
}
}
// 显示该共享内存里面的内容
printf("shmaddr=%s\n", (char *)shmaddr);
// 取消共享内存映射
munmap(shmaddr, SHM_SIZE);
// 删除共享内存
shm_unlink(SHM_NAME);
system("ls -l /dev/shm");
return 0;
}
///////////////////////////////////////////////////////////////////
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#define SHM_NAME "myshm"
#define SHM_SIZE 4096
int main(int argc, char const *argv[])
{
int fd, ret;
// 打开或创建一个共享内存
fd = shm_open(SHM_NAME, O_RDWR | O_CREAT, 0664);
if (fd < 0)
{
perror("shm_open");
exit(-1);
}
printf("fd:%d\n", fd);
system("ls -l /dev/shm");
// 共享内存申请空间
ret = ftruncate(fd, SHM_SIZE);
if (ret < 0)
{
perror("ftruncate");
exit(-1);
}
system("ls -l /dev/shm");
// 将共享内存映射到进程
void *shmaddr = mmap(NULL, SHM_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (shmaddr == MAP_FAILED)
{
perror("mmap");
exit(-1);
}
memset(shmaddr, 0, 64 + 1);
pid_t pid = fork();
if (pid < 0)
{
perror("fork");
exit(-1);
}
else if (pid == 0)
{
while (1)
{
printf("shmaddr=%s\n", (char *)shmaddr);
sleep(1);
}
}
else
{
while (1)
{
printf("shmaddr=%s\n", (char *)shmaddr);
sleep(1);
}
}
// 取消共享内存映射
munmap(shmaddr, SHM_SIZE);
// 删除共享内存
shm_unlink(SHM_NAME);
system("ls -l /dev/shm");
return 0;
}
该段程序通过共享内存来实现两个进程间的通信。
cpp
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <semaphore.h>
#define SHM_NAME "myshm"
#define SHM_SIZE 4096
#define SEM_NAME "mysem"
int main(int argc, char const *argv[])
{
int fd, ret;
int sval;
//判断信号量是否存在,存在删除,保证该信号量里面一直有值1
char buf[128] = {0};
sprintf(buf,"/dev/shm/sem.%s",SEM_NAME);
if(access(buf,F_OK) == 0)
{
sem_unlink(SEM_NAME);
}
//打开一个信号量,没有则创建,信号量设置为1
sem_t* sem = sem_open(SEM_NAME,O_RDWR|O_CREAT,0664,1);
if(sem == SEM_FAILED)
{
perror("sem_open");
exit(-1);
}
//获取一个信号量的值
ret = sem_getvalue(sem,&sval);
if(ret < 0)
{
perror("sem_getvalue");
exit(-1);
}
printf("sval=%d\n",sval);
// 打开或创建一个共享内存
fd = shm_open(SHM_NAME, O_RDWR | O_CREAT, 0664);
if (fd < 0)
{
perror("shm_open");
exit(-1);
}
printf("fd:%d\n", fd);
system("ls -l /dev/shm");
// 共享内存申请空间
ret = ftruncate(fd, SHM_SIZE);
if (ret < 0)
{
perror("ftruncate");
exit(-1);
}
system("ls -l /dev/shm");
// 将共享内存映射到进程
void *shmaddr = mmap(NULL, SHM_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (shmaddr == MAP_FAILED)
{
perror("mmap");
exit(-1);
}
memset(shmaddr, 0, 64 + 1);
pid_t pid = fork();
if (pid < 0)
{
perror("fork");
exit(-1);
}
else if (pid == 0)
{
while (1)
{
sem_wait(sem); //P操作,申请一个信号量
// 往共享内存里面写内容
memset(shmaddr, 'O', 32);
usleep(1000);
memset(shmaddr + 32, 'O', 32);
usleep(1000);
sem_post(sem); //V操作,释放一个信号量
}
}
else
{
while (1)
{
sem_wait(sem); //P操作,申请一个信号量
// 往共享内存里面写内容
memset(shmaddr, '-', 32);
usleep(1000);
memset(shmaddr + 32, '-', 32);
usleep(1000);
sem_post(sem); //V操作,释放一个信号量
}
}
// 显示该共享内存里面的内容
printf("shmaddr=%s\n", (char *)shmaddr);
// 取消共享内存映射
munmap(shmaddr, SHM_SIZE);
// 删除共享内存
shm_unlink(SHM_NAME);
system("ls -l /dev/shm");
return 0;
}
/////////////////////////////////////////////////////////////
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <semaphore.h>
#define SHM_NAME "myshm"
#define SHM_SIZE 4096
#define SEM_NAME "mysem"
int main(int argc, char const *argv[])
{
int fd, ret;
int sval;
//打开一个信号量,没有则创建,信号量设置为1
sem_t* sem = sem_open(SEM_NAME,O_RDWR|O_CREAT,0664,1);
if(sem == SEM_FAILED)
{
perror("sem_open");
exit(-1);
}
//获取一个信号量的值
ret = sem_getvalue(sem,&sval);
if(ret < 0)
{
perror("sem_getvalue");
exit(-1);
}
printf("sval=%d\n",sval);
// 打开或创建一个共享内存
fd = shm_open(SHM_NAME, O_RDWR | O_CREAT, 0664);
if (fd < 0)
{
perror("shm_open");
exit(-1);
}
printf("fd:%d\n", fd);
system("ls -l /dev/shm");
// 共享内存申请空间
ret = ftruncate(fd, SHM_SIZE);
if (ret < 0)
{
perror("ftruncate");
exit(-1);
}
system("ls -l /dev/shm");
// 将共享内存映射到进程
void *shmaddr = mmap(NULL, SHM_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (shmaddr == MAP_FAILED)
{
perror("mmap");
exit(-1);
}
memset(shmaddr, 0, 64 + 1);
pid_t pid = fork();
if (pid < 0)
{
perror("fork");
exit(-1);
}
else if (pid == 0)
{
while (1)
{
sem_wait(sem); //P操作,申请一个信号量
printf("shmaddr=%s\n", (char *)shmaddr);
sem_post(sem); //V操作,释放一个信号量
sleep(1);
}
}
else
{
while (1)
{
sem_wait(sem); //P操作,申请一个信号量
printf("shmaddr=%s\n", (char *)shmaddr);
sem_post(sem); //V操作,释放一个信号量
sleep(1);
}
}
// 显示该共享内存里面的内容
printf("shmaddr=%s\n", (char *)shmaddr);
// 取消共享内存映射
munmap(shmaddr, SHM_SIZE);
// 删除共享内存
shm_unlink(SHM_NAME);
system("ls -l /dev/shm");
return 0;
}
该段程序通过有名信号量来实现多个进程间的互斥。sem_open:打开或创建一个有名信号量,第一个参数要创建信号量的文件名,第二个参数打开权限位,第三个参数创建信号量后该文件的权限,第四个参数信号量的值,返回值成功返回信号量地址失败返回一个宏值。
cpp
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <semaphore.h>
#define SHM_NAME "myshm"
#define SHM_SIZE 4096
#define SEM_NAME_W1 "w1"
#define SEM_NAME_W2 "w2"
#define SEM_NAME_R1 "r1"
#define SEM_NAME_R2 "r2"
int main(int argc, char const *argv[])
{
int fd, ret;
int sval;
//判断信号量是否存在,存在删除,保证该信号量里面一直有值1
char buf[128] = {0};
sprintf(buf,"/dev/shm/sem.%s",SEM_NAME_W1);
if(access(buf,F_OK) == 0)
{
sem_unlink(SEM_NAME_W1);
}
sprintf(buf,"/dev/shm/sem.%s",SEM_NAME_W2);
if(access(buf,F_OK) == 0)
{
sem_unlink(SEM_NAME_W2);
}
sprintf(buf,"/dev/shm/sem.%s",SEM_NAME_R1);
if(access(buf,F_OK) == 0)
{
sem_unlink(SEM_NAME_R1);
}
sprintf(buf,"/dev/shm/sem.%s",SEM_NAME_R2);
if(access(buf,F_OK) == 0)
{
sem_unlink(SEM_NAME_R2);
}
//打开一个信号量,没有则创建,信号量设置为1
sem_t* sem_w1 = sem_open(SEM_NAME_W1,O_RDWR|O_CREAT,0664,1);
if(sem_w1 == SEM_FAILED)
{
perror("sem_open");
exit(-1);
}
sem_t* sem_w2 = sem_open(SEM_NAME_W2,O_RDWR|O_CREAT,0664,0);
if(sem_w2 == SEM_FAILED)
{
perror("sem_open");
exit(-1);
}
sem_t* sem_r1 = sem_open(SEM_NAME_R1,O_RDWR|O_CREAT,0664,0);
if(sem_r1 == SEM_FAILED)
{
perror("sem_open");
exit(-1);
}
sem_t* sem_r2 = sem_open(SEM_NAME_R2,O_RDWR|O_CREAT,0664,0);
if(sem_r2 == SEM_FAILED)
{
perror("sem_open");
exit(-1);
}
//获取一个信号量的值
ret = sem_getvalue(sem_w1,&sval);
if(ret < 0)
{
perror("sem_getvalue");
exit(-1);
}
printf("sval=%d\n",sval);
ret = sem_getvalue(sem_w2,&sval);
if(ret < 0)
{
perror("sem_getvalue");
exit(-1);
}
printf("sval=%d\n",sval);
ret = sem_getvalue(sem_r1,&sval);
if(ret < 0)
{
perror("sem_getvalue");
exit(-1);
}
printf("sval=%d\n",sval);
ret = sem_getvalue(sem_r2,&sval);
if(ret < 0)
{
perror("sem_getvalue");
exit(-1);
}
printf("sval=%d\n",sval);
// 打开或创建一个共享内存
fd = shm_open(SHM_NAME, O_RDWR | O_CREAT, 0664);
if (fd < 0)
{
perror("shm_open");
exit(-1);
}
printf("fd:%d\n", fd);
system("ls -l /dev/shm");
// 共享内存申请空间
ret = ftruncate(fd, SHM_SIZE);
if (ret < 0)
{
perror("ftruncate");
exit(-1);
}
system("ls -l /dev/shm");
// 将共享内存映射到进程
void *shmaddr = mmap(NULL, SHM_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (shmaddr == MAP_FAILED)
{
perror("mmap");
exit(-1);
}
memset(shmaddr, 0, 64 + 1);
pid_t pid = fork();
if (pid < 0)
{
perror("fork");
exit(-1);
}
else if (pid == 0)
{
while (1)
{
sem_wait(sem_w1); //P操作,申请一个信号量
// 往共享内存里面写内容
memset(shmaddr, 'O', 32);
usleep(1000);
memset(shmaddr + 32, 'O', 32);
usleep(1000);
sem_post(sem_r1); //V操作,释放一个信号量
}
}
else
{
while (1)
{
sem_wait(sem_w2); //P操作,申请一个信号量
// 往共享内存里面写内容
memset(shmaddr, '-', 32);
usleep(1000);
memset(shmaddr + 32, '-', 32);
usleep(1000);
sem_post(sem_r2); //V操作,释放一个信号量
}
}
// 显示该共享内存里面的内容
printf("shmaddr=%s\n", (char *)shmaddr);
// 取消共享内存映射
munmap(shmaddr, SHM_SIZE);
// 删除共享内存
shm_unlink(SHM_NAME);
system("ls -l /dev/shm");
return 0;
}
///////////////////////////////////////////////////////////////////////////////////
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <semaphore.h>
#define SHM_NAME "myshm"
#define SHM_SIZE 4096
#define SEM_NAME_W1 "w1"
#define SEM_NAME_W2 "w2"
#define SEM_NAME_R1 "r1"
#define SEM_NAME_R2 "r2"
int main(int argc, char const *argv[])
{
int fd, ret;
int sval;
//打开一个信号量,没有则创建,信号量设置为1
sem_t* sem_w1 = sem_open(SEM_NAME_W1,O_RDWR|O_CREAT,0664,1);
if(sem_w1 == SEM_FAILED)
{
perror("sem_open");
exit(-1);
}
sem_t* sem_w2 = sem_open(SEM_NAME_W2,O_RDWR|O_CREAT,0664,1);
if(sem_w2 == SEM_FAILED)
{
perror("sem_open");
exit(-1);
}
sem_t* sem_r1 = sem_open(SEM_NAME_R1,O_RDWR|O_CREAT,0664,1);
if(sem_r1 == SEM_FAILED)
{
perror("sem_open");
exit(-1);
}
sem_t* sem_r2 = sem_open(SEM_NAME_R2,O_RDWR|O_CREAT,0664,1);
if(sem_r2 == SEM_FAILED)
{
perror("sem_open");
exit(-1);
}
// 打开或创建一个共享内存
fd = shm_open(SHM_NAME, O_RDWR | O_CREAT, 0664);
if (fd < 0)
{
perror("shm_open");
exit(-1);
}
printf("fd:%d\n", fd);
system("ls -l /dev/shm");
// 共享内存申请空间
ret = ftruncate(fd, SHM_SIZE);
if (ret < 0)
{
perror("ftruncate");
exit(-1);
}
system("ls -l /dev/shm");
// 将共享内存映射到进程
void *shmaddr = mmap(NULL, SHM_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (shmaddr == MAP_FAILED)
{
perror("mmap");
exit(-1);
}
pid_t pid = fork();
if (pid < 0)
{
perror("fork");
exit(-1);
}
else if (pid == 0)
{
while (1)
{
sem_wait(sem_r1); //P操作,申请一个信号量
printf("shmaddr=%s\n", (char *)shmaddr);
sem_post(sem_w2); //V操作,释放一个信号量
sleep(1);
}
}
else
{
while (1)
{
sem_wait(sem_r2); //P操作,申请一个信号量
printf("shmaddr=%s\n", (char *)shmaddr);
sem_post(sem_w1); //V操作,释放一个信号量
sleep(1);
}
}
// 显示该共享内存里面的内容
printf("shmaddr=%s\n", (char *)shmaddr);
// 取消共享内存映射
munmap(shmaddr, SHM_SIZE);
// 删除共享内存
shm_unlink(SHM_NAME);
system("ls -l /dev/shm");
return 0;
}
该段程序使用有名信号量来实现进程间的同步机制。
cpp
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h> /* For O_* constants */
#include <sys/stat.h> /* For mode constants */
#include <mqueue.h>
#define Nsize 128
//在打开消息队列时这里必须加上/
#define MQ_NAME "/mymq"
int main(int argc, char const *argv[])
{
char buf[Nsize] = {0};
//打开消息队列,没有则创建,消息队列保存在/dev/mqueue
mqd_t mqfd = mq_open(MQ_NAME,O_RDWR|O_CREAT,0664,NULL);
if(mqfd < 0)
{
perror("mq_open");
exit(-1);
}
printf("mqfd=%d\n",mqfd);
system("ls -l /dev/mqueue");
//获取消息队列属性
struct mq_attr attr;
int ret = mq_getattr(mqfd,&attr);
if(ret < 0)
{
perror("mq_getattr");
exit(-1);
}
printf("attr.mq_msgsize=%ld\n",attr.mq_msgsize);
//创建父子进程
pid_t pid = fork();
if(pid < 0)
{
perror("fork");
exit(-1);
}
else if(pid == 0) //子进程
{
while(1)
{
printf("child >:");
fgets(buf,Nsize,stdin);
buf[strlen(buf)-1] = 0; //消除换行符
ret = mq_send(mqfd,buf,Nsize,1);
if(ret < 0)
{
perror("mq_send");
exit(-1);
}
memset(buf,0,Nsize);
usleep(1000*100);
}
}
else
{
int prio;
while(1)
{
ret = mq_receive(mqfd,buf,attr.mq_msgsize,&prio);
if(ret < 0)
{
perror("mq_send");
exit(-1);
}
printf("parent readfrom :%s -> %d\n",buf,prio);
}
}
return 0;
}
该段程序使用消息队列来实现进程间的通信。mq_open:打开或创建一个消息队列,第一个参数消息队列文件名,第二个参数打开权限,第三个参数消息队列创建文件权限,第四个参数消息队列的属性选择NULL位默认属性。mq_getattr:获取消息队列属性,第一个参数消息队列文件描述符,第二个参数保存要获取的消息队列的属性的结构体,返回值成功返回0失败返回-1.mq_send:发送消息,第一个参数要发送消息的消息队列文件描述符,第二个参数存放要发送消息的空间,第三个参数要发送消息的个数,第四个参数消息的优先级。mq_receive:接收一个消息,第一个参数要接收消息的消息队列描述符,第二个参数将接收的消息保存的空间,第三个参数消息体的长度,第四个参数保存接收消息的优先级,返回值成功返回接收到消息的字节数失败返回-1。