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;
}




注意:在使用一個多線程實現的併發程序中,當主線程一旦執行結束,該主線程所創建的...
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章