linux编程小结

1.linux创建子进程只有一种方法:调用fork()

View Code
 1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <sys/types.h>
4 #include <unistd.h>
5 int myvar = 0;
6
7 int main()
8 {
9 int pid;
10 pid = fork();
11 if ( pid<0 )
12 {
13 printf("fork failed !\n");
14 exit(1);
15 }
16 else if(pid == 0)
17 {
18 printf("child process executing...\n");
19 myvar ++;
20 printf("child:myvar = %d\n",myvar);
21 }
22 else
23 {
24 wait();
25 printf("child complete!\n");
26 myvar--;
27 printf("father:myvar=%d\n",myvar);
28 exit(0);
29 }
30 }

2.程序从终端读入用户指令,并由此进行系统调用:execlp()函数的用法

View Code
 1 #include <string.h>
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <sys/types.h>
5 #include <sys/wait.h>
6 #include <unistd.h>
7
8 char command[256];
9
10 int main(int argc,char **argv)
11 {
12 int rtn = 1;
13 // int *r=&rtn;
14 int errorno;
15 while(1)
16 {
17 printf(">");
18 fgets(command,256,stdin);
19 command[strlen(command)-1] = 0;
20 if(fork()==0)
21 {
22 errorno=execlp(command,command,NULL,NULL);
23 perror(command);
24 exit(errorno);
25 }
26 else
27 {
28 wait(&rtn);
29 printf("child process return %d\n",rtn);
30 }
31 }
32 }

3.等待子进程结束:调用waitpid()

View Code
 1 /*
2 * =====================================================================================
3 *
4 * Filename: waitchild.cpp
5 *
6 * Description: 等待子进程结束
7 *
8 * Version: 1.0
9 * Created: 2011年11月15日 16时35分10秒
10 * Revision: none
11 * Compiler: gcc
12 *
13 * Author: witcxc
14 * Company:
15 *
16 * =====================================================================================
17 */
18
19 #include <unistd.h>
20 #include <sys/types.h>
21 #include <sys/wait.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24
25 int main(int argc,char **argv)
26 {
27 pid_t childpid;
28 int status;
29 childpid = fork();
30 if ( -1==childpid )
31 {
32 perror("fork()");
33 exit(EXIT_FAILURE);
34 }
35 else if (0 == childpid)
36 {
37 puts("In child process");
38 sleep(3);
39 printf("\tchild pid = %d\n",getpid());
40 printf("\tchild ppid = %d\n",getppid());
41 exit(EXIT_SUCCESS);
42 }
43 else
44 {
45 waitpid(childpid, &status, 0);
46 puts("In parent process");
47 printf("\tparent pid = %d\n", getpid());
48 printf("\tparent ppid = %d\n", getppid());
49 printf("\tchild process exited with status %d\n",status);
50 }
51 exit(EXIT_SUCCESS);
52 }

4.共享内存:一系列系统函数调用

 

       采用共享内存通信的一个显而易见的好处是效率高,因为进程可以直接读写内存,而不需要任何数据的拷贝。对于像管道和消息队列等通信方式,则需要在内核和用 户空间进行四次的数据拷贝,而共享内存则只拷贝两次数据[1]:一次从输入文件到共享内存区,另一次从共享内存区到输出文件。实际上,进程之间在共享内存 时,并不总是读写少量数据后就解除映射,有新的通信时,再重新建立共享内存区域。而是保持共享区域,直到通信完毕为止,这样,数据内容一直保存在共享内存 中,并没有写回文件。共享内存中的内容往往是在解除映射时才写回文件的。因此,采用共享内存的通信方式效率是非常高的。

要使用共享内存,应该有如下步骤:
1.开辟一块共享内存 shmget()
2.允许本进程使用共某块共享内存 shmat()
3.写入/读出
4.禁止本进程使用这块共享内存 shmdt()
5.删除这块共享内存 shmctl()或者命令行下ipcrm

 1 #include <stdio.h>
2 #include <stdlib.h>//exit()
3 #include <string.h>
4 # include <sys/types.h>
5 # include <sys/ipc.h>
6 # include <sys/shm.h>
7 #include <errno.h> //errno
8 #include<sys/types.h> //pid_t
9 #include<unistd.h> //pid_t fork()
10 #include<sys/stat.h>
11 #define PERM S_IRUSR|S_IWUSR//数据、文件或目录权限设置
12
13 int DelShm(int shmid);
14
15 int main( void)
16 {
17 int shmid;
18 char * p_addr, * c_addr;
19 key_t ipckey;
20 ipckey= ftok("./MemShared.c",'a');//第一个参数为文件名或目录,必须存在
21 printf("ipckey= %x\n",ipckey);
22 if(( shmid = shmget(ipckey,1024,IPC_CREAT|PERM))==-1)
23 {
24 fprintf(stderr, " Create Share Memory Error:\n %s\n\a " ,strerror(errno));
25 exit( 1 );
26 }
27 if (fork())
28 {
29 p_addr = (char*)shmat(shmid, 0 , 0 );
30 memset(p_addr,'\0', 1024 );
31 strncpy(p_addr,"witcxc@gmail.com", 20 );
32 printf( "The string of pointer p_addr is: %s\n " ,p_addr);
33 // DelShm(shmid);
34 exit( 0 );
35 }
36 else
37 {
38 c_addr =(char*)shmat(shmid, 0 , 0 );
39 printf( "The string of pointer c_addr is: %s \n" ,c_addr);
40 exit( 0 );
41 }
42 }
43 int DelShm(int shmid)
44 {
45 struct shmid_ds buf;
46 shmctl(shmid , IPC_STAT, &buf);
47 if(shmctl(shmid , IPC_RMID, &buf)==-1)
48 {
49 fprintf(stderr, " Delete Share Memory Error:\n %s\n\a " ,strerror(errno));
50 exit( 1 );
51 }
52 else
53 exit( 0 );
54 }

在此程序中33行,我将共享内存删除的语句注释掉,便于自己调试。

shmget()
=============================================================================================================
int shmget(key_t key, size_t size, int shmflg);
key_t key

-----------------------------------------------
    key标识共享内存的键值: 0/IPC_PRIVATE。 当key的取值为IPC_PRIVATE,则函数shmget()将创建一块新的共享内存;如果key的取值为0,而参数shmflg中设置了IPC_PRIVATE这个标志,则同样将创建一块新的共享内存。
    在IPC的通信模式下,不管是使用消息队列还是共享内存,甚至是信号量,每个IPC的对象(object)都有唯一的名字,称为“键”(key)。通过 “键”,进程能够识别所用的对象。“键”与IPC对象的关系就如同文件名称之于文件,通过文件名,进程能够读写文件内的数据,甚至多个进程能够共用一个文 件。而在IPC的通讯模式下,通过“键”的使用也使得一个IPC对象能为多个进程所共用。
    Linux系统中的所有表示System V中IPC对象的数据结构都包括一个ipc_perm结构,其中包含有IPC对象的键值,该键用于查找System V中IPC对象的引用标识符。如果不使用“键”,进程将无法存取IPC对象,因为IPC对象并不存在于进程本身使用的内存中。
    通常,都希望自己的程序能和其他的程序预先约定一个唯一的键值,但实际上并不是总可能的成行的,因为自己的程序无法为一块共享内存选择一个键值。因此,在 此把key设为IPC_PRIVATE,这样,操作系统将忽略键,建立一个新的共享内存,指定一个键值,然后返回这块共享内存IPC标识符ID。而将这个 新的共享内存的标识符ID告诉其他进程可以在建立共享内存后通过派生子进程,或写入文件或管道来实现。


int size(单位字节Byte)
-----------------------------------------------
    size是要建立共享内存的长度。所有的内存分配操作都是以页为单位的。所以如果一段进程只申请一块只有一个字节的内存,内存也会分配整整一页(在 i386机器中一页的缺省大小PACE_SIZE=4096字节)这样,新创建的共享内存的大小实际上是从size这个参数调整而来的页面大小。即如果 size为1至4096,则实际申请到的共享内存大小为4K(一页);4097到8192,则实际申请到的共享内存大小为8K(两页),依此类推。


int shmflg
-----------------------------------------------
    shmflg主要和一些标志有关。其中有效的包括IPC_CREAT和IPC_EXCL,它们的功能与open()的O_CREAT和O_EXCL相当。
    IPC_CREAT   如果共享内存不存在,则创建一个共享内存,否则打开操作。
    IPC_EXCL    只有在共享内存不存在的时候,新的共享内存才建立,否则就产生错误。

    如果单独使用IPC_CREAT,shmget()函数要么返回一个已经存在的共享内存的操作符,要么返回一个新建的共享内存的标识符。如果将 IPC_CREAT和IPC_EXCL标志一起使用,shmget()将返回一个新建的共享内存的标识符;如果该共享内存已存在,或者返回-1。 IPC_EXEL标志本身并没有太大的意义,但是和IPC_CREAT标志一起使用可以用来保证所得的对象是新建的,而不是打开已有的对象。对于用户的读 取和写入许可指定SHM_RSHM_W,(SHM_R>3)和(SHM_W>3)是一组读取和写入许可,而(SHM_R>6)和(SHM_W>6)是全局读取和写入许可。


返回值
-----------------------------------------------
成功返回共享内存的标识符;不成功返回-1,errno储存错误原因。
    EINVAL        参数size小于SHMMIN或大于SHMMAX。
    EEXIST        预建立key所致的共享内存,但已经存在。
    EIDRM         参数key所致的共享内存已经删除。
    ENOSPC        超过了系统允许建立的共享内存的最大值(SHMALL )。
    ENOENT        参数key所指的共享内存不存在,参数shmflg也未设IPC_CREAT位。
    EACCES        没有权限。
    ENOMEM        核心内存不足。


struct shmid_ds
-----------------------------------------------
    shmid_ds数据结构表示每个新建的共享内存。当shmget()创建了一块新的共享内存后,返回一个可以用于引用该共享内存的shmid_ds数据结构的标识符。

include/linux/shm.h

    struct shmid_ds {
        struct ipc_perm    shm_perm;      /* operation perms */
        int                shm_segsz;     /* size of segment (bytes) */
        __kernel_time_t    shm_atime;     /* last attach time */
        __kernel_time_t    shm_dtime;     /* last detach time */
        __kernel_time_t    shm_ctime;     /* last change time */
        __kernel_ipc_pid_t shm_cpid;      /* pid of creator */
        __kernel_ipc_pid_t shm_lpid;      /* pid of last operator */
        unsigned short     shm_nattch;    /* no. of current attaches */
        unsigned short     shm_unused;    /* compatibility */
        void               *shm_unused2; /* ditto - used by DIPC */
        void               *shm_unused3; /* unused */
    };


struct ipc_perm
-----------------------------------------------
    对于每个IPC对象,系统共用一个struct ipc_perm的数据结构来存放权限信息,以确定一个ipc操作是否可以访问该IPC对象。

    struct ipc_perm {
        __kernel_key_t   key;
        __kernel_uid_t   uid;
        __kernel_gid_t   gid;
        __kernel_uid_t   cuid;
        __kernel_gid_t   cgid;
        __kernel_mode_t mode;
        unsigned short   seq;
};

ftok()

===============================================================================

系统建立IPC通讯(如消息队列、共享内存时)必须指定一个ID值。通常情况下,该id值通过ftok函数得到。
ftok原型如下:
key_t ftok( char * fname, int id )
fname就时你指定的文件名,id是子序号。

在一般的UNIX实现中,是将文件的索引节点号取出,前面加上子序号得到key_t的返回值。

如指定文件的索引节点号为65538,换算成16进制为0x010002,而你指定的ID值为38,换算成16进制为0x26,则最后的key_t返回值为0x26010002。
查询文件索引节点号的方法是: ls -i

当删除重建文件后,索引节点号由操作系统根据当时文件系统的使用情况分配,因此与原来不同,所以得到的索引节点号也不同。

如果要确保key_t值不变,要目确保ftok的文件不被删除,要么不用ftok,指定一个固定的key_t值,比如:

#define IPCKEY 0x111

char path[256];

    sprintf( path, "%s/etc/config.ini", (char*)getenv("HOME") );
    msgid=ftok( path, IPCKEY );[/code]

同一段程序,用于保证两个不同用户下的两组相同程序获得互不干扰的IPC键值。
由于etc/config.ini(假定)为应用系统的关键配置文件,因此不存在被轻易删除的问题——即使被删,也会很快被发现并重建(此时应用系统也将被重起)。
ftok()的设计目的也在于此。

shmat()

================================================================================

shmat()是用来允许本进程访问一块共享内存的函数。
int shmid是那块共享内存的ID。
char *shmaddr是共享内存的起始地址
int shmflag是本进程对该内存的操作模式。如果是SHM_RDONLY的话,就是只读模式。其它的是读写模式
成功时,这个函数返回共享内存的起始地址。失败时返回-1。

char *head , *pos ,

head = pos = shmat( shmid , 0 , 0 );

// 允许本进程使用这块共享内存

shmdt()与shmat()相反,是用来禁止本进程访问一块共享内存的函数。
参数char *shmaddr是那块共享内存的起始地址。
成功时返回0。失败时返回-1。

shmdt( head ); // 禁止本进程使用这块内存


shmctl()

============================================================================================================

删除共享内存:shmctl( shmid , IPC_RMID , &buf );

得到共享内存状态:shmctl( shmid , IPC_STAT , &buf );

int shmctl( int shmid , int cmd , struct shmid_ds *buf );
int shmid是共享内存的ID。
int cmd是控制命令,可取值如下:
IPC_STAT 得到共享内存的状态
IPC_SET 改变共享内存的状态
IPC_RMID 删除共享内存
struct shmid_ds *buf是一个结构体指针。IPC_STAT的时候,取得的状态放在这个结构体中。如果要改变共享内存的状态,用这个结构体指定。
返回值: 成功:0
失败:-1


注意:在使用共享内存,结束程序退出后。如果你没在程序中用shmctl()删除共享内存的话,一定要在命令行下用ipcrm命令删除这块共享内存。你要是不管的话,

它就一直在那儿放着了。
简单解释一下ipcs命令和ipcrm命令。

  取得ipc信息:
ipcs [-m|-q|-s]
-m 输出有关共享内存(shared memory)的信息
-q 输出有关信息队列(message queue)的信息
-s 输出有关“遮断器”(semaphore)的信息
%ipcs -m

删除ipc
ipcrm -m|-q|-s shm_id
%ipcrm -m 105

posted on 2011-11-15 23:37  witcxc  阅读(1279)  评论(1编辑  收藏  举报

导航