Linux进程与线程编程
1. 创建进程
1.1进程说明
进程有生命周期,由fork()
函数而创建,通过shedule()
函数而执行,由wait()
函数而等待,最后可强行终止运行进程,也可当完成任务后进程执行exit()
函数自动退出。
通过基本的Linux进程控制函数,由父进程创建子进程,并实现协同工作。创建两个进程,让子进程读取一个文件,父进程等待子进程读完文件后继续执行。
1.2 程序框架
fork()/exec()组合是典型的Linux新进程产生模式,通常先用fork()创建新进程,然后新进程通过调用exec()类执行自己的任务。
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
#define COL 1024
int main(void){
pid_t pc; //创建进程组ID
pc = fork();
if (pc < 0){
printf("error ocurred!\n");
}else if (pc == 0){ //子进程创建成功
printf("This is child process with pid of %d\n",getpid());
char line[COL] = {0};
FILE *file = fopen("/home/hcx/桌面/text/11.txt","r+");
if (!file){
printf("File opening failed\n");
}else {
printf("scan something to this file:"); //父进程
while ((fscanf(file,"%s ",line)) != EOF)
{
printf("%s",line);
sleep(1); //睡眠1秒钟进行一次操作
}
printf("\n");
fclose(file);
}
return 0;
}
else { //父进程
wait(NULL); //等待子进程
printf("This is father process\n");
}
return 0;
}
其中,用于子进程工作的文件11.txt内容是:
用fork()
创建进程时,子进程只是完全复制父进程的资源,子进程的执行独立于父进程,不改变父进程创建子进程之前定义的变量值;如果要控制父与子进程的执行顺序,必须由父进程使用函数wait(NULL)
(返回任意终止状态的子进程)或waitpid()
(等待特定的子进程)。
而vfork()
函数创建的子进程共享地址空间,即子进程运行在父进程的地址空间上,改变父进程创建子进程之前定义的变量值,创建子进程后,父进程会被阻塞,直到子进程执行exec()或exit()。
在Linux中可通过使用exec()类函数实现进程对其他程序的引用,对系统而言,还是同一个进程,只不过运行另一个可执行程序。
1.3 运行结果
当fork()调用成功后,父子进程完成各自的任务,但当父进程的工作告一段落,需要用到子进程的结果时,它就停下来调用wait(),直到子进程运行结束,然后利用子进程的结果继续执行,这样就解决了父子进程间的协同工作问题。
运行结果如图:
2 线程共享进程中的数据
2.1 说明
了解线程与进程之间的数据共享关系,创建一个线程,在线程中更改进程中的数据。
2.2 程序框架
在Linux系统中,可通过函数clone()
及pthread_create()
创建线程,两者区别在于clone()
创建一个内核支持的用户线程,由内核调度;而pthread_create()
创建一个线程库支持的用户线程,对内核不可见,由线程库调度。
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
static int shdata = 4;
void *create(void *arg){
printf("new pthread ...\n");
printf("shared data = %d \n", shdata);
return (void *)0;
}
int main (int argc, char *argv[]){
int error;
pthread_t tidp;
error = pthread_create(&tidp, NULL, create, NULL); //创建新线程
/*
当进程第1次调用pthread_create()创建一个线程时就会创建并启动管理线程
*/
if (error != 0){
printf("pthread_create is not created...");
return -1;
}
sleep(1); //休眠1秒
printf("prthread_create success...");
return 0;
}
注意:因为pthread库不是Linux系统的库,所以在进行编译的时候要加上-lpthread,否则编译通不过。如:
gcc -o test test.c -lpthread
2.3 运行结果
3 多线程实现单词统计工具
3.1 实验说明
多线程实现单词统计工具。
3.2 程序框架
允许线程使用互斥锁来修改临界资源,确保线程间的同步与协作。如果两个线程需要安全地共享一个公共计数器,需要把公共计数器加锁。线程需要访问称为互斥锁的变量,它可以使线程间很好地合作,避免对于资源的访问冲突。
#include <stdio.h>
#include <pthread.h>
#include <ctype.h>
pthread_mutex_t counter_clock = PTHREAD_MUTEX_INITIALIZER;
static int total_words = 0;
int main(int ac, char *av[]){
void *count_words(void *);
if (ac != 3){
printf("Usage:%s file1.txt file2.txt\n",av[0]);
return 0;
}
int t1, t2;
pthread_t a_thread, b_thread;
t1 = pthread_create(&a_thread, NULL, count_words, av[1]); /* 以a_thread 和 b_thread 作为参数,创建两个线程t1和t2 */
t2 = pthread_create(&b_thread, NULL, count_words, av[2]);
if (t1 != 0 || t2 !=0){
perror("Thread creation failed");
return -1;
}
//分别让线程t1和t2进入等待态
t1 = pthread_join(a_thread, NULL);
t2 = pthread_join(b_thread, NULL);
if (t1 != 0 || t2 != 0){
perror("Thread join failed");
return -1;
}
//统计两个线程对应的文件的单词总数
printf("The total words is:%d\n", total_words);
return 0;
}
void *count_words(void *f){
char *filename = (char *)f;
FILE *fp;
int c,prevc = '\0';
if ((fp = fopen(filename, "r")) != NULL){
while((c = getc(fp)) != EOF){
if (!isalnum(c) && isalnum(prevc)){
pthread_mutex_lock(&counter_clock);
total_words++;
pthread_mutex_unlock(&counter_clock);
}
prevc = c;
}
fclose(fp);
}else {
perror(filename);
}
return NULL;
}
其中两个线程所用到的文件内容分别为:
file1文件:
file2文件: