0%

C-学习笔记

makefile

1
2
3
4
5
6
7
8
9
10
11
12
13
14
cc = gcc
prom = multisum
deps = $(shell find ./ -name "*.h")
src = $(shell find ./ -name "*.c")
obj = $(src:%.c=%.o)

$(prom): $(obj)
$(cc) -o $(prom) $(obj) -lpthread

%.o: %.c $(deps)
$(cc) -c $< -o $@

clean:
rm -rf $(obj) $(prom)

线程

使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include<stdio.h>
#include <pthread.h>

void thread(void) {
int i;
for(i = 0; i < 30; i++) {
printf("This is a pthread.\n");
}
}

int main(int argc, char const *argv[]) {
pthread_t id;
int i, ret;
ret = pthread_create(&id, NULL, (void *) thread, NULL); // 成功返回0,错误返回错误编号
if(ret != 0) {
printf ("Create pthread error!\n");
return -1;
}
pthread_join(id, NULL);
for(i = 0; i < 30; i++) {
printf("This is the main process.\n");
}

return 0;
}
  • pthread_t id:

  • int pthread_create(&id, NULL, (void *) thread, (void *)arg):

    1. 第一个参数为指向线程标识符的指针,第二个参数用来设置线程属性,第三个参数是线程运行函数的起始地址,最后一个参数是运行函数的参数。当创建线程成功时,函数返回0,若不为0则说明创建线程失败,常见的错误返回代码为EAGAIN和EINVAL。前者表示系统限制创建新的线程,例如线程数目过多了;后者表示第二个参数代表的线程属性值非法。创建线程成功后,新创建的线程则运行参数三和参数四确定的函数,原来的线程则继续运行下一行代码。
    2. 多个参数:必须申明一个结构体来包含所有的参数,然后再传入线程函数。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
struct  parameter {
int size,
int count;
};

// 然后在main函数将这个结构体指针,作为void *形参的实际参数传递
struct parameter arg;
pthread_create(&ntid, NULL, fn,& (arg));

// 函数中需要定义一个parameter类型的结构指针来引用这个参数
void thr_fn(void *arg) {
struct parameter *pstru;
pstru = ( struct parameter *) arg;
// 然后在这个函数中就可以使用指针来使用相应的变量的值了。
}
  • int pthread_join(id, NULL);

    函数pthread_join用来等待一个线程的结束。第一个参数为被等待的线程标识符,第二个参数为一个用户定义的指针,它可以用来存储被等待线程的返回值。这个函数是一个线程阻塞的函数,调用它的函数将一直等待到被等待的线程结束为止,当函数返回时,被等待线程的资源被收回。一个线程的结束有两种途径,一种是像上面的例子一样,函数结束了,调用它的线程也就结束了;另一种方式是通过函数pthread_exit来实现。

  • void pthread_exit(arg);

    唯一的参数是函数的返回代码,只要pthread_join中的第二个参数thread_return不是NULL,这个值将被传递给thread_return。

  • 在主线程中初始化锁为解锁状态

    pthread_mutex_t mutex;
    pthread_mutex_init(&mutex, NULL);

  • 在编译时初始化锁为解锁状态

    锁初始化 pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

  • 访问对象时的加锁操作与解锁操作

    加锁 pthread_mutex_lock(&mutex)
    释放锁 pthread_mutex_unlock(&mutex)

信号量

信号量本质上是一个非负数的整数计数器,它也被用来控制对公共资源的访问。当公共资源增加的时候,调用信号量增加函数sem_post()对其进行增加,当公共资源减少的时候,调用函数sem_wait()来减少信号量。其实,我们是可以把锁当作一个0-1信号量的。

在使用semaphore之前,需要先引入头文件semaphore.h

  • 初始化信号量: int sem_init(sem_t *sem, int pshared, unsigned int value);

    • 成功返回0,失败返回-1
    • sem:指向信号量结构的一个指针
    • pshared: 不是0的时候,该信号量在进程间共享,否则只能为当前进程的所有线程们共享
    • value:信号量的初始值
  • 信号量减1操作,当sem=0的时候该函数会堵塞 int sem_wait(sem_t *sem);

    • 成功返回0,失败返回-1
    • 参数
    • sem:指向信号量的一个指针
  • 信号量加1操作 int sem_post(sem_t *sem);

    • 参数与返回同上
  • 销毁信号量 int sem_destroy(sem_t *sem);

    • 参数与返回同上
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <semaphore.h>

#define MAXSIZE 10

int stack[MAXSIZE];
int size = 0;
sem_t sem;

// 生产者
void provide_data(void) {
int i;
for (i=0; i< MAXSIZE; i++) {
stack[i] = i;
sem_post(&sem); //为信号量加1
}
}

// 消费者
void handle_data(void) {
int i;
while((i = size++) < MAXSIZE) {
sem_wait(&sem);
printf("乘法: %d X %d = %d\n", stack[i], stack[i], stack[i]*stack[i]);
sleep(1);
}
}

int main(void) {

pthread_t provider, handler;

sem_init(&sem, 0, 0); //信号量初始化
pthread_create(&provider, NULL, (void *)handle_data, NULL);
pthread_create(&handler, NULL, (void *)provide_data, NULL);
pthread_join(provider, NULL);
pthread_join(handler, NULL);
sem_destroy(&sem); //销毁信号量

return 0;
}

实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <time.h>

