linux应用编程

Linux应用编程复习
多进程: 多进程的实现和多进程之间的数据通信
进程是linux系统进行资源调度的基本单位
从程序实现中来说,实际上就是根据fork函数的不同返回值来实现的(实际上就是依赖于该接口在不同的环境有两个不同的返回值)--fork接口在子进程中返回0,在父进程中返回子进程ID
int main()
{
    int num = 12; //假如num在内存中的0x7fffffc0
    ....


    printf("num = %d", num);
    
    //在C的程序流程结构中,以下的if...else是唯一一个能够同时满足的情况
    if(fork() == 0) //fork调用成功,会立即进行资源的调度和分配--拷贝父进程的所有的资源--堆,栈,代码...
    {
        num = 11;
    }
    else
    {
        num = 13;
    }
    
    printf("num = %d\n", num);


    return 0;
}


#include <sys/types.h>
#include <unistd.h>


int main()
{
int pid;
int i;


for(i= 0; i< 3; i++)
{
     
pid= fork();
if(pid== 0)
{
printf("child\n");
}
else
{
printf("father\n");
}
}


return 0;
}


wait
waitpid
#include <sys/wait.h>
pid_t wait(int *stat_loc);  等待当前的父进程的任何一个子进程返回
pid_t waitpid(pid_t pid, int *stat_loc, int options);  等待进程ID等于指定的进程ID的进程返回


#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <stdlib.h>


int main(int argc, char *argv[])
{
int pid;
int num = 12;
int *pstat = (int *)malloc(4);


if((pid = fork()) == 0)
{
num = 11;
printf("child: num = %d\n", num);
exit(3);  //exit退出的时候指定的数据值代表进程退出的状态

}
else
{
wait(pstat);
printf("father: num = %d\n", num);
//对于wait的进程的退出状态不能直接通过wait中指定的指针来获取,必须通过WEXITSTATUS来获取,该宏的参数为进程退出的状态
printf("返回值: %d\n", WEXITSTATUS(*pstat));
}

return 0;
}


[root@localhost exam]# ./wait_test
child: num = 11
father: num = 12
返回值: 3


子进程退出采用exit(-1);
[root@localhost exam]# gcc wait_test.c -o wait_test
[root@localhost exam]# ./wait_test
child: num = 11
father: num = 12
返回值: 255




#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <stdlib.h>


int main(int argc, char *argv[])
{
int pid;
int num = 12;
int *pstat = (int *)malloc(4);


if((pid = fork()) == 0)
{
num = 11;
printf("child: num = %d\n", num);
exit(-1);  //exit退出的时候指定的数据值代表进程退出的状态

}
else
{
waitpid(pid, pstat, 0);//WCONTINUED阻塞, WNOHANG 不阻塞;wait只能是父进程等待子进程,waitpid除了父进程可以等待子进程以外还可以子进程等待父进程,也可以是两个无关进程之间的等待
printf("father: num = %d\n", num);
printf("返回值: %d\n", WEXITSTATUS(*pstat));
}

return 0;
}
[root@localhost exam]# gcc wait_test.c -o wait_test
[root@localhost exam]# ./wait_test
child: num = 11
father: num = 12
返回值: 255


#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <stdlib.h>


int main(int argc, char *argv[])
{
int pid;
int num = 12;
int *pstat = (int *)malloc(4);


if((pid = fork()) == 0)
{
system("clear");
system("ls -l");  //使用system来调用系统环境变量PATH指定的目录下的命令


exit(-1);  //exit退出的时候指定的数据值代表进程退出的状态

}
else
{
waitpid(pid, pstat, 0);//WCONTINUED阻塞, WNOHANG 不阻塞;wait只能是父进程等待子进程,waitpid除了父进程可以等待子进程以外还可以子进程等待父进程,也可以是两个无关进程之间的等待
printf("father: num = %d\n", num);
printf("返回值: %d\n", WEXITSTATUS(*pstat));
}

return 0;
}
[root@localhost exam]# gcc wait_test.c -o wait_test
[root@localhost exam]# ./wait_test


总计 4
-rwxrwxrwx 1 root root 5516 04-12 10:53 wait_test
-rwxrwxrwx 1 root root  811 04-12 10:53 wait_test.c
-rwxrwxrwx 1 root root  711 04-12 10:51 wait_test.c.bak
father: num = 12
返回值: 255


exec
#include <unistd.h>
extern char **environ;
int execl(const char *path, const char *arg0, ... /*,  (char *)0 */);
int execv(const char *path, char *const argv[]);
int execle(const char *path, const char *arg0, ... /*,
              (char *)0, char *const envp[]*/);
