1. 多线程
1.1 多线程的安装
Ubuntu默认是没有pthread库的,需要安装。输入命令:
$ sudo apt-get install -y glibc-doc manpages-posix-dev
然后我们进行代码测试演示:
1.2 创建线程
#include <pthread.h>
int pthread_create(
pthread_t *restrict tidp, //新创建的线程ID指向的内存单元。
const pthread_attr_t *restrict attr, //线程属性,默认为NULL
void *(*start_rtn)(void *), //新创建的线程从start_rtn函数的地址开始运行
void *restrict arg //默认为NULL。若上述函数需要参数,将参数放入结构中并将地址作为arg传入。
);
线程创建的函数:
首先我们需要makeflie把所有的.c文件编译一下:
接下来我们运行程序,输入命令:
$ ./1.pthread_create_exit.out
我们可以看到使用pthread_create创建了五个线程并显示出来,用pthread_exit来终止线程。
1.3 终止线程
void pthread_exit(void*_retval)
1.4 线程连接
函数如下:
int pthread_join(
pthread_t tid, //需要等待的线程,指定的线程必须位于当前的进程中,而且不得是分离线程
void **status //线程tid所执行的函数返回值(返回值地址需要保证有效),其中status可以为NULL
);
我们来看一下完整函数段:
接下来我们运行程序,输入命令:
$ ./2.pthread_join.out
1.5 线程管理
1.5.1 线程初始化
int pthread_attr_init(pthread_attr_t *attr);
1.5.2 线程销毁
int pthread_attr_destroy(pthread_attr_t *attr);
我们看一下销毁代码具体代码段:
1.6 互斥量、互斥锁
使用前先初始化,即上文的线程管理内容
#include <pthread.h>
int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);
int pthread_mutex_destroy(pthread_mutex_t *mutex);
对互斥量进行加锁和解锁函数:
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_trylock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);
具体代码段:
/* 使用互斥量保护多线程同时输出 */
#include<stdio.h>
#include<string.h>
#include<pthread.h>
#include<stdlib.h>
#include<unistd.h>
pthread_t tid[2];
pthread_mutex_t lock;
void* thread_func(void *arg)
{
int id = (long)arg;
int i = 0;
pthread_mutex_lock(&lock); /* 使用互斥量保护临界区 ================== begin ================== */
printf("Job %d started\n", id);
for (i = 0; i < 5; i++)
{
printf("Job %d printing\n", id);
usleep(10);
}
printf("Job %d finished\n", id);
pthread_mutex_unlock(&lock); /* 使用互斥量保护临界区 ================== end ================== */
return NULL;
}
int main(void)
{
long i = 0;
int err;
if (pthread_mutex_init(&lock, NULL) != 0) { /* (动态) 初始化 互斥量, 因为定义时候没有初始化 */
printf("\n Mutex init failed\n");
return 1;
}
while(i < 2) {
err = pthread_create(&(tid[i]), NULL, &thread_func, (void*)i);
if (err != 0)
printf("Can't create thread :[%s]", strerror(err));
i++;
}
pthread_join(tid[0], NULL);
pthread_join(tid[1], NULL);
pthread_mutex_destroy(&lock); /* 用完销毁 */
return 0;
}
我们运行程序,输入命令:
$ ./3.pthread_attr_init_destroy_getstack-foo_bar.out
$ ./4.pthread_mutex.out
我们可以看到使用互斥锁可以同一时间内只有一个线程访问数据。
1.7 死锁
接下来看代码段:
/* 死锁产生的范例 */
#include<stdio.h>
#include<string.h>
#include<pthread.h>
#include<stdlib.h>
#include<unistd.h>
pthread_t tid[2];
pthread_mutex_t mutexA = PTHREAD_MUTEX_INITIALIZER; /* 静态初始化互斥量 */
pthread_mutex_t mutexB = PTHREAD_MUTEX_INITIALIZER;
void * t1(void *arg) {
pthread_mutex_lock(&mutexA); /* 线程 1 获取 mutexA */
printf("t1 get mutexA\n");
usleep(1000);
pthread_mutex_lock(&mutexB); /* 线程 1 获取 mutexB */
printf("t1 get mutexB\n");
pthread_mutex_unlock(&mutexB); /* 线程 1 释放 mutexB */
printf("t1 release mutexB\n");
pthread_mutex_unlock(&mutexA); /* 线程 1 释放 mutexA */
printf("t1 release mutexA\n");
return NULL;
}
void * t2(void *arg) {
pthread_mutex_lock(&mutexB);
printf("t2 get mutexB\n");
usleep(1000);
pthread_mutex_lock(&mutexA);
printf("t2 get mutexA\n");
pthread_mutex_unlock(&mutexA);
printf("t2 release mutexA\n");
pthread_mutex_unlock(&mutexB);
printf("t2 release mutexB\n");
return NULL;
}
int main(void) {
int err;
err = pthread_create(&(tid[0]), NULL, &t1, NULL ); /* 创建线程 1 */
if (err != 0)
printf("Can't create thread :[%s]", strerror(err));
err = pthread_create(&(tid[1]), NULL, &t2, NULL); /* 创建线程 2 */
if (err != 0)
printf("Can't create thread :[%s]", strerror(err));
pthread_join(tid[0], NULL);
pthread_join(tid[1], NULL);
return 0;
}
所谓死锁就是让两个进程不断争夺运行资源来使程序进入死锁状态。然后我们运行程序,输入命令:
$ ./5.pthread_dead_lock_of_mutex.out
我们可以看到程序进入死锁状态.
1.8 条件变量
#include<stdio.h>
#include<string.h>
#include<pthread.h>
#include<stdlib.h>
#include<unistd.h>
pthread_t tid_array[3];
int sum = 0;
pthread_mutex_t sum_lock = PTHREAD_MUTEX_INITIALIZER; /* 互斥量 (静态初始化)*/
pthread_cond_t condition_sum_ready = PTHREAD_COND_INITIALIZER; /* 条件量 (静态初始化) */
void * worker_thread_func(void *arg) {
int i;
long id = (long) arg;
for (i = 0; i < 60; i++) {
pthread_mutex_lock(&sum_lock); /* 使用互斥量保护临界变量 */
printf("t%ld: read sum value before = %d\n", id + 1, sum);
sum++;
printf("t%ld: read sum value after = %d\n", id + 1, sum);
pthread_mutex_unlock(&sum_lock); /* 结束互斥量保护临界变量 */
if (sum >= 100)
pthread_cond_signal(&condition_sum_ready); /* 通过条件量 发送条件通知 -> 唤醒等待线程 */
}
return NULL;
}
void * waiting_thread_func(void *arg) {
long id = (long) arg;
pthread_mutex_lock(&sum_lock);
while (sum < 100) /* 不满足条件将一直等待 */
pthread_cond_wait(&condition_sum_ready, &sum_lock); /* 通过条件量 等待条件通知 -> 唤醒等待线程 */
sum = 0;
printf("waiting_thread_func: clear sum value [我是等待线程,已被唤醒。 ]\n");
printf("t%ld: read sum value = %d\n", id + 1, sum);
pthread_mutex_unlock(&sum_lock);
return NULL;
}
int main(void) {
int err;
long i;
for (i = 0; i < 2; i++) {
err = pthread_create(&(tid_array[i]), NULL, &worker_thread_func, (void *) i); /* 创建线程 1 线程 2 */
if (err != 0) {
printf("Can't create thread :[%s]", strerror(err));
}
}
err = pthread_create(&(tid_array[2]), NULL, &waiting_thread_func, (void *) i); /* 创建线程 3 */
if (err != 0)
printf("Can't create thread :[%s]", strerror(err));
for (i = 0; i < 3; i++)
pthread_join(tid_array[i], NULL);
return 0;
}
同样输入指令运行程序:
$ ./6.pthread_conditional_var.out
2. 进程
2.1 简介
Linux 的进程有以下6种状态:
D:不可中断的深度睡眠状态,处于这种状态的进程不能响应异步信号;
R:进程处于运行态或就绪状态,只有在该状态的进程才可能在CPU上运行。而同一时刻可能有多个进程处于可执行状态;
S:可中断的睡眠状态,处于这个状态的进程因为等待某种事件的发生而被挂起。;
T:暂停状态或跟踪状态;
X:退出状态,进程即将被销毁;
Z:退出状态,进程成为僵尸进程。
2.1 获取环境变量
获取环境变量的方式:
/* 通过env参数获取环境变量 */
#include <stdio.h>
int main(int argc, char * argv[], char *env[])
{ int i = 0;
while (env[i])
puts(env[i++]);
return 0;
}
/* 通过environ获取环境变量 */
#include <stdio.h>
extern char ** environ;
int main(int argc, char * argv[])
{ int i = 0;
while (environ[i]) puts(environ[i++]);
return 0;
}
2.3 创建进程
函数以拷贝父进程的方式创建子进程。子进程与父进程有相同的代码空间、文件描述符等资源
创建后,子进程与父进程开始并发执行,执行顺序由内核调度算法来决定
fork()对父子进程各返回一次,
父进程:子进程的PID,
子进程:0;
失败: 小于0;
同样运行.c 文件前我们需要makefile
接下来我们运行程序:
$ ./3-fork-print-pid.out
2.4 使用进程创建程序
/* 子程序执行程序-命名为sample3 */
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
extern char * *environ; /* 全局环境变量*/
int main(int argc, char *argv[]) {
int i;
printf("argc=%d\n",argc); /* 打印参数个数 */
printf("args:");
for(i=0;i<argc;i++)
printf("%s",argv[i]);/* 打印参数表 */
printf("\n");
i = 0;
while (environ[i])
puts(environ[i++]); /* 打印环境变量表 */
printf("\n");
return 0;
}
运行命令:
$ ./4-replacement-for-example5.out
运行结果如下:
2.5 使用子进程加载新程序
在创建进程后子进程与父进程有相同的代码空间;实际应用中,需要子进程 去执行另外一个程序,可以在子进程中调用exec族函数它的代码段完全替换为 新的程序,并从这个新进程的main函数开始执行。
/* 子进程加载新程序 */
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
char *env_init[] = {"USER=ujn", "HOME=/home/ujn/", NULL}; /* 为子进程定义环境变量 */
int main(int argc, char *argv[]) {
pid_t pid;
if ((pid = fork()) < 0) { /* 创建进程失败判断 */
perror("fork error");
} else if (pid == 0) { /* fork 对子进程返回 0 */
execle("/home/ujn/sec9/sample4", "sample4", "hello", "world", (char *) 0,env_init);/*子进程装载新程序*/
perror("execle error"); /* execle 失败时才执行 */
exit(-1);
} else {
exit(0); /* 父进程退出 */
}
return -1;
}
2.6 wait()函数
/* 获取子程序退出状态 */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <wait.h>
void print_exit_status(int status) { /* 自定义打印子进程退出状态函数 */
if (WIFEXITED(status)) /* 正常退出,打印退出返回值 */
printf("normal termination, exit status = %d\n", WEXITSTATUS(status));
else if (WIFSIGNALED(status)) /* 因信号异常退出,打印引起退出的信号 */
printf("abnormal termination, signal number = %d\n", WTERMSIG(status));
else
printf("other status\n"); /* 其它错误 */
}
int main(int argc, char *argv[]) {
pid_t pid;
int status;
if ((pid = fork()) < 0) { /* 创建子进程 */
perror("fork error");
exit(-1);
} else if (pid == 0) {
exit(7); /* 子进程调用 exit 函数,参数为 7 */
}
if (wait(&status) != pid) { /* 父进程等待子进程退出,并获取退出状态*/
perror("fork error");
exit(-1);
}
print_exit_status(status); /* 打印退出状态信息 */
if ((pid = fork()) < 0) { /* 创建第二个子进程 */
perror("fork error");
exit(-1);
} else if (pid == 0) {
abort(); /* 子进程调用 abort()函数异常退出 */
}
if (wait(&status) != pid) { /* 父进程等待子进程退出,并获取退出状态*/
perror("fork error");
exit(-1);
}
print_exit_status(status); /* 打印第二个退出状态信息 */
return 0;
}
status是用来保存子进程退出状态的指针·帮助父进程获取其子进程的退出状态
如果父进程未调用wait()函数,则子进程的退出信息将一直保存在内存中。
僵尸进程:子进程先终止,但父进程没有调用wait()。一直占用系统资源。
孤儿进程:被init进程收管,由init进程负责收集它们的退出状态。
运行命令:
$ ./6-wait-child-exit-status.out
结果如下:
2.7 使用daemon创建守护进程
守护进程(Daemon)是运行在后台的一种特殊进程。它独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件,它不需要用户输入就能运行并提供某种服务。
守护进程的父进程是init进程,因为它真正的父进程在fork出该子进程后就先于核子进 程exit退出了,所以它是-个由init领养的孤儿进程。守护进程是非交互式程序,没有控制终端,所以任何输出(无论是向标准输出设备还是标准错误输出设备的输出)都需要特殊处理。
/* 用daemon创建守护进程 */
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <time.h>
int main(void) {
int fd;
time_t curtime;
if (daemon(0, 0) == -1) {
perror("daemon error");
exit(-1);
}
fd = open("/tmp/daemon.log", O_WRONLY | O_CREAT | O_APPEND, 0644);
if (fd < 0) {
perror("open error");
exit(-1);
}
while (1) {
curtime = time(0);
char *timestr = asctime(localtime(&curtime));
write(fd, timestr, strlen(timestr));
sleep(60);
}
close(fd);
return 0;
}
2.8 信号函数sigaction的使用
信号(signal),又称为软中断信号,用来通知进程发生了异步事件。进程之 间可以互相发送信号,内核也可以因为内部事件而给进程发送信号。注意,信号 的作用仅仅是通知进程发生了什么事件,并不向该进程传递任何数据。
我们可以看一下函数原型:
/* sigaction函数的用法 */
#include <unistd.h>
#include <stdio.h>
#include <signal.h>
void ouch(int sig) { /* 信号处理函数 */
printf("\nOuch! - I got signal %d\n", sig);
}
int main(int argc, char *argv[]) {
struct sigaction act;
act.sa_handler = ouch; /* 设置信号处理函数 */
sigemptyset(&act.sa_mask); /* 清空屏蔽信号集 */
act.sa_flags = SA_RESETHAND; /* 设置信号处理之后恢复默认的处理方式 */
sigaction(SIGINT, &act, NULL); /* 设置 SIGINT 信号的处理方法 */
while (1) { /* 进入循环等待信号发生 */
printf("sleeping\n");
sleep(1);
}
return 0;
}
运行如下:
我们看到进程子在循环等待信号,ctrl+c我们给它一个信号;或者ctrl+z退出。
2.9 子进程:等待、退出及输出
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
void print_exit_status(int status) { /* 打印子进程退出状态信息 */
if (WIFEXITED(status))
printf("normal termination, exit status = %d\n", WEXITSTATUS(status));
else if (WIFSIGNALED(status)) /* 是否为信号引起的退出 */
printf("abnormal termination, signal number = %d\n", WTERMSIG(status));
else
printf("other status\n");
}
int main(int argc, char *argv[]) {
pid_t pid;
int status;
if ((pid = fork()) < 0) {
perror("fork error");
exit(-1);
} else if (pid == 0) { /* 子进程 */
while (1) {
printf("chlid sleeping\n");
sleep(1);
}
exit(0);
} else {
sleep(2);
printf("parent send SIGINT to child\n");
kill(pid, SIGINT); /* 向子进程发送 SIGINIT 信号 */
if (wait(&status) != pid) { /* 获取子进程的退出状态 */
perror("wait error");
exit(-1);
}
print_exit_status(status);
}
return 0;
}
运行如下:
子进程经过两次等待,父进程将信号传给子进程后,通过exit()退出,并且输出子进程的退出状态。
2.10 管道
管道,即进程间数据流的通道。通常A进程输出连B进程输入端。符号“|"
1.匿名管道: pipe文件
函数原型:int pipe(int pipefd[2])
2.命名管道: fifo文件:int mkfifo(const char *pathname, mode_t mode)(ps:参数pathname指定了文件名
参数mode则指定了文件的读写权限)
/* 匿名管道使用示例 */
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
int main(int argc, char *argv[])
{
int pipefd[2];
pid_t cpid;
char buf;
if (argc != 2) {//命令行参数只接受一个参数
fprintf(stderr, "Usage: %s <string>\n", argv[0]);
exit(EXIT_FAILURE);
}
if (pipe(pipefd) == -1) { /* 创建匿名管道 */
perror("pipe");
exit(EXIT_FAILURE);
}
cpid = fork(); /* 创建子进程 */
if (cpid == -1) {
perror("fork");
exit(EXIT_FAILURE);
}
if (cpid == 0) { /* 子进程读管道读端 */
close(pipefd[1]); /* 关闭不需要的写端 */
while(read(pipefd[0],&buf,1)>0)
write(STDOUT_FILENO, &buf, 1);
write(STDOUT_FILENO, "\n", 1);
close(pipefd[0]);
_exit(EXIT_SUCCESS);
}
else { /* 父进程写 argv[1]到管道 */
close(pipefd[0]); /* 关闭不需要的读端 */
write(pipefd[1], argv[1], strlen(argv[1]));
close(pipefd[1]); /* 关闭文件发送 EOF,子进程停止读*/
wait(NULL); /* 等待子进程退出 */
exit(EXIT_SUCCESS);
}
}
2.11 通过命名管道发送文件
/* 通过命名管道发送文件 */
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <limits.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <string.h>
#define BUFSIZE 1024 /* 一次最大写 1024 个字节 */
int main(int argc, char *argv[])
{
const char *fifoname = "/tmp/fifo"; /* 命名管道文件名 */
int pipefd, datafd;
int bytes, ret;
char buffer[BUFSIZE];
if (argc != 2) { /* 带文件名参数 */
fprintf(stderr, "Usage: %s < filename >\n", argv[0]);
exit(EXIT_FAILURE);
}
if(access(fifoname, F_OK) < 0) { /* 判断文件是否已存在 */
ret = mkfifo(fifoname, 0777); /* 创建管道文件 */
if(ret < 0) {
perror("mkfifo error");
exit(EXIT_FAILURE);
}
}
pipefd = open(fifoname, O_WRONLY); /* 打开管道文件 */
datafd = open(argv[1], O_RDONLY); /* 打开数据文件 */
if((pipefd > 0) && (datafd > 0)) { /* 将数据文件读出并写到管道文件 */
bytes = read(datafd, buffer, BUFSIZE);
while(bytes > 0) {
ret = write(pipefd, buffer, bytes);
if(ret < 0) {
perror("write error");
exit(EXIT_FAILURE);
}
bytes = read(datafd, buffer, BUFSIZE);
}
close(pipefd);
close(datafd);
}
else {
exit(EXIT_FAILURE);
}
return 0;
}
2.12 通过管道保存文件
/* 通过命名管道保存文件 */
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <limits.h>
#include <string.h>
#define BUFSIZE 1024
int main(int argc, char *argv[])
{
const char *fifoname = "/tmp/fifo"; /* 命名管道文件名,需对应写进程 */
int pipefd, datafd;
int bytes, ret;
char buffer[BUFSIZE];
if (argc != 2) { /* 带文件名参数 */
fprintf(stderr, "Usage: %s < filename >\n", argv[0]);
exit(EXIT_FAILURE);
}
pipefd = open(fifoname, O_RDONLY); /* 打开管道文件 */
datafd = open(argv[1], O_WRONLY|O_CREAT, 0644); /* 打开目标文件 */
if((pipefd > 0) && (datafd > 0)) { /* 将管道文件的数据读出并写入目标文件 */
bytes = read(pipefd, buffer, BUFSIZE);
while(bytes > 0) {
ret = write(datafd, buffer, bytes);
if(ret < 0) {
perror("write error");
exit(EXIT_FAILURE);
}
bytes = read(pipefd, buffer, BUFSIZE);
}
close(pipefd);
close(datafd);
} else {
exit(EXIT_FAILURE);
}
return 0;
}
2.13 共享内存写数据
/* 共享内存写数据 */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <fcntl.h>
#include <sys/stat.h>
#define SHMSIZE 10 /* 共享内存大小, 10 字节 */
#define SHMNAME "shmtest" /* 共享内存名称 */
int main()
{
int fd;
char *ptr;
/* 创建共享内存 */
fd = shm_open(SHMNAME, O_CREAT | O_TRUNC | O_RDWR, S_IRUSR | S_IWUSR);
if (fd<0) {
perror("shm_open error");
exit(-1);
}
ftruncate(fd, SHMSIZE); /* 设置大小为 SHMSIZE */
/* 设置共享内存大小*/
ptr = mmap(NULL, SHMSIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);/*映射共享内存*/
if (ptr == MAP_FAILED) {
perror("mmap error");
exit(-1);
}
*ptr = 18; /* 往起始地址写入 18 */
munmap(ptr, SHMSIZE); /* 取消映射 */
shm_unlink(SHMNAME); /* 删除共享内存 */
return 0;
}
2.14 共享内存读数据
/* 共享内存读数据 */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <fcntl.h>
#include <sys/stat.h>
#define SHMSIZE 10 /* 共享内存大小, 10 字节*/
#define SHMNAME "shmtest" /* 共享内存名称 */
int main()
{
int fd;
char *ptr;
fd = shm_open(SHMNAME, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR); /*创建共享内存 */
if (fd < 0) {
perror("shm_open error");
exit(-1);
}
ptr = mmap(NULL, SHMSIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);/*映射共享内存*/
if (ptr == MAP_FAILED) {
perror("mmap error");
exit(-1);
}
ftruncate(fd, SHMSIZE); /* 设置共享内存大小 */
while (*ptr != 18) { /* 读起始地址,判断值是否为 18 */
sleep(1); /* 不是 18,继续读取 */
}
printf("ptr : %d\n", *ptr); /* 数据是 18,打印显示 */
munmap(ptr, SHMSIZE); /* 取消内存映射 */
shm_unlink(SHMNAME); /* 删除共享内存 */
return 0;
}
2.15 使用有名信号量同步共享内存实例
服务端:
/* 使用有名信号量同步共享内存示例,服务端程序 */
#include <stdio.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>
#include <semaphore.h>
#include <string.h>
#include <errno.h>
#define MAPSIZE 100 /* 共享内存大小, 100 字节 */
int main(int argc, char **argv)
{
int shmid;
char *ptr;
sem_t *semid;
if (argc != 2) { /* 参数 argv[1]指定共享内存和信号量的名字 */
printf("usage: %s <pathname>\n", argv[0]);
return -1;
}
shmid = shm_open(argv[1], O_RDWR|O_CREAT, 0644); /* 创建共享内存对象 */
if (shmid == -1) {
printf( "open shared memory error\n");
return -1;
}
ftruncate(shmid, MAPSIZE); /* 设置共享内存大小 */
/* 将共享内存进行映射 */
ptr = mmap(NULL, MAPSIZE, PROT_READ | PROT_WRITE, MAP_SHARED, shmid, 0);
strcpy(ptr,"\0");
semid = sem_open(argv[1], O_CREAT, 0644, 0); /* 创建信号量对象 */
if (semid == SEM_FAILED) {
printf("open semaphore error\n");
return -1;
}
sem_wait(semid); /* 信号量等待操作,等待客户端修改共享内存 */
printf("server recv:%s",ptr); /* 从共享内存中读取值 */
strcpy(ptr,"\0");
munmap(ptr, MAPSIZE); /* 取消对共享内存的映射 */
close(shmid); /* 关闭共享内存 */
sem_close(semid); /* 关闭信号量 */
sem_unlink(argv[1]); /* 删除信号量对象 */
shm_unlink(argv[1]); /* 删除共享内存对象 */
return 0;
}
客户端:
/* 使用有名信号量同步共享内存示例,客户端程序 */
#include <stdio.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>
#include <semaphore.h>
#include <string.h>
#include <errno.h>
#define MAPSIZE 100 /* 共享内存大小, 100 字节 */
int main(int argc, char **argv)
{
int shmid;
char *ptr;
sem_t *semid;
if (argc != 2) {
printf("usage: %s <pathname>\n", argv[0]); /* 参数 argv[1]指定共享内存和信号量的名字 */
return -1;
}
shmid = shm_open(argv[1], O_RDWR, 0); /* 打开共享内存对象 */
if (shmid == -1) {
printf( "open shared memory error.\n");
return -1;
}
ftruncate(shmid, MAPSIZE); /* 设置共享内存大小 */
/* 将共享内存进行映射 */
ptr = mmap(NULL, MAPSIZE, PROT_READ | PROT_WRITE, MAP_SHARED, shmid, 0);
semid = sem_open(argv[1], 0); /* 打开信号量对象 */
if (semid == SEM_FAILED) {
printf("open semaphore error\n");
return -1;
}
printf("client input:");
fgets(ptr, MAPSIZE, stdin); /* 从标准输入读取需要写入共享内存的值 */
sem_post(semid); /* 通知服务器端 */
munmap(ptr, MAPSIZE); /* 取消对共享内存的映射 */
close(shmid);
sem_close(semid);
return 0;
}
3.开发板
3.1 下载QEMU
- 下载成功后,进入ubuntu-18.04_imx6ul_qemu_system目录,执行install_sdl.sh
- 必须在Ubunut的桌面环境下启动终端,执行./qemu-imx6ull-gui.sh
我们可以看到下面界面
3.2 打开LCD图像和屏幕
输入命令:
$ fb-test
$ cd myfb-test
输入命令:
$ ./myfb-test /dev/fb0
3.3串口EEPROM
$ cd
$ i2cdetect -l #列出所有ic2总线
$ i2cdetect -y 0 #列出总线0上的设备
矩阵的最左列是十位,最上面是个位。
3.5 命令控制LED
安装驱动:
$ cd
$ cd led_driver_qemu/
$ insmod 100ask_led.ko
控制LED零号灯亮,控制LED一号灯灭:
$ ./ledtest /dev/100ask_led0 on
$ ./ledtest /dev/100ask_led1 off
3.6 按键控制LED
安装驱动:
$ cd
$ cd button_driver_qemu/
$ insmod button_drv.ko #扫描你的按键
$ insmod board_100ask_qemu_imx6ull.ko
启动按键控制:
$ ./button_led_test
特别鸣谢:
https://blog.csdn.net/Zeilo/article/details/124084107?spm=1001.2014.3001.5502