(一)使用线程完成图片拷贝
用线程完成图片拷贝,要求一个线程拷贝一半,另一个线程拷贝另一半。
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
void* write_tailhalf(void* buf){
sleep(10); //防止主线程还没有写完,分支进程就写数据了
printf("后一半开始写了\n");
int fd1 = **((int**)buf+0);
int fd2 = **((int**)buf+1);
int size = **((int**)buf+2);
//写入后一半数据
char ch = '\0';
for(int i = 0; i < size - size/2; i++){
if(-1 == read(fd1, &ch, sizeof(ch))){
perror("read");
return NULL;
}
if(-1 == write(fd2, &ch, sizeof(ch))){
perror("write");
return NULL;
}
memset(&ch, 0, sizeof(ch));
}
fprintf(stderr, "后一半写完\n");
//结束分支线程,解除主线程阻塞
pthread_exit(NULL);
}
int main(int argc, const char *argv[])
{
if(argc < 3){
printf("请输入要复制的文件名字和复制后的文件名字\n");
return -1;
}
//以读的方式打开argv[1]文件
int fd1 = open(argv[1], O_RDONLY);
if(-1 == fd1){
perror("open");
return -1;
}
//以写的方式打开argv[2]文件
int fd2 = open(argv[2], O_WRONLY | O_CREAT | O_TRUNC, 0777);
if(-1 == fd2){
perror("open");
return -1;
}
//获取文件大小
struct stat state;
if(-1 == stat(argv[1], &state)){
perror("state");
return -1;
}
int size = state.st_size;
//创建新线程
pthread_t tid;
int *p[3];
*(p+0) = &fd1;
*(p+1) = &fd2;
*(p+2) = &size;
//传递指针数组
if(pthread_create(&tid, NULL, write_tailhalf, (void*)p) != 0){
fprintf(stderr, "pthread_create failed __%d__\n", __LINE__);
return -1;
}
char buf = '\0';
//复制前一半数据
for(int i = 0; i < size / 2; i++){
if(-1 == read(fd1, &buf, sizeof(buf))){
perror("read");
return -1;
}
if(-1 == write(fd2, &buf, sizeof(buf))){
perror("write");
}
memset(&buf, 0, sizeof(buf));
}
fprintf(stdout, "前一半写完\n");
//阻塞主线程,等待后一半写完
pthread_join(tid, NULL);
return 0;
}
(二)分析程序出错原因
出错:运行过程中,线程A循环打印全局变量buf字符串,并不是按照只有结果1234567或者7654321打印的.
个人思考:Linux的进程调度机制为:时间片轮询机制。即GPU以ms级别的速度在进程中来回切换。进程是资源分配的最小单位。
而同一个进程下的线程。共享该进程下的所有资源。那么我全局变量buf也是被线程A和线程B共享的,我在线程A中打印buf,在进程B中翻转buf,同一进程下的线程执行顺序可能不相同。
假如进程的时间片为10ms,其中线程A占2ms,线程B站3ms,主线程占3ms,当CPU执行线程B即翻转字符串,立马切换到了线程A即打印字符串,此时字符串自由部分发生交换,所以打印的结果错误·。
#include <stdio.h>
#include <string.h>
#include <pthread.h>
char buf[] = "1234567"; //定义一个全局变量
void swap(char* ch1, char* ch2){
char tmp = *ch1;
*ch1 = *ch2;
*ch2 = tmp;
}
void* print_buf(void* argv){
while(1){
printf("%s\n", buf);
}
}
void* buf_reverse(void* argv){
int len = strlen(buf);
int i;
while(1){
for(i = 0; i < len/2; i++){
swap(&buf[i], &buf[len - i -1]);
}
i = 0;
}
}
int main(int argc, const char *argv[])
{
pthread_t tida;
pthread_t tidb;
//创建A线程
if(pthread_create(&tida, NULL, print_buf, NULL) < 0){
fprintf(stderr, "pthread_create failed __%d__\n", __LINE__);
return -1;
}
//创建B进程
if(pthread_create(&tidb, NULL, buf_reverse, NULL) < 0){
fprintf(stderr, "pthread_create failed __%d__\n", __LINE__);
}
//主线程阻塞
pthread_join(tida, NULL);
printf("主线程退出\n");
return 0;
}
(三)父子进程拷贝图片修改
将父子进程拷贝图片的那题目进行修改:
父子进程的代码单独分离到其他的可执行二进制程序用,用exec函数族进行组合。
提示:学习使用sprintf函数 atoi函数。
sprintf:将数据格式化输出到字符串中。char str[32]; int a = 10; sprintf(str, "%d", a);
atio:将数字型字符串转换成整数 "10" ---> atoi ---> 整型10
1、方法1:当打开文件在fork之前
主代码
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
int main(int argc, const char *argv[])
{
if(argc < 3){
printf("请输入需要拷贝文件名和拷贝后的文件名\n");
return -1;
}
//以读的方式打开argv[1]文件
int fd1 = open(argv[1], O_RDONLY);
if(-1 == fd1){
perror("fd1");
return -1;
}
//以写的方式打开argv[2]文件,如果文件不存在,则创建,如果存在,清空
int fd2 = open(argv[2], O_WRONLY | O_CREAT | O_TRUNC, 0777);
if(-1 == fd2){
perror("fd2");
return -1;
}
//获取argv[1]文件的大小
struct stat state;
if(-1 == stat(argv[1], &state)){
perror("stat");
return -1;
}
pid_t pid = fork(); //创建一个子进程
if(pid > 0){ //父进程
//写前一半数据
off_t head_half = state.st_size / 2;
char buf;
while(head_half--){
if(-1 == read(fd1, &buf, sizeof(buf))){
perror("read");
return -1;
}
if(-1 == write(fd2, &buf, sizeof(buf))){
perror("write");
return -1;
}
}
}else if(0 == pid){ //子进程
sleep(10);
//写入后一半数据
//父进程和子进程共用同一个文件描述符表
//所以不需要修改文件偏移量
//设置文件偏移量
char str_fd1[3] = {0};
char str_fd2[3] = {0};
char str_size[32] = {0};
//将后一半文件大小、文件描述符转化为字符串,传参给单独分离的程序使用
sprintf(str_size, "%ld", state.st_size - state.st_size/2);
sprintf(str_fd1, "%d", fd1);
sprintf(str_fd2, "%d", fd2);
execl("./proj", "proj", str_size, str_fd1, str_fd2, NULL);
}else{
perror("fork");
return -1;
}
close(fd1);
close(fd2);
return 0;
}
进程重生代码
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, const char *argv[])
{
//进行后一半的复制
char buf = '\0';
//将字符换转换为整型
int size = atoi(argv[1]);
int fd1 = atoi(argv[2]);
int fd2 = atoi(argv[3]);
for(int i = 0; i < size; i++){
if(-1 == read(fd1, &buf, sizeof(buf))){
perror("read");
return -1;
}
if(-1 == write(fd2, &buf, sizeof(buf))){
perror("write");
return -1;
}
memset(&buf, 0, 1);
}
return 0;
}
2、方法二:当打开文件在fork之后
主代码
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
int main(int argc, const char *argv[])
{
if(argc < 3){
printf("请输入需要拷贝文件名和拷贝后的文件名\n");
return -1;
}
//以读的方式打开argv[1]文件
int fd1 = open(argv[1], O_RDONLY);
if(-1 == fd1){
perror("fd1");
return -1;
}
//防止先前的数据造成干扰
int fd_w = open(argv[2], O_WRONLY | O_CREAT | O_TRUNC, 0777);
if(-1 == fd_w){
perror("open");
return -1;
}
close(fd_w);
//获取argv[1]文件的大
off_t size = lseek(fd1, 0, SEEK_END);
//偏移回去
lseek(fd1, 0, SEEK_SET);
pid_t pid = fork(); //创建一个子进程
if(pid > 0){ //父进程
int fd2 = open(argv[2], O_WRONLY | O_CREAT, 0777);
if(-1 == fd2){
perror("open");
return -1;
}
printf("fd2 = %d\n", fd2);
printf("size = %ld\n", size);
//写前一半数据
off_t head_half = size / 2;
char buf;
for(int i = 0; i < head_half; i++){
if(-1 == read(fd1, &buf, sizeof(buf))){
perror("read");
return -1;
}
if(-1 == write(fd2, &buf, sizeof(buf))){
perror("write");
return -1;
}
memset(&buf, 0, 1);
}
fprintf(stderr, "前一半拷贝成功\n");
close(fd2);
}else if(0 == pid){ //子进程
sleep(10);
char str_fd1[5] = {0};
char str_size[32] = {0};
//将文件描述符和后一半大小转换字符串,传给重生之后的程序
sprintf(str_fd1, "%d", fd1);
sprintf(str_size, "%ld", size - size/2);
execl("./proj", "proj", str_fd1, str_size, argv[2], NULL);
}else{
perror("fork");
return -1;
}
close(fd1);
return 0;
}
进程重生代码
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, const char *argv[])
{
//进行后一半的复制
char buf = '\0';
//将字符换转换为整型
int size = atoi(argv[2]);
int fd1 = atoi(argv[1]);
int fd2 = open(argv[3], O_WRONLY | O_CREAT);
if(-1 == fd2){
perror("open");
return -1;
}
//做文件偏移
lseek(fd1, size-1, SEEK_SET);
lseek(fd2, size-1, SEEK_SET);
for(int i = 0; i < size; i++){
if(-1 == read(fd1, &buf, sizeof(buf))){
perror("read");
return -1;
}
if(-1 == write(fd2, &buf, sizeof(buf))){
perror("write");
return -1;
}
memset(&buf, 0, 1);
}
close(fd2);
return 0;
}