int execve(const char *path, char *const argv[], char *const envp[]);
int execlp(const char *file, const char *arg0, ... /*, (char *)0 */);
int execvp(const char *file, char *const argv[]);
实例:
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <stdlib.h>


int main(int argc, char *argv[])
{
int pid;
int num = 12;
int *pstat = (int *)malloc(4);


if((pid = fork()) == 0)
{
        //system("clear");
//system("ls -l");  //使用system来调用系统环境变量PATH指定的目录下的命令
//int execl(const char *path, const char *arg0, ... /*,  (char *)0 */);
/*path: 指定要执行的应用程序的路径
 arg0,arg1..: 用列表的方式指定命令的执行方式,随后以NULL结束
 比如:[root@localhost exam]# ls -l /home命令中arg0 = "ls", arg1 = "-l",arg2 = "/home"
*/
execl("/bin/ls", "/bin/ls", "-l", "/home", NULL);

//以下的子进程代码是不会执行的,是由于当前进程的资源被指定的应用程序运行以后所替换,也就是当前进程的资源(数据,代码)都被指定的应用程序替换
printf("child end...\n");


exit(-1);  //exit退出的时候指定的数据值代表进程退出的状态

}
else
{
//waitpid(pid, pstat, 0);//WCONTINUED阻塞, WNOHANG 不阻塞;wait只能是父进程等待子进程,waitpid除了父进程可以等待子进程以外还可以子进程等待父进程,也可以是两个无关进程之间的等待
printf("father: num = %d\n", num);
printf("返回值: %d\n", WEXITSTATUS(*pstat));
}

return 0;
}
[root@localhost exam]# gcc wait_test.c -o wait_test
[root@localhost exam]# ./wait_test
father: num = 12
返回值: 0
[root@localhost exam]# 总计 12
drwx------ 30 liao liao 4096 12-26 19:25 liao
drwx------ 26 test test 4096 2013-04-17 test
drwx------ 26 wei  wei  4096 2013-05-05 wei


#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <stdlib.h>


int main(int argc, char *argv[])
{
int pid;
int num = 12;
int *pstat = (int *)malloc(4);
char *arg[] = {"ls", "-l", "/home", NULL};


if((pid = fork()) == 0)
{
        //system("clear");
//system("ls -l");  //使用system来调用系统环境变量PATH指定的目录下的命令
//int execl(const char *path, const char *arg0, ... /*,  (char *)0 */);
/*path: 指定要执行的应用程序的路径
 arg0,arg1..: 用列表的方式指定命令的执行方式,随后以NULL结束
 比如:[root@localhost exam]# ls -l /home命令中arg0 = "ls", arg1 = "-l",arg2 = "/home"
*/
//execl("/bin/ls", "/bin/ls", "-l", "/home", NULL);
execv("/bin/ls", arg);

//以下的子进程代码是不会执行的,是由于当前进程的资源被指定的应用程序运行以后所替换,也就是当前进程的资源(数据,代码)都被指定的应用程序替换
printf("child end...\n");


exit(-1);  //exit退出的时候指定的数据值代表进程退出的状态

}
else
{
//waitpid(pid, pstat, 0);//WCONTINUED阻塞, WNOHANG 不阻塞;wait只能是父进程等待子进程,waitpid除了父进程可以等待子进程以外还可以子进程等待父进程,也可以是两个无关进程之间的等待
printf("father: num = %d\n", num);
printf("返回值: %d\n", WEXITSTATUS(*pstat));
}

return 0;
}
[root@localhost exam]# gcc wait_test.c -o wait_test
[root@localhost exam]# ./wait_test
father: num = 12
返回值: 0
总计 12
drwx------ 30 liao liao 4096 12-26 19:25 liao
drwx------ 26 test test 4096 2013-04-17 test
drwx------ 26 wei  wei  4096 2013-05-05 wei


文件描述符:
#include <stdio.h>
#include <sys/types.h>
#include <fcntl.h>
#include <stdlib.h>


int main(int argc, char *argv[])
{
int num;


read(0, &num, 4);  //从文件描述符为0的文件(标准输入设备--键盘)中读取数据--从键盘输入数据到指定的内存中


write(1, &num, 4);  //将内存中的数据写入到文件描述符为1的文件(标准输出设备--显示器)--将内存中的数据输出到屏幕

return 0;
}
[root@localhost exam]# gcc fd_test.c -o fd_test
[root@localhost exam]# ./fd_test
12
12




进程之间的通信:
管道: 使用在具有血缘关系的父子进程之间 -- 匿名的
#include <unistd.h>
int pipe(int fildes[2]);
该函数一旦执行成功,会自动打开两个文件,fd[0]中保存了用来读管道的文件描述符,fd[1]中保存了用来写管道的文件描述符
实例:
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>