static int N = 0;
static long M = 0;
static long sum = 0;
static long count = 0;
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

const static char *fileInput = "input.txt";
const static char *fileOutput = "output.txt";

void getInput() {
FILE *pFile = fopen(fileInput, "r");
char line[20];
if (pFile == NULL) {
perror("Error opening file");
} else {
fseek(pFile, 2, SEEK_SET);
if (fgets(line, 20, pFile) != NULL) {
N = atoi(line);
}

fseek(pFile, ftell(pFile)+2, SEEK_SET);
if (fgets(line, 20, pFile) != NULL) {
M = atol(line);
}
fclose(pFile);
}
}

void setOutput() {
FILE *pFile = fopen(fileOutput, "w");
if (pFile == NULL) {
perror("Error opening file");
} else {
fprintf(pFile,"%ld", sum);
fclose(pFile);
}
}

void threadSum() {
while (count <= M) {
pthread_mutex_lock(&mutex);
sum += count++;
printf("the thread result = %ld\n", sum);
pthread_mutex_unlock(&mutex);
}
}

int main(int argc, char const *argv[]) {
clock_t start = clock();

getInput();

printf("N = %d\n", N);
printf("M = %ld\n", M);

int ret;
pthread_t thread[N];
for (int i = 0; i < N; i++) {
ret = pthread_create(&thread[i], NULL, (void *)threadSum, NULL);

if(ret != 0) {
printf("create pthread-%d error!\n", i);
return -1;
}
}

for (int i = 0; i < N; i++) {
pthread_join(thread[i], NULL);
}

setOutput();

printf("the final result = %ld\n", sum);
printf("the tatol time = %ld\n", clock()-start);

return 0;
}

线程

fork

fork()会产生一个和父进程完全相同的子进程,可以认为fork后,这两个相同的虚拟地址指向的是不同的物理地址。但实际上,Linux为了提高 fork 的效率,采用了 copy-on-write 技术(写时复制),fork后,这两个虚拟地址实际上指向相同的物理地址(内存页),只有任何一个进程试图修改这个虚拟地址里的内容前,两个虚拟地址才会指向不同的物理地址(新的物理地址的内容从原物理地址中复制得到)。

共享内存

1
2
3
4
5
#include <sys/shm.h>
void *shmat(int shm_id, const void *shm_addr, int shmflg);
int shmctl(int shm_id, int cmd, struct shmid_ds *buf);
int shmdt(const void *shm_addr);
int shmget(key_t key, size_t size, int shmflg);

同步互斥

  • 信号量(PV)
  • 管程
  1. #include <semaphore.h>

  2. sem_init函数

    • 函数作用:初始化信号量
    • 函数原型:int sem_init(sem_t *sem,int pshared, unsigned int value)
    • 参数:sem:信号量指针
    • Pshared:决定信号量能否在几个进程间共享,一般取0
    • Value:信号量的初始值
  3. 信号的操作

    • int sem_wait(sem_t *sem); P操作
    • int sem_try_wait(sem_t *sem);
    • int sempost(sem_t *sem); V操作
    • int sem_getvalue(sem_t *sem);
    • int sem_destroy(sem_t *sem); 销毁信号

实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/shm.h>
#include <semaphore.h>

#define PERM S_IRUSR|S_IWUSR

static int N = 0;
static long M = 0;

const static char *fileInput = "input.txt";
const static char *fileOutput = "output.txt";

typedef struct {
long sum;
long count;
sem_t S;
} Process;

void getInput() {
FILE *pFile = fopen(fileInput, "r");
char line[20];
if (pFile == NULL) {
perror("Error opening file");
} else {
fseek(pFile, 2, SEEK_SET);
if (fgets(line, 20, pFile) != NULL) {
N = atoi(line);
}

fseek(pFile, ftell(pFile)+2, SEEK_SET);
if (fgets(line, 20, pFile) != NULL) {
M = atol(line);
}
fclose(pFile);
}
}

void setOutput(long result) {
FILE *pFile = fopen(fileOutput, "w");
if (pFile == NULL) {
perror("Error opening file");
} else {
fprintf(pFile,"%ld", result);
fclose(pFile);
}
}

int main(int argc, char const *argv[]) {
clock_t start = clock();

getInput();

printf("N = %d\n", N);
printf("M = %ld\n", M);

int shm_id;
Process *pProcess;

if((shm_id = shmget(IPC_PRIVATE, sizeof(Process), PERM)) == -1){
printf("Create Share Memory Error.\n");
return -1;
}

pProcess = (Process *)shmat(shm_id, NULL, 0);
pProcess->sum = 0;
pProcess->count = 0;

sem_init(&pProcess->S, 0, 1);

pid_t pid;
int status;

for (int i = 0; i < N; i++) {
pid =fork();
if (pid == 0 || pid == -1) {
break;
}
}

if(pid==-1) {
printf("fail to fork!\n");
return -1;
} else if(pid == 0) {
while (pProcess->count <= M) {
sem_wait(&pProcess->S);
pProcess->sum += pProcess->count++;
printf("the process result = %ld\n", pProcess->sum);
sem_post(&pProcess->S);
}
} else {
wait(&status);

sem_destroy(&pProcess->S);
setOutput(pProcess->sum);

printf("the final result = %ld\n", pProcess->sum);
printf("the tatol time = %ld\n", clock()-start);
}

return 0;
}