文章目录
- 进程与线程
- 实验一:创建进程
- 基础版:创建父子线程 fork
- 基础版:父子线程交替运行
- 基础版:创建进程 文件写入
- 练习版:创建线程 子读父阻塞
- 实验二:线程共享进程中的数据
- 实验三:多线程实现单词统计工具
进程与线程
实验一:创建进程
1、学会通过基本的linux进程控制函数,由父进程创建子进程,并实现协同工作
2、创建两个进程,让子进程读取一个文件,父进程等待子进程读完文件后继续执行
注意:
fork创建的新进程被称为子进程,该函数被调用一次,但返回两次。两次返回的区别是:在子进程中的返回值是0,而在父进程中的返回值则是新进程的进程ID。
创建子进程,父进程哪个先运行根据系统调度且赋值父进程的内存空间。
vfork创建子进程,但子进程先运行且不复制父进程的内存空间
基础版:创建父子线程 fork
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
int main(void){
printf("pid:%d\n",getpid());
pid_t pid;
pid = fork(); // 创建子进程
// 在fork之后会运行两个进程(父进程、子进程)
if(pid <0){
perror("fork error");
} else if(pid>0){
// 父进程(在父进程中fork返回的是子进程的pid)
printf("I am parent process pid is %d,ppid is %d,fork return is %d\n",
getpid(),getppid(),pid);
} else {
// 子进程(在子进程中fork返回的是0)
printf("I am child process pid is %d,ppid is %d,fork return is %d\n",
getpid(),getppid(),pid);
}
printf("pid:%d\n",getpid());
sleep(1); // 睡眠
return 0;
}
编译源文件:
root@ubuntu:/home/course/linux# gcc -o /home/course/linux/out/createThread /home/course/linux/createThread.c
-c
表示只编译(compile)源文件但不链接,会把.c或.cc的c源程序编译成目标文件,一般是.o文件。-o
用于指定输出(out)文件名。不用-o的话,一般会在当前文件夹下生成默认的a.out文件作为可执行程序。
root@ubuntu:/home/course/linux/# out/createThread # 直接运行
可以看到:
-
父进程(在父进程中fork返回的是子进程的pid)
-
子进程(在子进程中fork返回的是0)
返回顶部
基础版:父子线程交替运行
使用sleep()函数,实现线程的睡眠,每个进程运行后休眠一段时间,这时按照cpu的资源调度,使得其他进程运行。(若休眠时间短则会出现二次调用的情况)
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
int main(void){
printf("pid:%d\n",getpid());
pid_t pid;
pid = fork(); // 创建子进程
// 在fork之后会运行两个进程(父进程、子进程)
if(pid <0){
perror("fork error");
} else if(pid>0){
for(int i=0;i<10;i++){
// 父进程(在父进程中fork返回的是子进程的pid)
printf("I am parent process pid is %d\n",getpid());
sleep(1);
}
} else {
for(int i=0;i<10;i++){
// 子进程(在子进程中fork返回的是0)
printf("I am child process pid is %d\n",getpid());
sleep(1);
}
}
return 0;
}
编译运行:
root@ubuntu:/home/course/linux# vi createThread1.c
root@ubuntu:/home/course/linux# gcc -o /home/course/linux/out/createThread1 /home/course/linux/createThread1.c
返回顶部
基础版:创建进程 文件写入
父进程使用两种IO的形式进行文件的写入,默认当前路径下创建文件。注意区分缓存的概念以及文件的内容输出。
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
int main(void){
printf("pid:%d\n",getpid());
/*父进程调用写文件*/
FILE *fp =fopen("s.txt","w");
int fd = open("s_fd.txt",O_WRONLY|O_CREAT|O_TRUNC,S_IRWXU|S_IRWXG);
char *s = "hello world!";
ssize_t size = strlen(s)*sizeof(char);
// 标准IO函数 - 带缓存 - 全缓存
fprintf(fp,"s:%s,pid:%d",s,getpid());
// 内核提供的IO系统 - 不带缓存
write(fd,s,size);
pid_t pid;
pid = fork(); // 创建子进程
// 在fork之后会运行两个进程(父进程、子进程)
if(pid <0){
perror("fork error");
} else if(pid>0){
for(int i=0;i<10;i++){
// 父进程(在父进程中fork返回的是子进程的pid)
printf("I am parent process pid is %d\n",getpid());
sleep(1);
}
} else {
for(int i=0;i<10;i++){
// 子进程(在子进程中fork返回的是0)
printf("I am child process pid is %d\n",getpid());
sleep(1);
}
}
// 父子进程都要执行 - 写入各自缓存
fprintf(fp,"pid:%d",getpid());
return 0;
}
可以看到编译正常运行;
并且在目录下新生成了 s_fd.txt、s.txt 文件,当我们查看文件内容的时候,会发现两个文件中的内容有偏差:
使用内核提供的IO系统 - 不带缓存,是直接将内容写入,而标准IO函数 - 带缓存,写的内容是:fprintf(fp,"s:%s,pid:%d",s,getpid());
,并且在最后的时候父子进程都要执行一次标准的IO,將各自的缓存内容写入到文件中去,所以会重复内容一次。
返回顶部
练习版:创建线程 子读父阻塞
实验说明:
- 学会通过基本的Linux进程控制函数,由父进程创建子进程,并实现协同工作。创建两个进程,让子进程读取一个文件,父进程等待子进程读完文件后继续执行。
解决方案:
- 进程协同工作就是要协调好两个或两个以上的进程,使之安排好先后次序并依次执行,可以用wait()或者waitpid()函数来实现这一点。当只需要等待任一子进程运行结束时,可在父进程中调用wait()函数。若需要等待某一特定子进程的运行结果时,需调用waitpid()函数,它是非阻塞型函数。
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include<sys/types.h>
#include<sys/wait.h>
#define COLMAX 1024 //每一个字符串的最大长度(列)
#define ROWMAX 64 //字符串最大个数(行)
/*
本代码实现用子进程打开同目录下的s_fd.txt文件
并且父进程输出内容
*/
int main(void) {
int p_id = -1;
//子进程创建失败
if ((p_id = fork()) == -1) {
printf("Process_1 Create Error\n");
} else if (p_id == 0) { //子进程部分
printf("%d Process Start Work\n", getpid());
char text[ROWMAX][COLMAX] = {0};
FILE *fp = fopen("s_fd.txt", "r+");//打开文件
if (fp == NULL) { //打开文件失败
printf("Fail to open file!\n");
} else {
int i = 0;
while ((fscanf(fp, "%s", text[i])) != EOF) {
printf("%s\n", text[i]);
i++;
sleep(1); //等待1s方便查看输出
}
}
fclose(fp);
exit(0);
}
//父进程部分
waitpid(p_id, NULL, 0);//阻塞等待
printf("%d process is end\n", p_id);
return 0;
}
rse/linux# vi createThread3.c
root@ubuntu:/home/course/linux# gcc -o /home/course/linux/out/createThread3 /home/course/linux/createThread3.c
root@ubuntu:/home/course/linux# out/createThread3
运行结果:
返回顶部
实验二:线程共享进程中的数据
实验说明:
- 了解线程与进程之间的数据共享关系。创建一个线程,在线程中更改进程中的数。
解决方案:
- 在进程中定义共享数据,在线程中直接引用并输出该数据。
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include<sys/types.h>
#include<sys/wait.h>
static int sharedata=4; // 共享数据
void *create(void *arg){
printf("new pthread...\n");
printf("sharedata data = %d \n",sharedata);
sharedata = 3;
return (void *)(0);
}
int main(void){
pthread_t mythread ;
sharedata=5; // 修改变量值
int error = 0;
error = pthread_create(&mythread,NULL,create,NULL);
if(error){
printf("pthread_create is not created...\n");
return -1;
}
sleep(1);
printf("pthread_create is ok...\n");
printf("And shared data = %d\n \n",sharedata);
return 0;
}
root@ubuntu:/home/course/linux# vi createThread4.c
root@ubuntu:/home/course/linux# gcc -o /home/course/linux/out/createThread4 /home/course/linux/createThread4.c -l pthread
root@ubuntu:/home/course/linux# out/createThread4
运行结果:
如有报错,参见:https://blog.csdn.net/u014470361/article/details/83214911
返回顶部
实验三:多线程实现单词统计工具
实验说明:
- 多线程实现单词统计工具。
解决方案:
区分单词原则:
- 凡是一个非字母或数字的字符跟在字母或数字的后面,那么这个字母或数字就是单词的结尾。
- 允许线程使用互斥锁来修改临界资源,确保线程间的同步与协作。
- 如果两个线程需要安全地共享一个公共计数器,需要把公共计数器加锁。线程需要访问称为互斥锁的变量,它可以使线程间很好地合作,避免对于资源的访问冲突。
#include <stdio.h>
#include <pthread.h>
#include <ctype.h>
#include <stdlib.h>
#include <fcntl.h>
pthread_mutex_t counter_clock=PTHREAD_MUTEX_INITIALIZER;
int main(int ac,char *av[]){
void *count_words(void *);
if(ac!=3){
printf("Usage:%s file1 file2\n",av[0]);
exit(1);
}
/*分別以av[1]、av[2]作为参数,创建两个线程t1、t2,线程t1、t2进入等待状态,输出统计的单词总数*/
pthread_t tidp1,tidp2;
int error1,error2;
error1=pthread_create(&tidp1,NULL,count_words,av[1]);
error2=pthread_create(&tidp2,NULL,count_words,av[2]);
pthread_join(tidp1,NULL);
pthread_join(tidp2,NULL);
return 0;
}
void *count_words(void *f){
char *filename=(char *)f;
FILE *fp;
int c,prevc='\0';
int total_words=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);
printf("total_words=%d\n",total_words);
}else{
perror(filename);
}
return NULL;
}
创建两个包含英文单词的txt
文件:
root@ubuntu:/home/course/linux# vi createThread5.c
root@ubuntu:/home/course/linux# gcc -o /home/course/linux/out/createThread5 /home/course/linux/createThread5.c -l pthread
root@ubuntu:/home/course/linux# ./out/createThread5 ./a.txt ./b.txt
total_words=5
total_words=3
运行结果:
返回顶部