int main(int argc, char *argv[])
{
int fd[2];
int res;
int num = 12;
int pid;


res = pipe(fd);  //管道一旦创建成功,会自动的打开两个文件,打开的两个文件的文件描述符分别保存在参数fd数组的两个元素中


if(res)
{
   printf("管道创建失败!\n");
exit(-1);
}


printf("fd[0] = %d, fd[1] = %d\n", fd[0], fd[1]);  //3, 4


if((pid = fork()) == 0)
{
   sleep(1);
printf("child: num = %d\n", num);
read(fd[0], &num, 4);
printf("child: num = %d\n", num);
num = 88;
write(fd[1], &num, 4);
exit(0);
}
else
{
   num = 99;
write(fd[1], &num, 4);
wait(NULL);
read(fd[0], &num, 4);
printf("father: num = %d\n", num);
}

return 0;
}
[root@localhost exam]# gcc pipe_test.c -o pipe_test
[root@localhost exam]# ./pipe_test
fd[0] = 3, fd[1] = 4
child: num = 12
child: num = 99
father: num = 88
注意: 管道实际上就是两个进程利用两个共同的文件(fd[0], fd[1])来进行的数据的传输


有名管道: 可以使用在任意两个进程之间 -- 命名的 -- 在系统文件系统中确实存在的
#include <sys/stat.h>
int mkfifo(const char *path, mode_t mode);
参数:
  path: 所创建的命名管道的名字 -- 在文件系统中存在的文件的名字
  mode: 文件的创建权限
实例:
写有名管道的测试程序fifo_test.c
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>


//有名管道不要创建在共享目录下
#define FIFONAME "/home/liao/fifo_file"


int main(int argc, char *argv[])
{
int fd;
int res;
int num = 88;

unlink(FIFONAME); //删除指定的管道文件
res = mkfifo(FIFONAME, 0666);  //一旦创建,在磁盘上就会存在一个类型为p的管道文件;有名管道一旦创建,以后任何的进程都可以直接使用该管道来进行数据的通信


if(res)
{
   printf("管道创建失败!\n");
exit(-1);
}


printf("管道创建成功!\n");


//创建完有名管道以后,就可以使用文件系统的操作方式来操作该文件
fd = open(FIFONAME, O_WRONLY);  //对于以写的方式打开的管道文件必须有有某个进程以读的方式打开才能够打开,否则会一直阻塞在此直到有某个进程以读的方式打开为止
if(fd < 0)
{
   printf("管道打开失败!\n");
exit(-1);
}
printf("管道文件打开成功!\n");


res = write(fd, &num, 4);
printf("写入了%d个字节\n", res);


close(fd);


//sleep(10);

return 0;
}


读有名管道的测试程序fifo_read.c
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>


#define FIFONAME "/home/liao/fifo_file"


int main(int argc, char *argv[])
{
int fd;
int num;
int res;


fd = open(FIFONAME, O_RDONLY);
if(fd < 0)
{
   printf("管道打开失败!\n");
exit(-1);
}
printf("管道文件打开成功!\n");


res = read(fd, &num, 4);  //在从管道文件中读取数据的时候,如果没有进程向管道中写入数据,会一直阻塞在此
printf("res = %d\n", res);


printf("num = %d\n", num);


close(fd);




return 0;
}
[root@localhost exam]# gcc fifo_read.c -o fifo_read
[root@localhost exam]# gcc fifo_test.c -o fifo_test
在一个终端中运行写测试程序,在另外一个终端中运行读测试程序
[root@localhost exam]# ./fifo_test
管道创建成功!
管道文件打开成功!
写入了4个字节
[root@localhost exam]# ./fifo_read
管道文件打开成功!
res = 4
num = 88
注意: 管道文件在打开的过程中必须读端和写端同时存在才能操作,管道中的数据一旦被读走,文件中就不会存在该数据
以写的方式打开管道文件,必须有进程以读的方式打开了该管道文件,否则阻塞在文件打开的地方
以读的方式打开的管道文件,如果没有进程向管道中写入数据,读端阻塞在管道读的地方
[root@localhost exam]# ll /home/liao/fifo_file 
prw-r--r-- 1 root root 0 04-12 14:44 /home/liao/fifo_file


共享内存 -- 内存映射
#include <sys/mman.h>
void  *mmap(void *addr, size_t len, int prot, int flags, int fildes, off_t off);
参数:
  addr: 映射的内存空间的起始地址,一般使用NULL
  len: 映射的地址空间长度(字节数)
  prot: 共享内存的访问权限
     PROT_READ           Data can be read.
     PROT_WRITE          Data can be written.
     PROT_EXEC           Data can be executed.
     PROT_NONE           Data cannot be accessed.
  flags: 共享方式:
     MAP_SHARED          Changes are shared.
     MAP_PRIVATE         Changes are private.
     MAP_FIXED           Interpret addr exactly.
  fildes: 文件描述符
     如果共享内存有指定的文件和他对应,通过open或者creat来创建或者打开;如果没有确定的文件和共享内存对应,该参数直接使用-1,此时须指定flags参数中的MAP_ANON
  off: 映射区域的偏移量,一般该值为0
返回值:
  成功返回映射成功的内存首地址,失败返回MAP_FAILED
  #define MAP_FAILED ((void *) -1)


int munmap( void * addr, size_t len )
该调用在进程地址空间中解除一个映射关系,addr是调用mmap()时返回的地址,len是映射区的大小。当映射关系解除后,对原来映射地址的访问将导致段错误发生。
#include <stdio.h>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/mman.h>


typedef struct Stu
{
int num;
char name[12];
int score;
}Stu;


int main(int argc, char *argv[])
{
Stu st = {101, "Joe", 88};
Stu *pmap;//定义了一个指向Stu(结构体)类型的指针
int res;


pmap = mmap(NULL, sizeof(st), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANON, -1, 0);


if(pmap == MAP_FAILED)
{
   printf("内存共享失败!\n");
exit(-1);
}
else
{
   printf("内存共享成功, 共享的首地址为%p\n", pmap);
}


res = munmap(pmap, sizeof(st));
if(res)
{
   printf("映射解除失败!\n");
}
else
{
   printf("映射解除成功!\n");
}


return 0;
}
[root@localhost exam]# gcc mmap_test.c -o mmap_test
[root@localhost exam]# ./mmap_test
内存共享成功, 共享的首地址为0xb7f85000
映射解除成功!




实例:
#include <stdio.h>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <string.h>


typedef struct Stu
{
int num;
char name[12];
int score;
}Stu;


int main(int argc, char *argv[])
{
Stu st = {101, "Joe", 88};
Stu *pmap;
int res;
int pid;

//如果共享的区域是在具有血缘关系的进程之间,可以不将共享区域对应为文件
pmap = mmap(NULL, sizeof(st), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANON, -1, 0);


if(pmap == MAP_FAILED)
{
   printf("内存共享失败!\n");
exit(-1);
}
else
{
   printf("内存共享成功, 共享的首地址为%p\n", pmap);
}



//
if((pid = fork()) == 0)
{
sleep(1);
st = *pmap;
printf("%d, %s, %d\n", st.num, st.name, st.score);
}
else
{
pmap->num = 188;
pmap->score = 92;
strcpy(pmap->name, "Jack");
}


wait(NULL);


res = munmap(pmap, sizeof(st));
if(res)
{
   printf("映射解除失败!\n");
}
else
{
   printf("映射解除成功!\n");
}


return 0;
}
[root@localhost exam]# gcc mmap_test.c -o mmap_test
[root@localhost exam]# ./mmap_test
内存共享成功, 共享的首地址为0xb7fcc000
188, Jack, 92
映射解除成功!
映射解除成功!




对于文件映射请参见ppt上的代码


进程之间通信的方式:
如果是具有血缘关系的进程可以使用管道或者匿名的内存映射方式;如果是无关进程之间的数据通信可以使用FIFO或者命名的内存映射方式


多线程编程:
线程的创建:
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine) (void *), void *arg)
参数:
thread:代表线程的唯一标识符
attr:所创建的线程属性,一般为NULL.
start_routine:线程函数,是一个回调函数(调用是自动的,在线程创建成功的时候自动调用该函数,多线程的实现实际上就是通过创建一个线程就使用一个运行序列去执行函数,子线程去执行函数中的代码,原来的线程继续执行主函数中的代码)
arg:传递到线程函数中的数据




#include <stdio.h>
#include <pthread.h>
void *pfun(void *arg)
{
int n=*((int *)arg);
int i=0;
for(i=0;i<n;i++)
{
printf("pfun:%d\n",i+1);
}
printf("线程处理\n");
return (void *)NULL;
}
int main(int argc, char *argv[])
{
pthread_t ptid;
int res;
int num=10;
res=pthread_create(&ptid,NULL,pfun,(void *)&num);
if(!res)
{
printf("thread creat succes\n");
}
else
{
printf("thread creat fail\n");
}
pthread_join(ptid,NULL);
return 0;
}




注意:在使用一个多线程实现的并发程序中,当主线程一旦执行结束,该主线程所创建的...

你可能感兴趣的:(linux应用编程)